Групповые панды получают фильтрованную сумму по общей сумме - PullRequest
2 голосов
/ 30 октября 2019

У меня есть следующий фрейм данных:

df = pd.DataFrame([[1, 2, True], [1, 4, False], [2, 6, False], [2, 8, True]], columns=["Group", "Value", "C"])

   Group  Value      C
0      1      2   True
1      1      4  False
2      2      6  False
3      2      8   True

И я бы хотел, чтобы каждая группа знала сумму значений, где C равно true, по всей сумме значений. Так, например, для группы 1 у нас есть 2 / (2 + 4)

Мне удалось с помощью обширного поиска достичь следующего этапа:

df.groupby('Group').agg(lambda x: x.loc[x.C == True, 'Value'].sum() / x.Value.sum())

          Value         C
Group                    
1      0.333333  0.333333
2      0.571429  0.571429

Но (как и ожидалось) я получаюдве колонки, и я хотел бы получить только одну. Мой идеальный результат был бы:

       Ratio        
Group                    
1      0.333333  
2      0.571429  

Я, конечно, могу сделать некоторые изменения после группового и получить то, что я хочу, но, поскольку я новичок в Python, мне было интересно, если я здесь что-то упускаю.

Ответы [ 3 ]

2 голосов
/ 30 октября 2019

Вы можете разделить совокупные отфильтрованные строки со всеми строками, а затем преобразовать серии в один столбец DataFrame:

filt = df.loc[df['C']].groupby('Group')['Value'].sum()
tot = df.groupby('Group')['Value'].sum()
df1 = filt.div(tot, fill_value=0).to_frame('ratio')
print (df1)
          ratio
Group          
1      0.333333
2      0.571429

Ваше решение возможно с изменением .agg, работающим со всеми столбцами, в GroupBy.apply для возврата Series, но если большие данные / много уникальных групп, это должно быть медленно:

df = (df.groupby('Group')
        .apply(lambda x: x.loc[x.C, 'Value'].sum() / x.Value.sum())
        .to_frame('ratio'))
print (df)
          ratio
Group          
1      0.333333
2      0.571429

Решения, работающие хорошо, также только с False группой:

df = pd.DataFrame([[0, 2, False], [1, 2, True], [1, 4, False], 
                   [2, 6, False], [2, 8, True]], columns=["Group", "Value", "C"])


df1 = (df.groupby('Group')
        .apply(lambda x: x.loc[x.C, 'Value'].sum() / x.Value.sum())
        .to_frame('ratio'))
print (df1)
          ratio
Group          
0      0.000000
1      0.333333
2      0.571429

filt = df.loc[df['C']].groupby('Group')['Value'].sum()
tot = df.groupby('Group')['Value'].sum()

print (df1)
          ratio
Group          
0      0.000000
1      0.333333
2      0.571429
2 голосов
/ 30 октября 2019

Я полагаю, что вы можете использовать деление на groupby.transform() с суммой и назначить с помощью .assign() после фильтрации, чтобы выровнять по индексу:

df[df['C']].assign(Ratio=df['Value']/df.groupby('Group')['Value'].transform('sum'))

Если больше 1 Истины на группу, используйте:

m=(df.groupby(['Group','C'],as_index=False,sort=False)['Value'].sum()
  .query('C==True').assign(Sum=df.groupby(['Group'])['Value'].transform('sum')))
m[['Group']].assign(Ratio=m['Value']/m['Sum'])

   Group     Ratio
0      1  0.333333
3      2  0.571429
1 голос
/ 30 октября 2019

Вы можете использовать применить :

result = df.groupby('Group').apply(lambda x: pd.Series({'ratio' : (x.Value * x.C).sum() / x.Value.sum()}))
print(result)

Выход

          ratio
Group          
1      0.333333
2      0.571429
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...