[英]Get sum of positive values and sum of negative values
我有一個 DataFrame 如下。
d = {}
d['Model'] = ['M1', 'M2'] * 4 * 3 * 5
d['Support'] = ['S1', 'S1', 'S2', 'S2'] * 2 * 3 * 5
d['Group'] = ['G1', 'G2', 'G2'] * 2 * 4 * 5
d['Case'] = ['C11', 'C21', 'C22', 'C31', 'C32'] * 2 * 4 * 3
val1 = []
val2 = []
random.seed(0)
for i in range (2*4*3*5):
val1.append(random.randrange(-10, 11))
val2.append(random.randrange(-10, 11))
d['val1'] = val1
d['val2'] = val2
df = pd.DataFrame(d)
我正在尋找每個支持、組和案例的正值之和的最大值,並參考 Model 和負值之和的最小值之和。
這是我的嘗試:
df1 = df.groupby(['Model', 'Support', 'Group', 'Case'])[['val1', 'val2']].sum()
df2 = df1.groupby(['Model', 'Support', 'Group', 'Case']).agg([
('max' , lambda x : x[x > 0].sum()),
('min' , lambda x : x[x < 0].sum())
])
df3 = df2.groupby(['Support', 'Group', 'Case']).agg([
('max' , max),
('Model', lambda x: x.idxmax()[0]),
('min' , min),
('Model', lambda x: x.idxmin()[0]),
])
(見 2021 年 1 月 14 日版本)
最后一個DataFrame df3
的結果還可以,但這不是我想要的output格式。 我需要過濾最后一個 DataFrame df3
以通過這種方式獲得結果:
2021 年 1 月 14 日編輯
查看@aneroid 結果,我發現 df3 值與預期不符。
對於 Support S1
, Group G1
, Case C11
這將是結果:
df_M1S1G1_1 = df.loc[df['Model'] == 'M1']
df_M1S1G1_2 = df_M1S1G1_1.loc[df['Support'] == 'S1']
df_M1S1G1_3 = df_M1S1G1_2.loc[df['Group'] == 'G1']
Model Support Group Case val1 val2
0 M1 S1 G1 C11 2 3
12 M1 S1 G1 C22 -8 0
24 M1 S1 G1 C32 5 0
36 M1 S1 G1 C21 7 -4
48 M1 S1 G1 C31 -6 -9
60 M1 S1 G1 C11 10 0
72 M1 S1 G1 C22 10 -4
84 M1 S1 G1 C32 -8 -3
96 M1 S1 G1 C21 -5 0
108 M1 S1 G1 C31 -9 7
df_M2S1G1_1 = df.loc[df['Model'] == 'M2']
df_M2S1G1_2 = df_M2S1G1_1.loc[df['Support'] == 'S1']
df_M2S1G1_3 = df_M2S1G1_2.loc[df['Group'] == 'G1']
Model Support Group Case val1 val2
9 M2 S1 G1 C32 -2 7
21 M2 S1 G1 C21 -10 -8
33 M2 S1 G1 C31 -1 7
45 M2 S1 G1 C11 9 -2
57 M2 S1 G1 C22 -8 0
69 M2 S1 G1 C32 10 7
81 M2 S1 G1 C21 -10 7
93 M2 S1 G1 C31 5 8
105 M2 S1 G1 C11 -6 7
117 M2 S1 G1 C22 -8 -10
所以:
0 M1 S1 G1 C11 val1 = 2 val2 = 3
60 M1 S1 G1 C11 val1 = 10 val2 = 0
val1_sum_pos = 12
val1_sum_neg = 0
val2_sum_pos = 3
val2_sum_neg = 0
45 M2 S1 G1 C11 val1 = 9 val2 = -2
105 M2 S1 G1 C11 val1 = -6 val2 = 7
val1_sum_pos = 9
val1_sum_neg = -6
val2_sum_pos = 7
val2_sum_neg = -2
結果:
val1
val2
max Model_max min Model_min max Model_max min Model_min
Support Group Case
S1 G1 C11 12 M1 -6 M2 7 M2 -2
M2
這些結果與@aneroid 提出的解決方案一致。
這是與我之前的回答不同的方法。 之后需要更多的“准備代碼”但更少的步驟來按摩 dataframe。
和以前一樣,這是基於從您的第 2 步開始(跳過第 1 步):
df1 = df.groupby(['Model', 'Support', 'Group', 'Case']).agg([
('sum_pos', lambda x: x[x > 0].sum()),
('sum_neg', lambda x: x[x < 0].sum())
])
首先,模板 dataframe 為結果:
res = pd.DataFrame(
columns=pd.MultiIndex.from_product(
[('val1', 'val2'),
('sum_pos_max', 'Model_max', 'sum_neg_min', 'Model_min'),
]))
這個 function 將(再次)用於應用,不同之處在於我們將首先創建一個具有所需列結構的空 DataFrame。 (並且可選地,可以修改for group in grouped
一起使用,而不使用 apply,其中每條記錄都附加到它。)
def model_minmax_opt(gr):
# start with a deep copy of the result template
tf = res.copy(deep=True)
for val in ['val1', 'val2']:
max_pos = gr[(val, 'sum_pos')].idxmax()
min_neg = gr[(val, 'sum_neg')].idxmin()
tf.loc[0, [(val, 'sum_pos_max'), (val, 'Model_max')]] = gr.loc[max_pos, [(val, 'sum_pos'), ('Model', '')]].values
tf.loc[0, [(val, 'sum_neg_min'), (val, 'Model_min')]] = gr.loc[min_neg, [(val, 'sum_neg'), ('Model', '')]].values
return tf
然后創建一個分組並應用它:
group = df1.reset_index().groupby(['Support', 'Group', 'Case'])
df2 = group.apply(model_minmax_opt)
df2.reset_index(level=3, drop=True, inplace=True) # get rid of the added 0-index
結果df2
,與另一個相同,具有更好的列排序:
val1 val2
sum_pos_max Model_max sum_neg_min Model_min sum_pos_max Model_max sum_neg_min Model_min
Support Group Case
S1 G1 C11 12 M1 -6 M2 7 M2 -2 M2
C21 7 M1 -20 M2 7 M2 -8 M2
C22 10 M1 -16 M2 0 M1 -10 M2
C31 5 M2 -15 M1 15 M2 -9 M1
C32 10 M2 -8 M1 14 M2 -3 M1
G2 C11 9 M1 -23 M1 14 M1 -9 M1
C21 22 M1 -13 M2 21 M1 -9 M1
C22 24 M2 -6 M1 14 M2 -12 M2
C31 30 M2 -28 M1 28 M2 -6 M1
C32 11 M2 -10 M2 11 M2 -16 M1
S2 G1 C11 9 M1 -8 M1 1 M2 -15 M1
C21 6 M1 -9 M1 15 M2 -13 M1
C22 11 M2 -3 M1 0 M1 -8 M2
C31 5 M1 -7 M2 4 M1 -5 M1
C32 17 M1 0 M1 0 M1 -13 M2
G2 C11 20 M1 -10 M2 11 M1 -14 M1
C21 5 M1 -19 M1 6 M2 -18 M2
C22 11 M1 -23 M2 24 M1 -15 M2
C31 26 M1 -22 M2 10 M2 -12 M1
C32 11 M2 -20 M1 13 M2 -11 M1
這是另外兩個解決方案,它們與您的原始代碼和您帖子中的最終 dataframe 更具可比性。
第一步對groupby
的順序稍作修改。 minmax()
function 確定它是否被賦予一列正數或負數; 並根據需要應用idxmin/idxmax
。 並且返回應該是值還是Model ,作為參數( y
)傳入。 使用agg
可以在現有列索引下生成額外的列,作為另一個級別。 這提供了與您帖子中的格式相同的格式。
df1 = df.groupby(['Support', 'Group', 'Case', 'Model']).agg([
('sum_pos', lambda x: x[x > 0].sum()),
('sum_neg', lambda x: x[x < 0].sum())
])
# Model is the last column of the index, so `-1`
def minmax(x, y):
"""x is the series, y='val' or 'Model'"""
min = x.min()
if min >= 0: # or check x.name
# postive column, return max's; or both vals are 0
max_pos = x.idxmax()
return x[max_pos] if y == 'val' else max_pos[-1]
# negative column, return min's
return min if y == 'val' else x.idxmin()[-1]
# use `minmax` to generate columns in an aggregate:
df1.groupby(['Support', 'Group', 'Case']).agg([
('val', lambda x: minmax(x, 'val')),
('Model', lambda x: minmax(x, 'Model')),
])
結果與我之前的兩個答案相同,與您的帖子具有相同的結構(3 級索引列):
val1 val2
sum_pos sum_neg sum_pos sum_neg
val Model val Model val Model val Model
Support Group Case
S1 G1 C11 12 M1 -6 M2 7 M2 -2 M2
C21 7 M1 -20 M2 7 M2 -8 M2
C22 10 M1 -16 M2 0 M1 -10 M2
C31 5 M2 -15 M1 15 M2 -9 M1
C32 10 M2 -8 M1 14 M2 -3 M1
G2 C11 9 M1 -23 M1 14 M1 -9 M1
C21 22 M1 -13 M2 21 M1 -9 M1
C22 24 M2 -6 M1 14 M2 -12 M2
C31 30 M2 -28 M1 28 M2 -6 M1
C32 11 M2 -10 M2 11 M2 -16 M1
S2 G1 C11 9 M1 -8 M1 1 M2 -15 M1
C21 6 M1 -9 M1 15 M2 -13 M1
C22 11 M2 -3 M1 0 M1 -8 M2
C31 5 M1 -7 M2 4 M1 -5 M1
C32 17 M1 0 M1 0 M1 -13 M2
G2 C11 20 M1 -10 M2 11 M1 -14 M1
C21 5 M1 -19 M1 6 M2 -18 M2
C22 11 M1 -23 M2 24 M1 -15 M2
C31 26 M1 -22 M2 10 M2 -12 M1
C32 11 M2 -20 M1 13 M2 -11 M1
一個更短的解決方案,但會生成一個(min/max val, Model)
的元組,而不是將它們放在不同的列中:
df1 = df.groupby(['Support', 'Group', 'Case', 'Model']).agg([
('sum_pos', lambda x: x[x > 0].sum()),
('sum_neg', lambda x: x[x < 0].sum())
])
df1.groupby(['Support', 'Group', 'Case']).agg({
('val1', 'sum_pos'): lambda x: (x.max(), x.idxmax()[-1]),
('val1', 'sum_neg'): lambda x: (x.min(), x.idxmin()[-1]),
('val2', 'sum_pos'): lambda x: (x.max(), x.idxmax()[-1]),
('val2', 'sum_neg'): lambda x: (x.min(), x.idxmin()[-1]),
})
結果樣本:
val1 val2
sum_pos sum_neg sum_pos sum_neg
Support Group Case
S1 G1 C11 (12, M1) (-6, M2) (7, M2) (-2, M2)
C21 (7, M1) (-20, M2) (7, M2) (-8, M2)
C22 (10, M1) (-16, M2) (0, M1) (-10, M2)
C31 (5, M2) (-15, M1) (15, M2) (-9, M1)
...
您已經說過“尋找正值之和的最大值”和“負值之和的最小值之和”,但是在第一步中,您已將sum()
應用於整個groupby 不區分 +ve 或 -ve 值。 對我來說,您的第二步df2 = df1.groupby(...).agg(...)
實際上應該是您的第一步。
根據您的原始代碼設置df
,然后:
# doing your 2nd step as the first step
df1 = df.groupby(['Model', 'Support', 'Group', 'Case']).agg([
('sum_pos', lambda x: x[x > 0].sum()),
('sum_neg', lambda x: x[x < 0].sum())
])
# Btw, some sum's are `0` in `df1`
# stacking `val1` and `val2` into a column
df2 = df1.stack(level=0)
df2.index.names = df2.index.names[:-1] + ['val']
創建一個 function ,它將計算 model 與每個val
的“正數最大值”和“負數最小值”相關聯,並將該值包含在返回中:
def model_minmax(gr):
"""modifies both rows of the group object passed in"""
gr[['sum_pos_max', 'Model_max']] = gr.loc[gr['sum_pos'].idxmax(), ['sum_pos', 'Model']]
gr[['sum_neg_min', 'Model_min']] = gr.loc[gr['sum_neg'].idxmin(), ['sum_neg', 'Model']]
return gr
創建組並apply()
上面的 function:
group = df2.reset_index().groupby(['Support', 'Group', 'Case', 'val'])
df3 = group.apply(model_minmax)
# sort values and drop every alternate row
df3 = df3.sort_values(['Support', 'Group', 'Case', 'val', 'Model'])[::2]
什么df3.head(4)
看起來像:
Model Support Group Case val sum_neg sum_pos sum_pos_max Model_max sum_neg_min Model_min
0 M1 S1 G1 C11 val1 0 12 12 M1 -6 M2
1 M1 S1 G1 C11 val2 0 3 7 M2 -2 M2
2 M1 S1 G1 C21 val1 -5 7 7 M1 -20 M2
3 M1 S1 G1 C21 val2 -4 0 7 M2 -8 M2
刪除 'Model'、sum_neg' 和 'sum_pos' 列以及一些步驟以獲取與您所擁有的格式相似的數據:
df3.drop(['Model', 'sum_neg', 'sum_pos'], axis=1, inplace=True)
df4 = df3.pivot(index=['Support', 'Group', 'Case'],
columns='val',
values=['sum_pos_max', 'Model_max', 'sum_neg_min', 'Model_min'],
)
df4 = df4.swaplevel(0, 1, axis=1).sort_index(axis=1, level=0)
結果:
val val1 val2
Model_max Model_min sum_neg_min sum_pos_max Model_max Model_min sum_neg_min sum_pos_max
Support Group Case
S1 G1 C11 M1 M2 -6 12 M2 M2 -2 7
C21 M1 M2 -20 7 M2 M2 -8 7
C22 M1 M2 -16 10 M1 M2 -10 0
C31 M2 M1 -15 5 M2 M1 -9 15
C32 M2 M1 -8 10 M2 M1 -3 14
G2 C11 M1 M1 -23 9 M1 M1 -9 14
C21 M1 M2 -13 22 M1 M1 -9 21
C22 M2 M1 -6 24 M2 M2 -12 14
C31 M2 M1 -28 30 M2 M1 -6 28
C32 M2 M2 -10 11 M2 M1 -16 11
S2 G1 C11 M1 M1 -8 9 M2 M1 -15 1
C21 M1 M1 -9 6 M2 M1 -13 15
C22 M2 M1 -3 11 M1 M2 -8 0
C31 M1 M2 -7 5 M1 M1 -5 4
C32 M1 M1 0 17 M1 M2 -13 0
G2 C11 M1 M2 -10 20 M1 M1 -14 11
C21 M1 M1 -19 5 M2 M2 -18 6
C22 M1 M2 -23 11 M1 M2 -15 24
C31 M1 M2 -22 26 M2 M1 -12 10
C32 M2 M1 -20 11 M2 M1 -11 13
請注意,如上所述,由於您的第一步,這些值將與您的不同。 如果您確定這些是正確的,可以在我給出的之前添加。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.