Ответ 1
Это:
d1.groupby('ExamenYear').agg({'Participated': len,
'Passed': lambda x: sum(x == 'yes')})
не выглядит более неудобным, чем решение R, IMHO.
Моя проблема заключается в том, как рассчитать частоты для нескольких переменных в pandas. Я из этого блока данных:
d1 = pd.DataFrame( {'StudentID': ["x1", "x10", "x2","x3", "x4", "x5", "x6", "x7", "x8", "x9"],
'StudentGender' : ['F', 'M', 'F', 'M', 'F', 'M', 'F', 'M', 'M', 'M'],
'ExamenYear': ['2007','2007','2007','2008','2008','2008','2008','2009','2009','2009'],
'Exam': ['algebra', 'stats', 'bio', 'algebra', 'algebra', 'stats', 'stats', 'algebra', 'bio', 'bio'],
'Participated': ['no','yes','yes','yes','no','yes','yes','yes','yes','yes'],
'Passed': ['no','yes','yes','yes','no','yes','yes','yes','no','yes']},
columns = ['StudentID', 'StudentGender', 'ExamenYear', 'Exam', 'Participated', 'Passed'])
К следующему результату
Participated OfWhichpassed
ExamenYear
2007 3 2
2008 4 3
2009 3 2
(1) Одна из возможностей, которую я пробовал, - вычислить два кадра данных и связать их
t1 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Participated'], aggfunc = len)
t2 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Passed'], aggfunc = len)
tx = pd.concat([t1, t2] , axis = 1)
Res1 = tx['yes']
(2) Вторая возможность заключается в использовании функции агрегации.
import collections
dg = d1.groupby('ExamenYear')
Res2 = dg.agg({'Participated': len,'Passed': lambda x : collections.Counter(x == 'yes')[True]})
Res2.columns = ['Participated', 'OfWhichpassed']
Оба способа наименее верны. Как это сделано правильно в pandas?
P.S: Я также попробовал value_counts вместо collection.Counter, но не смог заставить его работать
Для справки: Несколько месяцев назад я задал аналогичный вопрос для R здесь и plyr мог помочь
---- UPDATE ------
пользователь DSM прав. ошибка в желаемом результате таблицы.
(1) Код для опции 1 -
t1 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], aggfunc = len)
t2 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Participated'], aggfunc = len)
t3 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Passed'], aggfunc = len)
Res1 = pd.DataFrame( {'All': t1,
'OfWhichParticipated': t2['yes'],
'OfWhichPassed': t3['yes']})
Он даст результат
All OfWhichParticipated OfWhichPassed
ExamenYear
2007 3 2 2
2008 4 3 3
2009 3 3 2
(2) Для варианта 2, благодаря пользователю herrfz, я понял, как использовать value_count, и код будет
Res2 = d1.groupby('ExamenYear').agg({'StudentID': len,
'Participated': lambda x: x.value_counts()['yes'],
'Passed': lambda x: x.value_counts()['yes']})
Res2.columns = ['All', 'OfWgichParticipated', 'OfWhichPassed']
который даст тот же результат, что и Res1
Мой вопрос остается, хотя:
Используя вариант 2, можно ли использовать одну и ту же переменную дважды (для другой операции?), можно ли передать настраиваемое имя для результирующей переменной?
---- НОВОЕ ОБНОВЛЕНИЕ ----
Наконец-то я решил использовать apply, который, как я понимаю, более гибкий.
Это:
d1.groupby('ExamenYear').agg({'Participated': len,
'Passed': lambda x: sum(x == 'yes')})
не выглядит более неудобным, чем решение R, IMHO.
Наконец, я решил использовать применить.
Я публикую то, что я придумал, надеясь, что это может быть полезно для других.
Из того, что я понимаю из книги Уэса "Анализ Python для данных"
Вот что я придумал
def ZahlOccurence_0(x):
return pd.Series({'All': len(x['StudentID']),
'Part': sum(x['Participated'] == 'yes'),
'Pass' : sum(x['Passed'] == 'yes')})
когда я запустил его:
d1.groupby('ExamenYear').apply(ZahlOccurence_0)
Я получаю правильные результаты
All Part Pass
ExamenYear
2007 3 2 2
2008 4 3 3
2009 3 3 2
Этот подход также позволит мне комбинировать частоты с другими статистическими данными
import numpy as np
d1['testValue'] = np.random.randn(len(d1))
def ZahlOccurence_1(x):
return pd.Series({'All': len(x['StudentID']),
'Part': sum(x['Participated'] == 'yes'),
'Pass' : sum(x['Passed'] == 'yes'),
'test' : x['testValue'].mean()})
d1.groupby('ExamenYear').apply(ZahlOccurence_1)
All Part Pass test
ExamenYear
2007 3 2 2 0.358702
2008 4 3 3 1.004504
2009 3 3 2 0.521511
Я надеюсь, что кто-то еще найдет это полезное
Вы можете использовать функцию pandas crosstab, которая по умолчанию вычисляет частотную таблицу из двух или более переменных. Например,
> import pandas as pd
> pd.crosstab(d1['ExamenYear'], d1['Passed'])
Passed no yes
ExamenYear
2007 1 2
2008 1 3
2009 1 2
Используйте параметр margins=True
, если вы также хотите увидеть промежуточный итог каждой строки и столбца.
> pd.crosstab(d1['ExamenYear'], d1['Participated'], margins=True)
Participated no yes All
ExamenYear
2007 1 2 3
2008 1 3 4
2009 0 3 3
All 2 8 10
Существует другой подход, который мне нравится использовать для подобных задач, он использует groupby
и unstack
:
d1 = pd.DataFrame({'StudentID': ["x1", "x10", "x2","x3", "x4", "x5", "x6", "x7", "x8", "x9"],
'StudentGender' : ['F', 'M', 'F', 'M', 'F', 'M', 'F', 'M', 'M', 'M'],
'ExamenYear': ['2007','2007','2007','2008','2008','2008','2008','2009','2009','2009'],
'Exam': ['algebra', 'stats', 'bio', 'algebra', 'algebra', 'stats', 'stats', 'algebra', 'bio', 'bio'],
'Participated': ['no','yes','yes','yes','no','yes','yes','yes','yes','yes'],
'Passed': ['no','yes','yes','yes','no','yes','yes','yes','no','yes']},
columns = ['StudentID', 'StudentGender', 'ExamenYear', 'Exam', 'Participated', 'Passed'])
(это только исходные данные сверху)
d2 = d1.groupby("ExamenYear").Participated.value_counts().unstack(fill_value=0)['yes']
d3 = d1.groupby("ExamenYear").Passed.value_counts().unstack(fill_value=0)['yes']
d2.name = "Participated"
d3.name = "Passed"
pd.DataFrame(data=[d2,d3]).T
Participated Passed
ExamenYear
2007 2 2
2008 3 3
2009 3 2
Это решение немного более громоздко, чем предыдущее, применяя, но это легче понять и расширить, я чувствую.