【问题标题】:Iterating and modifying dataframe using Pandas groupby使用 Pandas groupby 迭代和修改数据框
【发布时间】:2017-06-03 16:04:14
【问题描述】:

我正在处理大量 1 的数组,需要系统地从数组的各个部分中删除 0。大数组由许多较小的数组组成,对于每个较小的数组,我需要系统地将其上下三角形替换为 0。例如,我们有一个数组,其中包含由索引值指示的 5 个子数组(所有子数组的列数相同):

     0    1    2
0  1.0  1.0  1.0
1  1.0  1.0  1.0
1  1.0  1.0  1.0
2  1.0  1.0  1.0
2  1.0  1.0  1.0
2  1.0  1.0  1.0
3  1.0  1.0  1.0
3  1.0  1.0  1.0
3  1.0  1.0  1.0
3  1.0  1.0  1.0
4  1.0  1.0  1.0
4  1.0  1.0  1.0
4  1.0  1.0  1.0
4  1.0  1.0  1.0
4  1.0  1.0  1.0

我希望每组行在其上下三角形中进行修改,使得生成的矩阵为:

      0    1    2
0  1.0  1.0  1.0
1  1.0  1.0  0.0
1  0.0  1.0  1.0
2  1.0  0.0  0.0
2  0.0  1.0  0.0
2  0.0  0.0  1.0
3  1.0  0.0  0.0
3  1.0  1.0  0.0
3  0.0  1.0  1.0
3  0.0  0.0  1.0
4  1.0  0.0  0.0
4  1.0  1.0  0.0
4  1.0  1.0  1.0
4  0.0  1.0  1.0
4  0.0  0.0  1.0

目前我只使用 numpy 来实现这个结果数组,但我认为我可以使用 Pandas 分组来加速它。实际上,我的数据集非常大,将近 500,000 行。 numpy 代码如下:

import numpy as np

candidateLengths = np.array([1,2,3,4,5])
centroidLength =3

smallPaths = [min(l,centroidLength) for l in candidateLengths]

# This is the k_values of zeros to delete. To be used in np.tri
k_vals = list(map(lambda smallPath: centroidLength - (smallPath), smallPaths))
maskArray = np.ones((np.sum(candidateLengths), centroidLength))

startPos = 0
endPos = 0
for canNo, canLen in enumerate(candidateLengths):
    a = np.ones((canLen, centroidLength))
    a *= np.tri(*a.shape, dtype=np.bool, k=k_vals[canNo])
    b = np.fliplr(np.flipud(a))
    c = a*b

    endPos = startPos + canLen

    maskArray[startPos:endPos, :] = c

    startPos = endPos

print(maskArray)

当我在我的真实数据集上运行它时,它需要将近 5-7 秒来执行。我认为这是由于这个巨大的 for 循环。如何使用 pandas 分组来实现更高的速度?谢谢

【问题讨论】:

    标签: python arrays pandas numpy


    【解决方案1】:

    新答案

    def tris(n, m):
        if n < m:
            a = np.tri(m, n, dtype=int).T
        else:
            a = np.tri(n, m, dtype=int)
        return a * a[::-1, ::-1]
    
    idx = np.append(df.index.values, -1)
    w = np.append(-1, np.flatnonzero(idx[:-1] != idx[1:]))
    c = np.diff(w)
    df * np.vstack([tris(n, 3) for n in c])
    
         0    1    2
    0  1.0  1.0  1.0
    1  1.0  1.0  0.0
    1  0.0  1.0  1.0
    2  1.0  0.0  0.0
    2  0.0  1.0  0.0
    2  0.0  0.0  1.0
    3  1.0  0.0  0.0
    3  1.0  1.0  0.0
    3  0.0  1.0  1.0
    3  0.0  0.0  1.0
    4  1.0  0.0  0.0
    4  1.0  1.0  0.0
    4  1.0  1.0  1.0
    4  0.0  1.0  1.0
    4  0.0  0.0  1.0
    

    旧答案

    我定义了一些辅助三角函数

    def tris(n, m):
        if n < m:
            a = np.tri(m, n, dtype=int).T
        else:
            a = np.tri(n, m, dtype=int)
        return a * a[::-1, ::-1]
    
    def tris_df(df):
        n, m = df.shape
        return pd.DataFrame(tris(n, m), df.index, df.columns)
    

    然后

    df * df.groupby(level=0, group_keys=False).apply(tris_df)
    
         0    1    2
    0  1.0  1.0  1.0
    1  1.0  1.0  0.0
    1  0.0  1.0  1.0
    2  1.0  0.0  0.0
    2  0.0  1.0  0.0
    2  0.0  0.0  1.0
    3  1.0  0.0  0.0
    3  1.0  1.0  0.0
    3  0.0  1.0  1.0
    3  0.0  0.0  1.0
    4  1.0  0.0  0.0
    4  1.0  1.0  0.0
    4  1.0  1.0  1.0
    4  0.0  1.0  1.0
    4  0.0  0.0  1.0
    

    【讨论】:

    • 嗨@piRSquared,谢谢。我认为与我最初编写的 for 循环相比,您提供的解决方案很慢。我认为在后台应用很像一个 for 循环。如果您使用 CandidateLengths = np.random.randint(1,7, size=300000) 尝试它,我发现我的代码在 6 秒内执行。谢谢!
    • @user3063482 试试看。
    • 嗨,谢谢我计时了,你的新函数在 3.74 秒内返回,而我的在 5.34 秒内返回!这很好用。感谢您的帮助!
    • @user3063482 很高兴我能提供帮助。
    猜你喜欢
    • 2018-01-15
    • 2016-08-21
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-04
    • 2019-01-14
    相关资源
    最近更新 更多