【问题标题】:Quickly convert Pandas Series of labels into Series of indirect values from corresponding columns快速将 Pandas 系列标签转换为相应列的间接值系列
【发布时间】:2019-10-12 16:12:55
【问题描述】:

我有以下示例数据框:

N = np.arange(1, 10)
df = pd.DataFrame({
    'ref': [ 'a',  'b',  'c',  'd',  'c',  'b',  'a',  'b',  'c'],
    'a':   [   1,    2,    3,    4,    5,    6,    7,    8,    9],
    'b':   [  10,   20,   30,   40,   50,   60,   70,   80,   90],
    'c':   [ 100,  200,  300,  400,  500,  600,  700,  800,  900],
    'd':   [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000],
})

我想以某种方式“取消引用”ref 列,以获得此:

    'ref': [ 'a',  'b',  'c',  'd',  'c',  'b',  'a',  'b',  'c'],
    'ind': [   1,   20,  300, 4000,  500,   60,    7,   80,  900],

所以ind 中的每个值都应该对应于同一位置从ref 标记的列中的值。

天真的方法是使用类似df[df['ref']] 的东西,然后乘以单位矩阵,然后按列求和。但是因为我有相当大的(~8 GB) 数据框,所以我想这样做几乎会成正比。而且感觉不对。

另外,由于只是迭代它的大小非常缓慢。而且我无法使用 Cython 进行迭代,因为将此数据帧转换为 numpy 数组会丢失标签信息,我需要正确找到该列。

有什么建议吗?..

【问题讨论】:

  • 这是简单的查找:df['ind'] = df.lookup(df.index, df['ref'])
  • 是的 DataFrame.lookup 是一个替代方案,但它的 Python 实现效率低下。
  • df['ind'] = df.apply(lambda x: x[x['ref']], axis=1)
  • 是的,@ayhan 基本上是[df.get_value(row, col) for row, col in zip(row_labels, col_labels)],见source code

标签: python pandas dataframe


【解决方案1】:

您可以使用DataFrame.mask 或 numpy 来实现,如下所示,numpy 在此数据集中的性能稍好一些

N = np.arange(1, 10)
df_b = pd.DataFrame({
    'ref': [ 'a',  'b',  'c',  'd',  'c',  'b',  'a',  'b',  'c'],
    'a':   [   1,    2,    3,    4,    5,    6,    7,    8,    9],
    'b':   [  10,   20,   30,   40,   50,   60,   70,   80,   90],
    'c':   [ 100,  200,  300,  400,  500,  600,  700,  800,  900],
    'd':   [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000],
})

df_b

在哪里使用 Pandas

%%timeit
df = df_b.copy()
cols = df.columns[1:]
df["ind"] = df["ref"]

for col in cols:
    df.ind.mask(df.ind==col, df[col], inplace=True)
df
## 6.73 ms ± 129 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

使用 Numpy 的 Where

%%timeit
df = df_b.copy()
arr = df.ref.values

cols = df.columns[1:]
for col in cols:
    arr2 = df[col].values
    arr = np.where(arr==col, arr2, arr)

df["ind"] = arr
df

## 1.21 ms ± 73 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

结果

    ref a   b   c   d   ind
0   a   1   10  100 1000    1
1   b   2   20  200 2000    20
2   c   3   30  300 3000    300
3   d   4   40  400 4000    4000
4   c   5   50  500 5000    500
5   b   6   60  600 6000    60
6   a   7   70  700 7000    7
7   b   8   80  800 8000    80
8   c   9   90  900 9000    900

【讨论】:

  • 我已修改您的 .mask 解决方案以直接使用 df['ref']==col,因为标签和项目之间存在价值冲突的可能性很大。它是性能最高的解决方案(每 100'000'000 个项目需要 8.86 秒),而不是 .apply(约 2000 秒)。谢谢!
  • 好的,您已经使用 numpy 解决方案编辑了您的答案,根据您的基准测试速度更快。我也会检查它。再次感谢您!
  • 已检查,np.where 是每循环 3.64 秒(100kk 个项目)。
【解决方案2】:

您可以使用 numpy 索引:

lookup = dict(zip(df.columns, range(len(df.columns))))
result = pd.DataFrame({ 'ref' : df.ref, 'ind': df.values[np.arange(len(df)), df.ref.map(lookup)] })

print(result)

输出

  ref   ind
0   a     1
1   b    20
2   c   300
3   d  4000
4   c   500
5   b    60
6   a     7
7   b    80
8   c   900

【讨论】:

    【解决方案3】:

    使用 pandas.lookup()

    df['ind'] = df.lookup(df.index, df['ref'])
    
      ref  a   b    c     d   ind
    0   a  1  10  100  1000     1
    1   b  2  20  200  2000    20
    2   c  3  30  300  3000   300
    3   d  4  40  400  4000  4000
    4   c  5  50  500  5000   500
    5   b  6  60  600  6000    60
    6   a  7  70  700  7000     7
    7   b  8  80  800  8000    80
    8   c  9  90  900  9000   900
    

    【讨论】:

      猜你喜欢
      • 2020-04-23
      • 2018-07-25
      • 2019-04-21
      • 2019-11-28
      • 2019-03-08
      • 1970-01-01
      • 2022-01-24
      • 2023-03-23
      • 1970-01-01
      相关资源
      最近更新 更多