Обработка результатов опросов с несколькими ответами (из Google Forms) с помощью панд

У меня есть форма Google, которую я использую для сбора данных опроса (для этого вопроса я буду использовать пример формы), в которой есть вопросы, которые могут иметь несколько ответов, выбранных с помощью набора флажков.

Когда я получаю данные из формы и импортирую их в панд, я получаю это:

             Timestamp    What sweets do you like?
0  23/11/2013 13:22:30  Chocolate, Toffee, Popcorn
1  23/11/2013 13:22:34                   Chocolate
2  23/11/2013 13:22:39      Toffee, Popcorn, Fruit
3  23/11/2013 13:22:45               Fudge, Toffee
4  23/11/2013 13:22:48                     Popcorn

Я хотел бы сделать статистику по результатам вопроса (скольким людям понравился шоколад, какой процент людей понравился Ирис и т. Д.). Проблема в том, что все ответы находятся в одном столбце, поэтому группировка по этому столбцу и запрос количества не работает.

Есть ли в Pandas простой способ преобразовать этот тип фрейма данных в такой, где есть несколько столбцов, называемых "Шоколад", "Ириска", "Попкорн", "Выдумка" и "Фрукты", и каждый из них является логическим (1 для да, 0 для нет)? Я не могу придумать разумного способа сделать это, и я не уверен, действительно ли это поможет (делать агрегации, которые я хочу сделать, может быть сложнее в этом смысле).

2 ответа

Несколько дней назад я столкнулся с той же проблемой, и после некоторых поисков я нашел str.get_dummies функция в документации pandas. Посмотрим, как это работает:

pandas.Series.str.get_dummies

Как упоминалось в документации, str.get_dummiesразделить каждую строку в Серии на sep и вернуть DataFrame фиктивных / индикаторных переменных.

Вот упрощенная версия вышеупомянутого DataFrame:

In [27]: df
Out[27]: 
     What sweets do you like?
0  Chocolate, Toffee, Popcorn
1                   Chocolate
2      Toffee, Popcorn, Fruit
3               Fudge, Toffee
4                     Popcorn

Единственный аргумент, который нам нужно указать в str.get_dummiesявляется Сентябрь, что в нашем случае запятая:

In [28]: df['What sweets do you like?'].str.get_dummies(sep=', ')
Out[28]: 
   Chocolate  Fruit  Fudge  Popcorn  Toffee
0          1      0      0        1       1
1          1      0      0        0       0
2          0      1      0        1       1
3          0      0      1        0       1
4          0      0      0        1       0

Внимание:

Обратите внимание, что в аргументе sep после запятой есть пробел, потому что сам пробел является символом, если мы не включим его в sep, результат будет примерно таким, как показано ниже, что явно неверно:

In [29]: df['What sweets do you like?'].str.get_dummies(sep=',')
Out[29]: 
    Fruit   Popcorn   Toffee  Chocolate  Fudge  Popcorn  Toffee
0       0         1        1          1      0        0       0
1       0         0        0          1      0        0       0
2       1         1        0          0      0        0       1
3       0         0        1          0      1        0       0
4       0         0        0          0      0        1       0

Как правило, всегда обращайте внимание на то, чтобы разделитель был написан точно!

Читать как таблицу с фиксированной шириной, опуская первый столбец

In [30]: df = pd.read_fwf(StringIO(data),widths=[3,20,27]).drop(['Unnamed: 0'],axis=1)

In [31]: df
Out[31]: 
             Timestamp What sweets do you like0
0  23/11/2013 13:22:34                Chocolate
1  23/11/2013 13:22:39   Toffee, Popcorn, Fruit
2  23/11/2013 13:22:45            Fudge, Toffee
3  23/11/2013 13:22:48                  Popcorn

Сделайте метку времени в правильный тип datetime64 dtype (не обязательно для этого упражнения), но почти всегда то, что вы хотите.

In [32]: df['Timestamp'] = pd.to_datetime(df['Timestamp'])

Новые имена столбцов

In [33]: df.columns = ['date','sweets']

In [34]: df
Out[34]: 
                 date                  sweets
0 2013-11-23 13:22:34               Chocolate
1 2013-11-23 13:22:39  Toffee, Popcorn, Fruit
2 2013-11-23 13:22:45           Fudge, Toffee
3 2013-11-23 13:22:48                 Popcorn

In [35]: df.dtypes
Out[35]: 
date      datetime64[ns]
sweets            object
dtype: object

Разбейте сладкий столбец из строки в список

In [37]: df['sweets'].str.split(',\s*')
Out[37]: 
0                 [Chocolate]
1    [Toffee, Popcorn, Fruit]
2             [Fudge, Toffee]
3                   [Popcorn]
Name: sweets, dtype: object

Ключевой шаг, это создает фиктивную матрицу для того, где существуют значения

In [38]: df['sweets'].str.split(',\s*').apply(lambda x: Series(1,index=x))
Out[38]: 
   Chocolate  Fruit  Fudge  Popcorn  Toffee
0          1    NaN    NaN      NaN     NaN
1        NaN      1    NaN        1       1
2        NaN    NaN      1      NaN       1
3        NaN    NaN    NaN        1     NaN

Конечный результат, где мы заполняем nans до 0, а затем вводим bool, чтобы сделать True/False. Затем добавьте его в исходную рамку

In [40]: pd.concat([df,df['sweets'].str.split(',\s*').apply(lambda x: Series(1,index=x)).fillna(0).astype(bool)],axis=1)
Out[40]: 
                 date                  sweets Chocolate  Fruit  Fudge Popcorn Toffee
0 2013-11-23 13:22:34               Chocolate      True  False  False   False  False
1 2013-11-23 13:22:39  Toffee, Popcorn, Fruit     False   True  False    True   True
2 2013-11-23 13:22:45           Fudge, Toffee     False  False   True   False   True
3 2013-11-23 13:22:48                 Popcorn     False  False  False    True  False

Как насчет чего-то вроде этого:

#Create some data
import pandas as pd
import numpy as np
Foods = ['Chocolate, Toffee, Popcorn', 'Chocolate', 'Toffee, Popcorn, Fruit', 'Fudge,     Toffee', 'Popcorn']
Dates = ['23/11/2013 13:22:30', '23/11/2013 13:22:34', '23/11/2013 13:22:39', '23/11/2013 13:22:45', '23/11/2013 13:22:48']
DF = pd.DataFrame(Foods, index = Dates, columns = ['Sweets'])

#create unique list of foods
UniqueFoods = ['Chocolate', 'Toffee', 'Popcorn', 'Fruit']

# Create new data frame withy columns for each food type, with indenitcal index
DFTransformed = pd.DataFrame({Food: 0 for Food in UniqueFoods}, index = DF.index)

 #iterate through your data and modify the second data frame according to your wishes
for row in DF.index:
    for Food in UniqueFoods:
        if Food in DF['Sweets'][row]:
            DFTransformed[Food][row] = 1
DFTransformed
Другие вопросы по тегам