Pandas: пользовательская функция groupby (). Apply (), когда переменные группы не одинаковой длины? - PullRequest
0 голосов
/ 20 июня 2020

У меня есть большой набор данных из более чем 2 миллионов строк со следующей структурой:

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'name': ['Alex', 'Joe', 'Alex', 'Joe', 'Alex', 'Joe', 'Alex', 'Joe', 'Alex', 'Joe', 'Alex'],
    'month': ['May', 'May', 'May', 'May', 'May', 'May', 'April', 'April', 'April', 'April', 'February'],
    'variable': ['Cash', 'Cash', 'Debt', 'Debt', 'Others', 'Others', 'Cash', 'Cash', 'Debt', 'Debt', 'Cash'],
    'value': np.random.randint(low=0, high=100, size=11)
})

        name     month variable  value
0   Alex       May     Cash     44
1    Joe       May     Cash     47
2   Alex       May     Debt     64
3    Joe       May     Debt     67
4   Alex       May   Others     67
5    Joe       May   Others      9
6   Alex     April     Cash     83
7    Joe     April     Cash     21
8   Alex     April     Debt     36
9    Joe     April     Debt     87
10  Alex  February     Cash     70

Если бы я хотел рассчитать net долг для каждого человека за каждый месяц, я бы сделал следующее:

df.groupby(['name', 'month']).apply(lambda x: x[x['variable'] == 'Debt'].value - x[x['variable'] == 'Cash'].value)

name  month
Alex  April     6    NaN
                8    NaN
      February  10   NaN
      May       0    NaN
                2    NaN
Joe   April     7    NaN
                9    NaN
      May       1    NaN
                3    NaN
Name: value, dtype: float64 

Однако результат полон значений NA, которые, как я полагаю, являются результатом того, что фрейм данных не имеет одинакового количества ca sh и переменных долга для каждого человека и месяца. Есть ли у меня способ избежать этого и просто получить долг net за каждый месяц / человека, когда это возможно, и НС, когда это не так? и, как я уже упоминал, набор данных, над которым я работаю, довольно велик, поэтому, если кто-нибудь знает более быстрый / альтернативный метод для этого, был бы очень признателен!

Ответы [ 2 ]

1 голос
/ 20 июня 2020

Вы можете использовать df.pivot_table, а затем df.sub здесь.

df_p = df.pivot_table(index=['name','month'], columns='variable',values='value',aggfunc='sum')

df_p['Debt'].sub(df_p['Cash'],fill_value=0)

name  month
Alex  April      -47.0
      February   -70.0
      May         20.0
Joe   April       66.0
      May         20.0
dtype: float64

Или

df_p = df.set_index(['name','month','variable'])['value'].unstack()
df_p['Debt'].sub(df_p['Cash'],fill_value=0)

Результаты Timeit

In [41]: %%timeit
    ...: df_p = df.set_index(['name','month','variable'])['value'].unstack()
    ...: df_p['Debt'].sub(df_p['Cash'],fill_value=0)
    ...:
    ...:
3.13 ms ± 8.91 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [42]: %%timeit
    ...: df_p = df.pivot_table(index=['name','month'], columns='variable',values='value')
    ...: df_p['Debt'].sub(df_p['Cash'],fill_value=0)
    ...:
    ...:
6.9 ms ± 99.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [43]: %%timeit
    ...: (df.groupby(['name', 'month']).apply(lambda d: d.loc[d["variable"].eq("Debt"),"value"].sum()
    ...:                                                      - d.loc[d["variable"].eq("Cash"),"value"].sum()))
    ...:
6.64 ms ± 421 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1 голос
/ 20 июня 2020

IIU C, используйте loc:

print (df.groupby(['name', 'month']).apply(lambda d: d.loc[d["variable"].eq("Debt"),"value"].sum() 
                                                     - d.loc[d["variable"].eq("Cash"),"value"].sum()))

name  month   
Alex  April       40
      February   -17
      May        -25
Joe   April        5
      May        -38
dtype: int64
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...