Эффективный способ агрегирования в dataFrame сгруппированных и вывода в новый DataFrame - PullRequest
2 голосов
/ 11 октября 2019

Я пытаюсь понять, есть ли лучший и более эффективный способ сделать это:Пример данных:

df = pd.DataFrame ({'ID' : ['A','A','A','A','B','B','B','B'], 
'Month' : [-4,-3,1,2,-3,-2,1,2],
'Cost' : [20,30,10,15,1,2,5,10] })

Затем я сгруппировал ID:

df = df.groupby(ID)Затем я объединяю стоимость с условием <0 и> 0 в цикле for и сохраняю вывод в новом dataFrame:

output = pd.DataFrame([])
for group, data in df:
    totalPre = 0
    totalPost = 0
    for row_index, row in data.iterrows():
            if row ['Month'] < 0:
                totalPre = totalPre + row ['Cost']
            elif row['Month'] > 0:
                totalPost = totalPost + row ['Cost']
    output = output.append(pd.DataFrame({'ID': group, 'Total pre': totalPre,'Total post': totalPost }, index=[0]), ignore_index=True)

И вот вывод:

index  ID.  Total pre   Total post
0      A       50         25
1      B       3          15

Спасибо.

Ответы [ 5 ]

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

Я считаю, что это хорошая и простая альтернатива!

df_1 = pd.DataFrame([])
df_1 = df_1.assign(totalPre=df[df['Month'] < 0].groupby('ID')['Cost'].sum(), 
                   totalPost= df[df['Month'] > 0].groupby('ID')['Cost'].sum())
print(df_1)

Вывод:

    totalPre  totalPost
ID
A         50         25
B          3         15
1 голос
/ 11 октября 2019

Использование mask с GroupBy.sum:

grp = df.mask(df['Month']>0).groupby('ID', as_index=False)['Cost'].sum().rename(columns={'Cost':'Total pre'})
grp['Total post'] = df.mask(df['Month']<0).groupby('ID')['Cost'].sum().to_numpy()

Выход

  ID  Total pre  Total post
0  A       50.0        25.0
1  B        3.0        15.0

Подробности

mask устанавливает строки, которые соответствуют условиям (Month > 0), равными NaN, таким образом, мы можем groupby.sum и получать только правильные строки:

df.mask(df['Month']>0)

    ID  Month  Cost
0    A   -4.0  20.0
1    A   -3.0  30.0
2  NaN    NaN   NaN
3  NaN    NaN   NaN
4    B   -3.0   1.0
5    B   -2.0   2.0
6  NaN    NaN   NaN
7  NaN    NaN   NaN
0 голосов
/ 11 октября 2019

Для петель редко являются единственным и лучшим решением в пандах. Возможно, я бы создал новый столбец для условия до / после, а затем сгруппировал бы по ID и новому столбцу. Group by создает DataFrame для каждой уникальной комбинации значений в указанных столбцах, а затем агрегирует значения с помощью функции.

import pandas as pd
import numpy as np

# sample DataFrame
df = pd.DataFrame ({'ID' : ['A','A','A','A','B','B','B','B'], 
'Month' : [-4,-3,1,2,-3,-2,1,2],
'Cost' : [20,30,10,15,1,2,5,10] })

# Create a new column `Timepoint` to group by
df['Timepoint'] = (df['Month'] <= 0).replace({True: 'pre', False: 'post'})
# Create a group for each unique combination of `ID` and `Timepoint` and aggregate the `Cost` using the function `sum`.
output = df.groupby(['ID', 'Timepoint'])['Cost'].sum()

Мой вывод:

Timepoint  post  pre
ID                  
A            25   50
B            15    3
0 голосов
/ 11 октября 2019

Один из способов - отфильтровать Month==0 1 , а затем сгруппировать по ID и условию, чтобы месяц был меньше 0.

output = df[df["Month"]!=0].groupby(["ID", df["Month"]<0])["Cost"].sum()\
    .unstack().reset_index().rename_axis(None, axis=1)
    .rename(columns={True: "Total pre", False: "Total post"})

print(output)
#  ID  Total post  Total pre
#0  A          25         50
#1  B          15          3

1 Поскольку вы, похоже, указываете, что ваше состояние должно быть строго больше / меньше чем.

0 голосов
/ 11 октября 2019

Вы можете сделать это несколькими способами.

Один способ фильтрации до groupby

df1 = df[df["Month"]<0].groupby("ID")["Cost"].sum()\
                       .reset_index(name="Total_pre")

df2 = df[df["Month"]>0].groupby("ID")["Cost"].sum()\
                       .reset_index(name="Total_post")

out = pd.merge(df1, df2, on="ID", how="outer")

Другой способ - группировка по ID и условию, а затем использование pd.pivot_table

out = df.groupby(["ID", df["Month"]<0])["Cost"].sum()\
        .reset_index()

out = pd.pivot_table(out,
                     index="ID",
                     columns="Month",
                     values="Cost")\
        .reset_index()\
        .rename(columns={"False":"Total_post",
                         "True":"Total_pre"})

out.columns.name = None

РЕДАКТИРОВАТЬ В последнем случае, если вам нужно исключить случай, Месяц равен 0, вы можете добавить этот фильтр df["Month"]!=0 до groupby

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