【问题标题】:Apply a conditional function to billions of data points将条件函数应用于数十亿个数据点
【发布时间】:2018-02-18 11:17:53
【问题描述】:

我有一个如下所示的数据框:

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&gt;0] = np.log(df)

df[df&lt;0] = -np.log(-df)

似乎有点工作..顺序运行这两个操作将不起作用,因为数据帧在第一次操作后从 int 转换为 float 并呈现原始值与 log 值不可区分,例如。是 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

最后一个问题是在我处理数十亿行时如何有效地做到这一点。

【问题讨论】:

  • 数字转换为浮点数有什么问题?
  • 数字可能接近于零,这会影响浮点数学。最好计算 abs(x-0)
  • 我没有转换为浮动部分。我知道日志可以计算出非常小的值,但是既然你抱怨得到float,那么做log 的目的是什么?在加载/创建时,您始终可以将整个数据框转换为 float32
  • 没有关于浮动的抱怨。问题是关于在同一个数据帧上应用这两个操作。一次操作后,df 被突变为具有 log 值的 float32。那么它不再可能区分原始值与日志值,即值 0 还是 log(1) = 0?我已经从标题中删除了该部分,因为它不必要且令人困惑。

标签: python pandas numpy


【解决方案1】:

您可以使用numpy.piecewise函数:https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.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)))

【讨论】:

  • 似乎它应该可以工作,但我收到了这个错误:ValueError: cannot copy sequence with size 54572193 to array axis with dimension 10
  • negative = df.values &lt; 0 (logical_not of > is positive = df.values > 0 negative = df.values < 0 df = np.piecewise(df.values, [positive, negative], [lambda x: np.log(x), lambda x: -np.log(-x)])
  • @encore2097 很抱歉将 0 包含在负数中。我在最新的编辑中修复了这个问题。我还添加了如何将新的 numpy 数组的值分配回您的 dataframe
【解决方案2】:

之前有人添加了这个答案,然后又删除了:

df = np.log(df.where(df&gt;0)).fillna(-1*np.log(-1*df.where(df&lt;0))).fillna(0)

它使用了一大块内存,但似乎可以工作。 我怀疑它已被删除,因为它按顺序运行操作并可能破坏一些值。

更新:这似乎与 .0005 内其他解决方案的答案相匹配。希望原始发帖人能重新发布他们的答案!

【讨论】:

    【解决方案3】:

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

    将我的专栏分为三种类型:

    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 的类似代码。

    Type 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 change values on condition

    [2]Function application over numpy's matrix row/column

    [3]RuntimeWarning: divide by zero encountered in log

    【讨论】:

      猜你喜欢
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 2015-12-04
      • 2011-11-17
      • 1970-01-01
      • 1970-01-01
      • 2010-11-15
      • 1970-01-01
      相关资源
      最近更新 更多