【问题标题】:apply function not working as expected in groupby应用功能在 groupby 中没有按预期工作
【发布时间】:2021-01-08 20:37:56
【问题描述】:

我有一个看起来像这样的数据框:

ID    |     timestamp    |Phase| current
========================================
001   | 2020-09-20 07:00 | A   | 1.4
001   | 2020-09-20 07:00 | B   | 2.0
001   | 2020-09-20 07:00 | C   | 1.6
002   | 2020-09-20 09:00 | A   | 1.4
002   | 2020-09-20 09:00 | B   | 1.23
002   | 2020-09-20 09:00 | C   | 1.46

我需要计算每个 ID/时间戳分组的阶段差异百分比,所以我创建了一个 groupby:

imbalanced = df.groupby(['timestamp','ID']).apply(calcImbalance)

这里是 calcImbalance:

def calcImbalance(pole):
    
        phA = pole.loc[pole['Phase'] == 'A']['current'].astype('float')
        phB = pole.loc[pole['Phase'] == 'B']['current'].astype('float')
        phC = pole.loc[pole['Phase'] == 'C']['current'].astype('float')
        
        imb = abs((phA-phB)/phB)
        print ('imb:', imb)
        if imb  >= 0.3:
            return pole
        imb = abs((phB-phA)/phA)
        if imb >= 0.3:
            return pole
        imb = abs((phA-phC)/phC)
        if imb >= 0.3:
            return pole
        imb = abs((phC-phA)/phA)
        if imb >= 0.3:
            return pole

但这只是打印:

imb: 2661   NaN
2662   NaN
Name: Amps, dtype: float64
imb: 2661   NaN
2662   NaN
Name: Amps, dtype: float64

然后 抛出异常: ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

我要做的是创建一个数据框,其中仅包含 df 中相位之间差异 > 30% 的实例。我想我已经为一些看起来应该微不足道的事情掉进了一个兔子洞

在上面的示例中,“不平衡”数据框应包含:

ID    |     timestamp    |Phase| current
========================================
001   | 2020-09-20 07:00 | A   | 1.4
001   | 2020-09-20 07:00 | B   | 2.0

apply 函数不测试阶段 B 和 C 之间的不平衡,只测试 A & B 和 A & C

【问题讨论】:

  • 请根据显示的示例数据显示预期结果的示例。
  • 另外,imbpandas.Series,而不是单个值。当您进行比较时,imb > 0.3 它需要知道您是指该系列中的任何值还是所有值。在示例的情况下,imb 为空,因此示例没有帮助。
  • 阶段之间哪一行有变化> .3?我找不到任何 IIUC。
  • 对不起,我的样本数据不好,没有不平衡>= 30%的实例!
  • 好的,已编辑示例。 ID 001 阶段 A&B 相差 42%。:abs((1.4-2)/1.4)=.42,所以它(ID 001 应该包含在不平衡的数据帧中。

标签: python pandas pandas-groupby pandas-apply


【解决方案1】:

IIUC 您可以使用 pandas 函数找到所需的行

df['cng'] = (df.groupby('ID')['current'].pct_change() + 1).groupby(df.ID).cumprod()-1
df[df.groupby('ID')['cng'].transform(lambda x: x.fillna(x.max())) > .30]

输出

   ID         timestamp Phase  current       cng
0   1  2020-09-20 07:00     A      1.4       NaN
1   1  2020-09-20 07:00     B      2.0  0.428571

这是如何工作的

查找在阶段之间发生变化的组 > .30

df[df.groupby('ID')['current'].pct_change().groupby(df.ID).transform('max') > .30]

输出

   ID         timestamp Phase  current
0   1  2020-09-20 07:00     A      1.4
1   1  2020-09-20 07:00     B      2.0
2   1  2020-09-20 07:00     C      1.6

这给出了组中的百分比变化

df.groupby('ID')['current'].pct_change()

输出

0         NaN
1    0.428571
2   -0.200000
3         NaN
4   -0.121429
5    0.186992

每组的累积变化

(df.groupby('ID')['current'].pct_change() + 1).groupby(df.ID).cumprod()

输出

0         NaN
1    1.428571
2    1.142857
3         NaN
4    0.878571
5    1.042857

此解决方案可以检测到什么?

import pandas as pd

df = pd.DataFrame([('001', '2020-09-20 07:00', 'A', 1.4),
                   ('001', '2020-09-20 07:00', 'B', 2.0),
                   ('001', '2020-09-20 07:00', 'C', 1.6),
                   ('002', '2020-09-20 09:00', 'A', 1.4),
                   ('002', '2020-09-20 09:00', 'B', 1.2),
                   ('002', '2020-09-20 09:00', 'C', 2.0),
                   ('003', '2020-09-20 09:00', 'A', 1.4),
                   ('003', '2020-09-20 09:00', 'B', 2.0),
                   ('003', '2020-09-20 09:00', 'C', 1.6),
                   ('003', '2020-09-20 09:00', 'D', 2.0),

                  ],
                  columns=['ID', 'timestamp', 'Phase', 'current'])

在数据框中

    ID         timestamp Phase  current  
0  001  2020-09-20 07:00     A      1.4  
1  001  2020-09-20 07:00     B      2.0 
2  001  2020-09-20 07:00     C      1.6 
3  002  2020-09-20 09:00     A      1.4 
4  002  2020-09-20 09:00     B      1.2 
5  002  2020-09-20 09:00     C      2.0 
6  003  2020-09-20 09:00     A      1.4 
7  003  2020-09-20 09:00     B      2.0 
8  003  2020-09-20 09:00     C      1.6 
9  003  2020-09-20 09:00     D      2.0 

有了这个解决方案

df['cng'] = (df.groupby('ID')['current'].pct_change() + 1).groupby(df.ID).cumprod()-1
df[df.groupby('ID')['cng'].transform(lambda x: x.fillna(x.max())) > .30]

结果。请注意,cng 是计算第一行变化的累积乘积。

    ID         timestamp Phase  current       cng
0  001  2020-09-20 07:00     A      1.4       NaN
1  001  2020-09-20 07:00     B      2.0  0.428571
3  002  2020-09-20 09:00     A      1.4       NaN
5  002  2020-09-20 09:00     C      2.0  0.428571
6  003  2020-09-20 09:00     A      1.4       NaN
7  003  2020-09-20 09:00     B      2.0  0.428571
9  003  2020-09-20 09:00     D      2.0  0.428571

【讨论】:

  • 感谢您的回答,但由于某种原因,您的回答的第二行引发了异常:ValueError: Length mismatch: Expected axis has 4261 elements, new values have 4590 elements
  • 使用您在问题中提供的数据框?
  • 另外,数据帧是 4590 行长,所以我不知道为什么第二个操作只需要 4261,除非这恰好是 cng = NaNs 的确切数量
  • 不,使用我正在操作的实际数据框。它是 4590 x 13
  • 我的解决方案不适用于分组列 ID 中的缺失值 (NaN)。我认为您可以删除这些行,因为您不能将它们分配给一个组。
【解决方案2】:

根据您的代码,这可能有效。这会将电流收集到一个列表中并将它们传递给calcImbalance 函数。

import pandas as pd

dd = {
'ID':[1,1,1,2,2,2],
'timestamp':['2020-09-20 07:00','2020-09-20 07:00','2020-09-20 07:00','2020-09-20 09:00','2020-09-20 09:00','2020-09-20 09:00'],
'Phase':['A','B','C','A','B','C'],
'current':[1.4,1.5,1.6,1.4,1.23,1.46]
}

df = pd.DataFrame(dd)


def calcImbalance(pole):
        
        phA, phB, phC = tuple(pole)  # currents in group
        print('ph >',phA, phB, phC)
        
        imb = abs((phA-phB)/phB)
        print ('imb:', imb)
        if imb >= 0.3:
            return pole
        imb = abs((phB-phA)/phA)
        if imb >= 0.3:
            return pole
        imb = abs((phA-phC)/phC)
        if imb >= 0.3:
            return pole
        imb = abs((phC-phA)/phA)
        if imb >= 0.3:
            return pole
            

gb = df.groupby(['timestamp','ID'])['current'].apply(lambda x:[i for i in x]).apply(calcImbalance) 

print('\n',gb)

输出

ph > 1.4 1.5 1.6
imb: 0.06666666666666672
ph > 1.4 1.23 1.46
imb: 0.13821138211382109

timestamp         ID
2020-09-20 07:00  1     None
2020-09-20 09:00  2     None
Name: current, dtype: object

-- 更新--

根据您的帖子更新,这可能不是完整的答案,但仍可能有助于获得解决方案。

【讨论】:

    【解决方案3】:

    编辑:此代码回答了问题,包括编辑。

    import pandas as pd
    
    
    def calc_imbalance(current):
        pairs_to_test = [[0, 1], [0, 2], [1, 2]]
        for pair in pairs_to_test:
            abs_percentage_imbalance = abs((current[pair[0]] - current[pair[1]])/current[pair[1]])
            if abs_percentage_imbalance >= .3:
                return pair
        return []
    
    df = pd.DataFrame([('001', '2020-09-20 07:00', 'A', 1.4),
                       ('001', '2020-09-20 07:00', 'B', 2.0),
                       ('001', '2020-09-20 07:00', 'C', 1.6),
                       ('002', '2020-09-20 09:00', 'A', 1.4),
                       ('002', '2020-09-20 09:00', 'B', 1.23),
                       ('002', '2020-09-20 09:00', 'C', 1.46)],
                      columns=['ID', 'timestamp', 'Phase', 'current'])
    
    df['original_index'] = df.index
    
    all_index_to_keep = []
    for _, group in df.groupby(['timestamp', 'ID']).agg(list).reset_index().iterrows():
        index_to_keep = calc_imbalance(group['current'])
        all_index_to_keep += [v for k, v in enumerate(group['original_index']) if k in index_to_keep]
    df.drop('original_index', axis=1, inplace=True)
    print(df.loc[all_index_to_keep, :])
    
    

    返回:

        ID         timestamp Phase  current
    0  001  2020-09-20 07:00     A      1.4
    1  001  2020-09-20 07:00     B      2.0
    

    【讨论】:

      猜你喜欢
      • 2021-12-26
      • 2015-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-25
      • 1970-01-01
      相关资源
      最近更新 更多