【问题标题】:How to speed up this task in Python如何在 Python 中加速这项任务
【发布时间】:2020-04-27 11:34:12
【问题描述】:

我有一个大的 Pandas 数据框,24'000'000 行 × 6 列加上索引。 我需要读取第 1 列中的整数(即 = 1 或 2),然后如果第 1 列 = 1,则强制第 3 列中的值为负数,如果 = 2,则强制为正数。我在 Jupyter 笔记本中使用以下代码:

for i in range(1000):
    if df.iloc[i,1] == 1:
        df.iloc[i,3] = abs(df.iloc[i,3])*(-1)
    if df.iloc[i,1] == 2:
        df.iloc[i,3] = abs(df.iloc[i,3])

上面的代码只需要 2 分 30 秒才能运行 1000 行。对于 2400 万行,需要 41 天才能完成!

有些不对劲。该代码在相当高端的 PC 上的 Jupyter Notebook/Chrome/Windows 中运行。

Pandas 数据框是使用 pd.read_csv 创建的,然后以这种方式排序和索引:

df.sort_values(by = "My_time_stamp", ascending=True,inplace = True)
df = df.reset_index(drop=True)

数据框的创建和排序只需几秒钟。我还有其他计算要在这个数据帧上执行,所以我显然需要了解我做错了什么。

【问题讨论】:

  • 您所做的正是许多熊猫人建议您不要做的事情。不要使用 for 循环来做数学运算。
  • 阅读 Pandas 文档。

标签: python pandas for-loop


【解决方案1】:

np.where

a = np.where(df.iloc[:, 1].to_numpy() == 1, -1, 1)
b = np.abs(df.iloc[:, 3].to_numpy())
df.iloc[:, 3] = a * b

【讨论】:

  • 当我使用 DataFrame 尝试此操作时,我收到错误 'Series' object has no attribute 'to_numpy'。您愿意用数据示例展示您的代码吗?
  • 您使用的是旧版本的 pandas。而是用户 df.iloc[:, 1].valuesdf.iloc[:, 3].values
  • 也有时间测试这个提案,它有效,实际上是最快的,运行时间大约为 0.51 秒。谢谢/编辑:它是最快的,但 df.iloc[:, 3] 总是来出负。有点不对劲。
  • 好的,我发现了问题,你在 where 函数中的条件总是返回 True,所以 -1。我改为: a = np.where(df.iloc[:, 1].to_numpy()==1, -1, 1) 并且它有效,并且是最快的。
【解决方案2】:

矢量化它:

df.iloc[:, 3] = df.iloc[:, 3].abs() * (2 * (df.iloc[:, 1] != 1) - 1)

解释:

被视为 int,布尔系列 df.iloc[:, 1] != 1 被转换为 1 和 0。乘以 2,得到 2 和 0。减一后,第一列为 1 时为 -1,否则为 1。最后乘以第三列的绝对值,强制符号。

与 for 循环相比,向量化通常提供一个数量级或两个数量级的加速。

【讨论】:

  • 它实际上运行速度较慢。 100 条记录需要 1 分钟,所以我假设 1'000 条记录需要 10 分钟。所以可以肯定的是,我已经用你的单行替换了我的 2 个 IF 块(上面的 4 行)。
  • @Hugues这个方法不用循环for,你的5行代码可以替换
  • 对不起,我的错,我现在看到了,确实,仅 Marat 的线就在大约一秒钟内通过了整个 24M 行!谈论改进,我又爱上了我的电脑。我需要研究矢量化以更好地理解它,因为其余部分我真的需要它。
  • 这个提议有效,是第二快的 0.57 秒
【解决方案3】:

使用

df.iloc[:,3] = df.iloc[:,3].abs().mul( df.iloc[:,-1].map({2:1,1:-1}) )

【讨论】:

  • returns: AttributeError: 'DataFrame' object has no attribute 'map' 我研究了一下,发现了这一点,但对我来说不是很清楚:stackoverflow.com/questions/39535447/…
  • 我喜欢,.map() 没有得到足够的爱。
  • 好的,我今晚有时间测试这个提议,如果你纠正错字,上面的代码行有效,它是 df.iloc[:,1],而不是 df.iloc[:,-1]。对于 24M 行,它的运行时间约为 0.88 秒,而 Marat 的建议在 0.58 秒左右运行得更快。目前并没有什么不同,但我的数据帧以每天 100 万行的速度增长。但是 .map 指令对于我的鸟脑来说更容易掌握。谢谢。
【解决方案4】:

另一种方法:

import pandas as pd

以数据集为例:

df = pd.DataFrame({'x1':[1,2,1,2], 'x2':[4,8,1,2]})

新建列,编码值为-1和+1:

df['nx1'] = df['x1'].replace({1:-1, 2:1})

按列相乘:

df['nx1'] * df['x2']

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-31
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 2021-04-13
    • 2020-11-01
    • 1970-01-01
    相关资源
    最近更新 更多