У меня есть столбец Pandas, который содержит результаты опроса, которые являются либо свободным текстом, либо числами от 1 до 5. Я извлекаю их из API в формате JSON и преобразовываю в DataFrame. Каждая строка представляет один вопрос с ответом участника, как это:

Memberid | Question | Answer
       1   Q1             3
       1   Q2             2
       1   Q3         Test Text
       2   Q1             3
       2   Q2             2
       2   Q3         Test Text

Столбец, в котором есть результаты, пока что хранит их все в виде строки, поэтому при экспорте в Excel числа сохраняются в виде текста.

Моя цель - иметь отдельный столбец для текстовых ответов и оставить поле, в котором они были изначально, пустым, чтобы у нас были отдельные столбцы для текстовых результатов и числовых результатов для целей расчета.

Memberid | Question | Numeric Answers | Freetext answers
       1   Q1             3
       1   Q2             2
       1   Q3                             Test Text
       2   Q1             3
       2   Q2             2
       2   Q3                             Test Text

Я генерирую этот df из списков, подобных этому:

d = {'Memberid':memberid, 'Question':title, 'Answer':results}
df = pd.DataFrame(d)

Итак, первое, что я попробовал, это преобразовать числовые значения в столбце из строки в числа с помощью этого:

df["Answer"] = pd.to_numeric(df['Answer'], errors='ignore')

Идея заключалась в том, что если это работает, я могу просто сделать цикл for, чтобы проверить, является ли значение в столбце ответа строкой, а затем переместить это значение в новый столбец.

Проблема в том, что команда ошибок работает не так, как предназначено для меня. Когда я оставляю это игнорировать, ничто не преобразуется. Когда я изменяю его на принудительное, числа преобразуются из строковых в числовые, но поля, в которых есть ответы в свободном тексте, теперь пусты в Excel.

5
Tim 2 Июл 2019 в 14:42

5 ответов

Лучший ответ

Вы можете использовать Series.str.extract с шаблоном регулярного выражения:

  • (\d+)? будет извлекать последовательные цифры
  • (\D+) будет извлекать последовательные нецифровые символы
  • Синтаксис ?P<text> будет называть вашу группу совпадений - это будет заголовок столбца.

df.join(df.pop('Answer').str.extract('(?P<numbers>\d+)?(?P<text>\D+)?').fillna(''))

[вне]

   Memberid Question numbers       text
0         1       Q1       3           
1         1       Q2       2           
2         1       Q3          Test Text
3         2       Q1       3           
4         2       Q2       2           
5         2       Q3          Test Text
8
Chris A 2 Июл 2019 в 11:52

Примерно так можно сделать, чтобы построить 2 списка (один для текстовых данных и один для числовых данных):

text_data = ["" if s.isdigit() else s for s in df['Question']] # "" default string
numeric_data = [s if s.isdigit() else 0 for s in df['Question']] # 0 default numeric value
2
Simon Delecourt 2 Июл 2019 в 11:52

Вы можете построить столбец Numeric Answers с помощью to_numeric(,errors='coerce'), а затем использовать isna для этого столбца, чтобы построить FreeText Answers:

df['Numeric Answers'] = pd.to_numeric(df['Answer'], errors='coerce')
mask = df['Numeric Answers'].isna()
df.loc[mask, 'FreeText Answers'] = df.loc[mask, 'Answer']
df.drop(columns=['Answer'])

Это дает:

   Memberid Question  Numeric Answers FreeText Answers
0         1       Q1              3.0              NaN
1         1       Q2              2.0              NaN
2         1       Q3              NaN        Test Text
3         2       Q1              3.0              NaN
4         2       Q2              2.0              NaN
5         2       Q3              NaN        Test Text

Если вам не нравится NaN, вы можете заменить их пустыми строками:

df['FreeText Answers'].fillna('', inplace=True)
df['Numeric Answers'] = df['Numeric Answers'].astype(object).fillna('')

Чтобы наконец получить:

   Memberid Question Numeric Answers FreeText Answers
0         1       Q1               3                 
1         1       Q2               2                 
2         1       Q3                        Test Text
3         2       Q1               3                 
4         2       Q2               2                 
5         2       Q3                        Test Text
2
Serge Ballesta 2 Июл 2019 в 12:09

Вы могли бы сделать что-то вроде:

import pandas as pd
df = pd.DataFrame({"Question":['Q1', 'Q2','Q3'],'Answers':['Answer1', '1','2']})
idx = df.Answers.str.isnumeric()
df['Numeric Answers'] = None
df['Freetext answers'] = ''
df.loc[~idx, 'Freetext answers'] = df.loc[~idx, 'Answers']
df.loc[idx, 'Numeric Answers'] = df.loc[idx, 'Answers']
2
Ayoub ZAROU 23 Июл 2019 в 08:35

Надеюсь, это ответит на ваш вопрос. Я использовал метод строковых цифр, чтобы отделить числа от текста. Затем вы можете применить pd.numeric для преобразования столбца чисел

    import pandas as pd
    import numpy as np
    import string   

     a={
        'Memberid':[1,1,1,2,2,2],
        'Question':['Q1','Q2','Q3','Q1','Q2','Q3'],
        'Answer':['3','2','Test Text','3','2','Test Text']
      }

    df = pd.DataFrame.from_dict(a)
    digits = list(string.digits)   
    df = df.assign(Numeric_Answers= np.where(df['Answer'].isin(digits),                          
                                             df['Answer'],
                                             np.nan
                                            ),

                   FreeText =       np.where(df['Answer'].isin(digits),
                                             np.nan,
                                             df['Answer']
                                           )
                  )

        Memberid    Question    Answer  Numeric_Answers     FreeText
    0       1        Q1           3          3                 NaN
    1       1        Q2           2          2                 NaN
    2       1        Q3        Test Text    NaN             Test Text
    3       2        Q1           3          3                 NaN
    4       2        Q2           2          2                 NaN
    5       2        Q3        Test Text    NaN             Test Text
2
Ayoub ZAROU 2 Июл 2019 в 13:52