【问题标题】:Faster nested for-loop, perhaps using pd.groupby更快的嵌套 for 循环,可能使用 pd.groupby
【发布时间】:2019-07-03 07:54:40
【问题描述】:

我有一个嵌套循环。但是我的数据集非常大,所以我需要一个更快的方法。我相信它可以通过以某种巧妙的方式对数据进行分组或映射来完成。 我有一个 X 名称列表。对于每个名称,我都有由颜色、品牌和价值组成的项目。 我需要对每个名称的颜色和品牌的每个组合的值求和。然后对这些和进行一些操作,首先在每个颜色内,然后在每个名称内,同时跟踪计算总和的组合。 嵌套的 for 循环变得非常慢,因为我的数据集很大。 有更快的方法吗?

import random
import pandas
list1 = ['Name 0']
list2 = ['Color 0']
list3 = ['Brand 0']
list4 = [random.randint(10,1000)]
nName = 25
nColor = 5
nBrand = 3
nSim = 1000
for i in range(1,nSim):
    list1.extend(['Name {}'.format(random.randint(0,nName))])
    list2.extend(['Color {}'.format(random.randint(0,nColor))])
    list3.extend(['Brand {}'.format(random.randint(0, nBrand))])
    list4.append(random.randint(10,1000))
d = list(zip(list1, list2, list3, list4))
df1 = pd.DataFrame(d, columns=['Name','Color','Brand','Value'])

end_values = np.zeros(nName)
for iName in range(0,nName):
    y = 0
    for iColor in range(0,nColor):
        x = np.zeros(nBrand)
        for iBrand in range(0,nBrand):
            x[iBrand] = np.nansum(df1[((df1['Name'] == 'Name {}'.format(iName)) & (df1['Color'] == 'Color {}'.format(iColor)) & (df1['Brand'] == 'Brand {}'.format(iBrand)))]['Value'])
        y = y + x[0] + 1.5 * x[1] + 3 * x[0] * x[2]
    end_values[iName] = y

【问题讨论】:

    标签: python for-loop nested pandas-groupby


    【解决方案1】:

    目前尚不清楚您的问题究竟是什么。因为您在问题中提供的代码中没有嵌套的 for 循环。您的意思是对NameColorBrand 值的所有组合的嵌套循环吗?如果是这样,您不需要编写任何循环。只需使用pandas 表示:

    df2 = df1.groupby(['Name', 'Color', 'Brand']).agg({'Value': 'sum'})
    

    是的,只需一行即可计算所有内容。就像 SQL 中的 group by。例如,num22df2.loc[('Name 0', 'Color 2', 'Brand 2'), 'Value']

    如果计算稍微复杂一点

    df3 = df1.groupby(['Name', 'Color', 'Brand']).agg({'Value': ['count', 'sum']})
    

    num22df3.loc[('Name 0', 'Color 2', 'Brand 2'), ('Value', 'sum')]

    .agg(...) 的参数是一个字典,您可以在其中指定要处理的列以及要获取的统计信息。

    试试这个方法,看看我是否误解了你的问题。

    编辑

    考虑以下代码:

    # 1. original code with nested loops
    # includes slight correction (+1 in range(...) and zeros(...) functions)
    def f_1(df1):
        end_values = np.zeros(nName + 1)
        for iName in range(nName + 1):
            y = 0
            for iColor in range(nColor + 1):
                x = np.zeros(nBrand + 1)
                for iBrand in range(nBrand + 1):
                    x[iBrand] = np.nansum(df1[((df1['Name'] == 'Name {}'.format(iName)) & (df1['Color'] == 'Color {}'.format(iColor)) & (df1['Brand'] == 'Brand {}'.format(iBrand)))]['Value'])
                y = y + x[0] + 1.5 * x[1] + 3 * x[0] * x[2]
            end_values[iName] = y
        return end_values
    
    # 3. new code - supportive function to calculate result for brands
    def f_2_suppport(srs):
        cnt = {k: v for k, v in zip(srs.index.get_level_values(2), srs)}
        a = cnt.get('Brand 0', 0)
        b = cnt.get('Brand 1', 0)
        c = cnt.get('Brand 2', 0)
        return a + 1.5 * b + 3 * a * c
    
    # 2. new code - main function with pandas groupby/agg
    def f_2(df1):
    
        return (
            df1
            .groupby(['Name', 'Color', 'Brand'])
            .agg({'Value': 'sum'})
            .groupby(['Name', 'Color'])
            .agg({'Value': lambda x: f_2_suppport(x)})
            .groupby('Name')
            .agg({'Value': 'sum'}))
    
    
    if __name__ == '__main__':
    
        print(f_1(df1))
        print(f_2(df1))
    

    输出:

    [28735057.  26111382.  28304153.5 15495841.  15915862.5 13884354.5
     21172873.  27342470.5 29403143.  38212958.5 37504605. ]
                  Value
    Name               
    Name 0   28735057.0
    Name 1   26111382.0
    Name 10  37504605.0
    Name 2   28304153.5
    Name 3   15495841.0
    Name 4   15915862.5
    Name 5   13884354.5
    Name 6   21172873.0
    Name 7   27342470.5
    Name 8   29403143.0
    Name 9   38212958.5
    

    在我的机器上运行时间:

    %timeit f_1(df1)
    1.07 s ± 736 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit f_2(df1)
    35.2 ms ± 163 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    所以groupby + agg 使计算快了大约 30 倍。

    【讨论】:

    • 我没有在代码中写循环,但逻辑如下: 对于每个名称:(对于每个颜色:(对于每个品牌:对这三个数字求和并进行操作)将内循环的返回相加,对这五个数字进行运算)
    • 非常感谢!代码似乎完成了这项工作。而且我节省了很多时间。我也有 nName = 1500,所以我花了大约 20 分钟。现在几秒钟​​内就完成了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 2019-04-15
    • 2022-01-26
    • 1970-01-01
    • 2019-06-27
    相关资源
    最近更新 更多