【问题标题】:Improve efficiency of selecting values from dataframe by index提高按索引从数据框中选择值的效率
【发布时间】:2021-08-28 11:56:40
【问题描述】:

我有一个模拟,它使用熊猫数据框来描述层次结构中的对象。为此,我使用了 MultiIndex 来显示到子对象的路径。

父 df

        par_val
a b            
0 0.0  0.366660
  1.0  0.613888
1 2.0  0.506531
  3.0  0.327356
2 4.0  0.684335
  0.0  0.013800
3 1.0  0.590058
  2.0  0.179399
4 3.0  0.790628
  4.0  0.310662

儿童 df

          child_val
a b   c           
0 0.0 0   0.528217
  1.0 0   0.515479
1 2.0 0   0.719221
  3.0 0   0.785008
2 4.0 0   0.249344
  0.0 0   0.455133
3 1.0 0   0.009394
  2.0 0   0.775960
4 3.0 0   0.639091
  4.0 0   0.150854
0 0.0 1   0.319277
  1.0 1   0.571580
1 2.0 1   0.029063
  3.0 1   0.498197
2 4.0 1   0.424188
  0.0 1   0.572045
3 1.0 1   0.246166
  2.0 1   0.888984
4 3.0 1   0.818633
  4.0 1   0.366697

这意味着子 Dataframe 中的对象 (0,0,0) 和 (0,0,1) 都由父 Dataframe 中的 (0,0) 处的值来表征。

当对“a”的某个主题的子数据帧执行函数时,它可能需要从“b”中获取一个值。我当前的解决方案通过 solution 函数中的索引从父 Dataframe 中定位值:

import pandas as pd
import numpy as np
import time
from matplotlib import pyplot as plt

r = range(10, 1000, 10)
dt = []
for i in r:
    start = time.time()

    df_par = pd.DataFrame(
        {'a': np.repeat(np.arange(5), i/5),
        'b': np.append(np.arange(i/2), np.arange(i/2)),
        'par_val': np.random.rand(i)
        }).set_index(['a','b'])

    df_child = pd.concat([df_par[[]]] * 2, keys = [0, 1], names = ['c'])\
        .reorder_levels(['a', 'b', 'c'])
    df_child['child_val'] = np.random.rand(i * 2)
    df_child['solution'] = np.nan

    def solution(row, df_par, var):
        data_level = len(df_par.index.names)
        index_filt = tuple([row.name[i] for i in range(data_level)])
        sol = df_par.loc[index_filt, 'par_val'] / row.child_val
        return sol



    a_mask = df_child.index.get_level_values('a') == 0

    df_child.loc[a_mask, 'solution'] = df_child.loc[a_mask].apply(solution,
                                                                  df_par = df_par,
                                                                  var = 10,
                                                                  axis = 1)
    stop = time.time()
    dt.append(stop - start)

plt.plot(r, dt)
plt.show()

对于模拟中的大量迭代,求解函数变得非常昂贵:

(迭代次数 (x) 与以秒为单位的时间 (y))

有没有更有效的计算方法?我曾考虑在子 df 中包含“par_val”,但我试图避免这种情况,因为大量重复会减少我可以放入 RAM 中的模拟量。

【问题讨论】:

    标签: python pandas dataframe performance


    【解决方案1】:

    par_val 是一个 float64,每个值占用 8 个字节。如果子数据帧有 100 万行,那就是 8MB 内存(在操作系统的内存压缩功能启动之前)。如果它有 10 亿行,那么是的,我会担心内存影响。

    更大的性能瓶颈在您的df_child.loc[a_mask].apply(..., axis=1) 行中。这使得 pandas 使用慢速 Python 循环而不是更快的矢量化代码。在 SQL 中,我们将循环方法称为 row-by-agonizing-row,它是一种反模式。出于这个原因,您通常希望避免使用.apply(..., axis=1)

    这是在不更改df_pardf_child 的情况下提高性能的一种方法:

    a_mask = df_child.index.get_level_values('a') == 0
    
    child_val = df_child.loc[a_mask, 'child_val'].droplevel(-1)
    solution = df_par.loc[child_val.index, 'par_val'] / child_val
    df_child.loc[a_mask, 'solution'] = solution.to_numpy()
    

    之前:

    之后:

    【讨论】:

      猜你喜欢
      • 2019-07-09
      • 1970-01-01
      • 2019-01-10
      • 1970-01-01
      • 2016-11-09
      • 2021-10-10
      • 2023-03-03
      • 1970-01-01
      • 2017-08-27
      相关资源
      最近更新 更多