将条件功能应用于数十亿个数据点



我有一个看起来像这样的数据框架:

idx    a      b      c      d      e      f      g      h      i       j
1      0     17     17     83     17      0     21     16     21       4
2     -9     31     31     74     40      0     39     39     39       9
3    -27      0    -27     92     27    -37      3    -37     40      16
4     -4      0     -4     81      4     -1      5      5      6       9

我想申请:

where x>0: functionA(x)
where x<0: functionB(x)

我独立尝试的是:

df[df>0] = np.log(df)

df[df<0] = -np.log(-df)

哪种有效的作用。.顺序运行这两个操作将无法使用,因为数据框在第一个操作后从int转换为float,并且从log values,ex ex。毫无差异的原始值。是0还是log(1)= 0?

我也担心这些错误:

除以零

usr/local/anaconda3/envs/ds/lib/python3.6/site-packages/ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in log
  """Entry point for launching an IPython kernel.```

无效的值

/usr/local/anaconda3/envs/ds/lib/python3.6/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in log
      """Entry point for launching an IPython kernel.

由于没有NaN值,因此我明确选择了非零值。

df.isnull().values.any()
False

最后一个问题是如何在与数十亿行合作时如何有效地执行此操作。

您可以使用 numpy.piecewise函数:https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.piecewise.piecewise.html

import numpy as np
positive = df.values > 0
negative = df.values < 0
df[:] = np.piecewise(df.values, (positive, negative), (np.log, lambda x: -np.log(-x)))

可能有更好的方法,但现在是我所做的:

将我的专栏分为三种:

  1. (-Inf,0)
  2. (0,inf)
  3. (-INF,INF)

前两个很简单[1]:

for i in [a, ...]:
    s = df[i]
    df[i] = np.where(s<0, -np.log(-s), s).astype('float32')

类型2的类似代码。

类型3更棘手,较慢:

def apply_log(x):
    if x>0:
        return np.log(x)
    elif x<0:
        return -np.log(-x)
    elif x == 0:
        return 0.0
    else:
        assert False

然后将其矢量化[2]

veclog = np.vectorize(apply_log)

然后运行它: df['c'] = veclog(s.astype('float32')).astype('float32')

〜50m子集的运行时: 57.7 s ± 142 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

从[3]带有np.where,在条件之前应用FN,因此除以零误差。类型1和2将鸿沟丢为零警告/错误,第3型中没有错误。

来源:

[1] python:numpy/pandas在条件上更改值

[2]函数应用程序通过Numpy的矩阵行/列

[3] Runtime Warning:在日志中遇到的零零分隔

有人在之前添加了此答案,然后删除它:

df = np.log(df.where(df>0)).fillna(-1*np.log(-1*df.where(df<0))).fillna(0)

使用了大量内存,但似乎有效。我有一个怀疑,因为它按顺序运行OP,并且可能会使某些值运行。

update :这似乎与.0005中其他解决方案的答案匹配。如果原始海报会重新发布他们的答案!

最新更新