2015-04-10 3 views
0

Я пытаюсь преобразовать рамку данных pandas в один из кумулятивных счетчиков/процентов. Мой текущий dataframe выглядит следующим образом:Создание таблицы счетчиков в Python pandas

Name Purchase 
alice apple 
bob  orange 
dave orange 
bob  apple 
bob  apple 
alice apple 

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

Name tot-purchases num-apple percent-apple 
alice 2    2   100 
bob  3    2   66.67 
dave 1    0   0 

Я знаю, что могу использовать ДФ [ «Name»] .value_counts(), чтобы получить «tot-покупки», но я не могу понять, как использовать «Имя» в качестве ключа для двух других столбцов. Мне просто не хватает базового понимания того, что groupby и оператор select [] делают на dataframes. Например, я бы подумал, что это даст мне DataFrame из всего случаев с яблоками, но это дает мне недопустимое сравнение типа:

df[df['Purchase'] == 'apple']['Name']] 

ответ

1

Что-то, как это должно работать. Он будет быстрее, чем apply, поскольку он использует векторизованные операции. Кроме того, вместо того, чтобы жестко кодировать результат apple, он дает вам количество и проценты для всех покупок, независимо от того, сколько у вас может быть.

>>> df2 = df.reset_index().groupby(['Name', 'Purchase']).count().unstack('Purchase').fillna(0) 
>>> df2.columns = df2.columns.droplevel(0) 
>>> 
>>> totals = df2.sum(axis=1) 
>>> totals.name = 'tot-purchases' 
>>> 
>>> df3 = df2.divide(df2.sum(axis=1), axis=0) 
>>> 
>>> df2.columns = ['num-'+x for x in df2.columns] 
>>> df3.columns = ['percent-'+x for x in df3.columns] 
>>> dff = pd.concat([totals, df2, df3], axis=1) 
>>> 
>>> print(dff) 
     tot-purchases num-apple num-orange percent-apple percent-orange 
Name                  
alice    2   2   0  1.000000  0.000000 
bob    3   2   1  0.666667  0.333333 
dave    1   0   1  0.000000  1.000000 
>>> print(dff.loc[:,('tot-purchases', 'num-apple', 'percent-apple')]) 
     tot-purchases num-apple percent-apple 
Name           
alice    2   2  1.000000 
bob    3   2  0.666667 
dave    1   0  0.000000 

В основном он делит данные на группы по Name и Purchase, затем подсчитывает, сколько в каждой группе. Затем он задает имя Purchase быть заголовком столбца, давая вам 2D DataFrame где index является Name, то columns является Purchase типа, а значения отсчетов этого Purchase типа для данного Name. Тогда вопрос арифметики - получить проценты и итоговые суммы.

Если вы готовы изменить выход немного, вы можете сделать что-то еще более полезным с MultiIndex:

>>> df2 = df.reset_index().groupby(['Name', 'Purchase']).count().unstack('Purchase').fillna(0) 
>>> df2.columns.rename('Value',level=0, inplace=True) 
>>> df2.columns = df2.columns.set_levels(['Count'], level=0) 
>>> 
>>> totals = df2.sum(axis=1) 
>>> totals.name = ('Count', 'all') 
>>> 
>>> df3 = df2.divide(df2.sum(axis=1), axis=0) 
>>> df3.columns = df3.columns.set_levels(['Percent'], level=0) 
>>> 
>>> dff = pd.concat([totals, df2, df3], axis=1) 
>>> 
>>> print(dff) 
     Count    Percent   
     all apple orange  apple orange 
Name           
alice  2  2  0 1.000000 0.000000 
bob  3  2  1 0.666667 0.333333 
dave  1  0  1 0.000000 1.000000 
1

я написал небольшую функцию, чтобы сделать это.

Pass фрейм данных с DF и столбца, который эксплуатируется на колонке .

def fruits(df,column): # column needs to be string 

     df['tot-purchases'] = 1 
     for item in df[column].unique(): 
       df['num-%s' % item] = df[column].apply(lambda value: 1 if value == str(item) else 0) 

     new_data = data.groupby('Name').sum() 
     cols = [col for col in new_data.columns if 'num' in col] 
     for col in cols: 
       new_data[col.replace('num','percent')] = new_data[col]/new_data['tot-purchases'] * 100 


     return new_data 

Выход:

In [73]: data 
Out[73]: 
    Name Purchase 
0 alice apple 
1 bob orange 
2 dave orange 
3 bob apple 
4 bob apple 
5 alice apple 

In [74]: print fruits(data, 'Purchase') 
     tot-purchases num-apple num-orange percent-apple percent-orange 
Name                  
alice    2   2   0  100.000000  0.000000 
bob    3   2   1  66.666667  33.333333 
dave    1   0   1  0.000000  100.000000 
+0

Мне нравится этот ответ, потому что это концептуально легче понять, хотя, как TheBlackCat указывает, вся итерация делает ее менее эффективной. Но он тоже работает. – Guerre

Смежные вопросы