Pandas-根据多种条件更新列-按方法分组

我的目标是以下输出。

一种 C D F
0000 ZZZ 987 QW1 8 前三四列和偏移量
0000 ZZZ 987 QW1 -8 前三四列和偏移量
0000 ZZZ 987 QW1 -8 第一次或不匹配
1111 AAA 123 AB1 1 前三四列和偏移量
1111 AAA 123 CD1 -1 前三四列和偏移量
2222 BBB 456 EF1 -4 前三四列和偏移量
2222 BBB 456 GH1 -1 前三四列和偏移量
2222 BBB 456 IL1 5 前三四列和偏移量
3333 CCC 789 MN1 2 前两个 col 和 offset
3333 CCC 101 MN1 -2 前两个 col 和 offset
4444 直拨电话 121 UYT 6 前两个 col 和 offset
4444 直拨电话 131 FB1 -5 前两个 col 和 offset
4444 直拨电话 141 UYT -1 前两个 col 和 offset
5555 电子电气设备 151 CB1 3 前两个 col 和 offset
5555 电子电气设备 161 CR1 -3 前两个 col 和 offset
5555 电子电气设备 161 CR1 -5 第一次或不匹配
6666 FFF 111 CB1 4 第一次或不匹配
7777 GGG 222 ZB1 10.5 前三四列和小偏移
7777 GGG 222 ZB1 -10 前三四列和小偏移

回答

这是解决方案的一个潜在开始......我怀疑这个逻辑需要更加强大才能处理您的真实世界数据集。

#Read in your dataframe from this question
df = pd.read_clipboard(dtype={'A':'str'})

def f2(x):
    cum = x.cumsum()
    m = (cum == 0)[::-1].cumsum()[::-1].astype(bool)
    x[m]='first two col and offset'
    x[~m]=np.nan
    return x

def f1(x):
    cum = x.cumsum()
    m = (cum == 0)[::-1].cumsum()[::-1].astype(bool)
    x[m]='first three col and offset'
    cl = ((cum.abs() <= .5) & (cum != 0))[::-1].cumsum()[::-1].astype(bool)
    x[cl] = 'first three col and small offset'
    x[~m & ~cl] = np.nan
    return x
    
df['F2'] = df.groupby(['A','B'])['E'].apply(f2)
df['F1'] = df.groupby(['A', 'B', 'C'])['E'].apply(f1)
df['F'] = df['F1'].fillna(df['F2']).fillna('first or no match')
df = df.drop(['F1', 'F2'], axis=1)

输出:

       A    B    C    D     E                                 F
0   0000  ZZZ  987  QW1   8.0        first three col and offset
1   0000  ZZZ  987  QW1  -8.0        first three col and offset
2   0000  ZZZ  987  QW1  -8.0                 first or no match
3   1111  AAA  123  AB1   1.0        first three col and offset
4   1111  AAA  123  CD1  -1.0        first three col and offset
5   2222  BBB  456  EF1  -4.0        first three col and offset
6   2222  BBB  456  GH1  -1.0        first three col and offset
7   2222  BBB  456  IL1   5.0        first three col and offset
8   3333  CCC  789  MN1   2.0          first two col and offset
9   3333  CCC  101  MN1  -2.0          first two col and offset
10  4444  DDD  121  UYT   6.0          first two col and offset
11  4444  DDD  131  FB1  -5.0          first two col and offset
12  4444  DDD  141  UYT  -1.0          first two col and offset
13  5555  EEE  151  CB1   3.0          first two col and offset
14  5555  EEE  161  CR1  -3.0          first two col and offset
15  5555  EEE  161  CR1  -5.0                 first or no match
16  6666  FFF  111  CB1   4.0                 first or no match
17  7777  GGG  222  ZB1  10.5  first three col and small offset
18  7777  GGG  222  ZB1 -10.0  first three col and small offset

细节:

  • f2,基于相同的“A”和“B”取一组“E”,然后计算累积和。

  • 然后我们检查 cumsum 等于 0 的位置以创建一个布尔系列。

  • 反转,使用 [::-1] 以负一步切片,该系列并再次使用 cumsum 来标记 cumsum == 0 之前的所有记录。

  • 使用 [::-1] 恢复到原始顺序并转换为布尔值。

  • 接下来,使用该 bolean 系列的“真值”来设置“前两个列和偏移量”,然后使用假记录来设置 np.nan。

f1,除了捕获偏移量关闭记录的额外逻辑外,其他功能相同。


回答

1.定义一个函数 _thresh_sum

我们可以定义一个递归函数,它将输入参数作为一维 numpy 数组,并返回与输入数组具有相同形状的一维布尔数组

from numba import jit

@jit(nopython=True)
def _thresh_sum(arr, indices, flags, offset, thresh):
    if flags[indices].any(): return

    s = np.abs(arr[indices].sum())
    if s <= thresh and len(indices) > 1:
        flags[indices] = True
        return

    for i, _ in enumerate(arr[offset:]):
        new_offset = offset + i + 1
        new_indices = np.array(list(indices) + [offset + i])
        _thresh_sum(arr, new_indices, flags, new_offset, thresh)


def thresh_sum(arr, thresh=0):
    flags = np.full(len(arr), False)
    _thresh_sum(np.array(arr), 
                np.array([], dtype='int'), flags, 0, thresh + 1e-6)
    return flags

背后的直觉 _thresh_sum

  • 本质上,我们正在对输入数组执行深度优先搜索以检查所有可能的路径,其中给定路径中的顶点是数组中的元素
  • 如果对于给定路径,路径中所有数字的总和小于或等于给定阈值,则我们停止在当前路径上进一步搜索并探索具有不同起始节点的其他路径。
  • 如果找到路径,则我们标记路径中的所有数字/顶点

我们可以通过将函数直接编译为机器码,借助称为即时编译的技术来进一步提高代码的性能,使用_thresh_sumnumba

注意:这是NP-Hard类型问题。随着每个唯一组的元素数量增加,问题的计算复杂度将增加大约O(2^n)

2.Grouptransformthresh_sum

现在我们组数据帧按照给定的规则124transformE使用函数thresh_sum对应于每个规则

m1 = df.groupby(['A', 'B', 'C'])['E'].transform(thresh_sum) # Rule 1
m2 = df[~m1].groupby(['A', 'B'])['E'].transform(thresh_sum)  # Rule 2
m3 = df[~(m1 | m2)].groupby(['A', 'B', 'C'])['E'].transform(thresh_sum, thresh=0.5)  # Rule 4

3. np.select

根据计算出的布尔掩码m1m2m3对应规则1,24,填充列中的值F

df['F'] = np.select([m1, m2.reindex(m1.index, fill_value=False), m3.reindex(m1.index, fill_value=False)], 
                    ['first three-four col and offset', 'first two col and offset', 'first three-four col and small offset'], 'first or no match')

结果

       A    B    C    D     E                                      F
0   0000  ZZZ  987  QW1   8.0        first three-four col and offset
1   0000  ZZZ  987  QW1  -8.0        first three-four col and offset
2   0000  ZZZ  987  QW1  -8.0                      first or no match
3   1111  AAA  123  AB1   1.0        first three-four col and offset
4   1111  AAA  123  CD1  -1.0        first three-four col and offset
5   2222  BBB  456  EF1  -4.0        first three-four col and offset
6   2222  BBB  456  GH1  -1.0        first three-four col and offset
7   2222  BBB  456  IL1   5.0        first three-four col and offset
8   3333  CCC  789  MN1   2.0               first two col and offset
9   3333  CCC  101  MN1  -2.0               first two col and offset
10  4444  DDD  121  UYT   6.0               first two col and offset
11  4444  DDD  131  FB1  -5.0               first two col and offset
12  4444  DDD  141  UYT  -1.0               first two col and offset
13  5555  EEE  151  CB1   3.0               first two col and offset
14  5555  EEE  161  CR1  -3.0               first two col and offset
15  5555  EEE  161  CR1  -5.0                      first or no match
16  6666  FFF  111  CB1   4.0                      first or no match
17  7777  GGG  222  ZB1  10.5  first three-four col and small offset
18  7777  GGG  222  ZB1 -10.0  first three-four col and small offset


以上是Pandas-根据多种条件更新列-按方法分组的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>