通过pandas数据帧高效循环

时间:2017-04-26 11:29:33

标签: python performance pandas numpy vectorization

我有以下需要帮助的问题。 我在csv文件中有310条记录,其中包含有关错误的一些信息。 在另一个csv文件中,我有80万条记录包含有关行李的统计信息(可能导致错误的事件)。

使用下面的脚本,我正在尝试

  1. 遍历错误并选择一个。
  2. 循环统计记录并检查一些条件
  3. 如果匹配,请将错误记录中的列添加到统计信息中 记录。
  4. 保存新文件
  5. 我的问题是,如果我能够以更有效的方式使用numpy或其他任何东西来实现这一点。 由于统计信息的大小,当前的方法将永远运行

    任何有关正确方向的帮助或提示都将受到赞赏。 thanx in adavance

    dataset = pd.read_csv('310_records.csv')
    dataset1 = pd.read_csv('800K_records.csv')
    cols_error = dataset.iloc[:, [0, 1, 2, 3, 4, 5, 6]]
    cols_stats = dataset1.iloc[:, [1, 2, 3, 4, 5, 6, 7, 8, 9]]
    cols_stats['Fault'] = ''
    cols_stats['Created'] = ''
    
    for i, error in cols_error.iterrows():    
        fault_created = error [0]
        fault_ucs = error [1]
        fault_dn = error [2]
        fault_epoch_end = error [3]
        fault_epoch_begin = error [4]
        fault_code = error [6]    
    
        for index, stats in cols_stats.iterrows():
            stats_epoch = stats[0]
            stats_ucs = stats[5]        
            stats_dn = stats[7]
            print("error:", i, " Stats:", index)        
    
            if(stats_epoch >= fault_epoch_begin and stats_epoch <= fault_epoch_end):
                if(stats_dn == fault_dn):
                    if(stats_ucs == fault_ucs):
                        cols_stats.iloc[index, 9] = fault_code
                        cols_stats.iloc[index, 10] = fault_created
    
            else:
                cols_stats.iloc[index, 9] = 0
                cols_stats.iloc[index, 10] = fault_created
    
    cols_stats.to_csv('datasets/dim_stats_error.csv', sep=',', encoding='utf-8')
    

1 个答案:

答案 0 :(得分:1)

首先:您确定您的代码符合您的要求吗?在我看来,你不断循环你的统计数据,所以如果你找到了错误#1的匹配错误,你可以稍后用bug#310覆盖统计数据的相应附录。目前还不清楚您应该对没有匹配错误事件的统计信息事件做些什么,但目前您在某种程度上随意存储这些数据点的fault_created列。更不用说每次为每个错误检查每个事件所做的额外工作。

缓慢的原因是你要注意充分利用熊猫的力量。在numpy和pandas中,性能部分来自内存管理,其余来自矢量化。通过将大部分工作从本机python循环推送到矢量化函数(运行编译代码),您开始看到巨大的速度改进。

我不确定是否有一种先进的方法可以对你工作的所有进行矢量化,但由于你正在查看310 vs 800k项目,这似乎是完全合理的保持循环超过您的错误并矢量化内循环。关键是逻辑索引,使用它可以一次处理所有800k项目:

for i, error in cols_error.iterrows():
    created, ucs, dn, epoch_end, epoch_begin, _, code = error

    inds = ( (epoch_begin <= cols_stats['epoch']) &
             (cols_stats['epoch'] <= epoch_end) &
             (cols_stats['dn'] == dn) &
             (cols_stats['ucs'] == ucs) )
    cols_stats['Fault'][inds] = code
    cols_stats['Created'][inds] = created

cols_stats.to_csv('datasets/dim_stats_error.csv', sep=',', encoding='utf-8')

请注意,上述内容不会将不匹配的列设置为非常重要的内容,因为我不认为您的问题中有合理的示例。无论您想要设置什么默认值都应该独立于错误列表,因此您应该在整个匹配测试之前设置这些值。

请注意,我为您的代码进行了一些整容。您可以使用解包分配来设置error中的所有值,并删除这些变量的前缀会使其更清晰。我们可以处理前缀,因为我们没有为统计数据帧定义单独的变量。

如您所见,您可以根据向量化逻辑索引操作来定义查找给定错误的所有匹配统计项的条件。生成的名为Series的panda inds对于统计数据框的每一行都有一个bool。这可用于分配名为'Fault''Created'的列的子集。请注意,您可以(并且可能应该)按名称对列进行索引,至少我发现很多更清晰方便。

因为对于每个错误,您的codecreated都是标量(可能是字符串),所以向量化作业cols_stats['Fault'][inds] = codecols_stats['Created'][inds] = created设置cols_stats的每个索引项这些标量。

我认为结果应该和以前一样,但速度要快得多,代价是增加内存使用量。

可以在初始化时进一步简化,但如果没有MCVE,很难说明具体细节。至少你可以使用切片表示法:

cols_error = dataset.iloc[:, :7]
cols_stats = dataset1.iloc[:, 1:10]

但是你很可能只忽略了几个专栏,在这种情况下,drop代替它们可能更清楚。例如,如果在dataset中您有一个名为“垃圾邮件”的第七列。你忽略了,你可以设置

cols_error = dataset.drop('junk', axis=1)