簡體   English   中英

獲取正值的總和和負值的總和

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM