Обработка результатов опросов с несколькими ответами (из 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