вычитание pandas по значению столбца

У меня есть один временной ряд в кадре данных pandas, в котором есть строка с месяцем. Я позвонил df1. Затем я получаю среднемесячное значение по group_by, я назвал df2 полученным кадром данных. Теперь я хотел бы вычесть среднемесячное значение каждого столбца без использования цикла. То есть строку «месяц==1» в df2 нужно вычесть из всех строк, где «месяц»==1 в df1.

В numpy я бы изменил матрицу, выполнил операцию (из-за трансляции numpy) и снова изменил ее форму. Но я не знаю, что такое пандатонический способ сделать это. Несмотря на то, что у меня есть ощущение, что это должен быть очень простой подход.

Вот пример, где я делаю это очень неэффективным способом с двойным циклом for.

import pandas as pd

df1 = pd.DataFrame({'month': [1, 1, 1, 2, 2, 3, 3, 3, 4, 4],
                   'value': [51, 16, 17, 25, 28, 37, 39, 73, 84, 56],
                    'value2': [551, 165, 175, 255, 258, 375, 359, 735, 854, 556]})


df2 = df1.groupby(["month"]).mean()
df2["month"] = [1,2,3,4]

for mon in range(1, 5):
    for val in ["value", "value2"]:
        mon_mean = float(df2.loc["month"] == mon, [val])
        df1.loc[df1["month"] == mon, [val]] = df1.loc[df1["month"] == mon, [val]].apply(lambda x: x- mon_mean)

Любая подсказка или функция pandas, которую вы бы использовали, более чем приветствуется.

ИЗМЕНИТЬ:

Как бы вы это сделали, если бы вместо вычитания df2 из df1 это был бы третий кадр данных, а именно df3 с большим количеством строк, чем df1. Таким образом, расширяя предыдущий пример:

import pandas as pd

df1 = pd.DataFrame({'month': [1, 1, 2, 2, 3, 3, 4, 4],
                    'value': [51, 16, 17, 25, 28, 37, 39, 73],
                    'value2': [551, 165, 175, 255, 258, 375, 359, 735, ]})

df3 = pd.DataFrame({'month': [1, 1, 1, 2, 2, 3, 3, 3, 4, 4],
                    'value': [43, 32, 54, 54, 54, 35, 76, 65, 35, 74],
                    'value2': [745, 346, 175, 889, 543, 876, 345, 876, 345, 987]})

df2 = df1.groupby(["month"]).mean()
df2["month"] = [1, 2, 3, 4]

for mon in range(1, 5):
    for val in ["value", "value2"]:
        mon_mean = float(df2[df2["month"] == mon][val])
        print(mon_mean)
        df3.loc[df3["month"] == mon, [val]] = df3.loc[df3["month"] == mon, [val]].apply(lambda x: x - mon_mean)
print(df3)
month  value  value2
0      1    9.5   387.0
1      1   -1.5   -12.0
2      1   20.5  -183.0
3      2   33.0   674.0
4      2   33.0   328.0
5      3    2.5   559.5
6      3   43.5    28.5
7      3   32.5   559.5
8      4  -21.0  -202.0
9      4   18.0   440.0

person Xbel    schedule 18.06.2020    source источник


Ответы (1)


Используйте GroupBy.transform. для нового DataFrame, заполненного совокупными значениями, поэтому возможно вычитание на DataFrame.sub только отфильтрованные столбцы в списке c:

c = ["value", "value2"]
df1[c] = df1[c].sub(df1.groupby(["month"])[c].transform('mean'))
print (df1)
   month      value      value2
0      1  23.000000  254.000000
1      1 -12.000000 -132.000000
2      1 -11.000000 -122.000000
3      2  -1.500000   -1.500000
4      2   1.500000    1.500000
5      3 -12.666667 -114.666667
6      3 -10.666667 -130.666667
7      3  23.333333  245.333333
8      4  14.000000  149.000000
9      4 -14.000000 -149.000000

Подробности:

print (df1.groupby(["month"])[c].transform('mean'))
       value      value2
0  28.000000  297.000000
1  28.000000  297.000000
2  28.000000  297.000000
3  26.500000  256.500000
4  26.500000  256.500000
5  49.666667  489.666667
6  49.666667  489.666667
7  49.666667  489.666667
8  70.000000  705.000000
9  70.000000  705.000000

РЕДАКТИРОВАТЬ: для вычитания в другом DataFrame используется month преобразованный в индекс с помощью DataFrame.set_index для данных о совпадениях по месяцам:

c = ["value", "value2"]
df2 =  df1.groupby(["month"])[c].mean()

df3 = df3.set_index('month')[c].sub(df2).reset_index()
print (df3)
   month  value  value2
0      1    9.5   387.0
1      1   -1.5   -12.0
2      1   20.5  -183.0
3      2   33.0   674.0
4      2   33.0   328.0
5      3    2.5   559.5
6      3   43.5    28.5
7      3   32.5   559.5
8      4  -21.0  -202.0
9      4   18.0   440.0
person jezrael    schedule 18.06.2020
comment
Продлеваю вопрос, так как понял, что мой пример недостаточно точен. - person Xbel; 18.06.2020
comment
@Xbel - я пытаюсь запустить код редактирования и получаю KeyError: 'month' - возможно ли добавить ожидаемый результат для тестирования моего нового решения? - person jezrael; 18.06.2020
comment
@jerzal Исправлен и добавлен ожидаемый результат. Извините за неудобства. - person Xbel; 18.06.2020
comment
Спасибо тебе. Я бы чаще использовал Pandas и углубился во все эти функции. Они чрезвычайно полезны. - person Xbel; 18.06.2020