【问题标题】:Map function taking too much time (Pandas DataFrame)地图功能花费太多时间(Pandas DataFrame)
【发布时间】:2020-02-11 23:41:29
【问题描述】:

我有一个具有以下形状的 pandas 数据框:12.000.000 x 2(行 x 列)我需要应用一个映射函数,但是,它需要花费很多时间来比较第 1 列的每个日期到给定的日期,例如今天。

DataFrame 示例

╔════════════╦══════════╗
║    Col1    ║   Col2   ║
╠════════════╬══════════╣
║ 2019-03-19 ║        1 ║
║ 2019-03-20 ║        2 ║
║ 2019-05-15 ║        3 ║
║ 2019-07-15 ║        4 ║
║ ...        ║          ║
║ 2019-10-20 ║ 12000000 ║
╚════════════╩══════════╝

代码示例

import pandas as pd
from datetime import datetime

df = pd.read_csv('path_of_file.csv')
today = datetime.now()
df['output'] = df['Col1'].apply(lambda x: 1 if x > today else 0) 

我错过了什么吗?可以改进吗?谢谢!

【问题讨论】:

  • 我不是专家,但此链接可能会有所帮助:towardsdatascience.com/…
  • 它可能正在将整个文件加载到占用时间的内存中。如果您可以一次读取一行来创建二进制数组,那么您不必将整个输入文件包含在内存中。加载与从加载的数据创建输出数组需要多长时间?
  • Col1 的 dtype 是什么 - df.dtypes
  • df['output'] = 1 * df['Col1'] > today 怎么样??
  • numpy where 函数可以提供帮助:df['output'] = np.where(df.Col1>today,1,0)

标签: python pandas dataframe dictionary


【解决方案1】:

编辑 - 查看二战解决方案

二战解决方案显然是 OP 和我的赢家。

他的解决方案运行速度比我自己的快 2 倍:

df['output'] = 1 * (df['Col1'] > today)

这也是一个非常简洁的方法,因为您所做的只是将 1 与 1 或 0 相乘,从而得出将日期列与今天的日期进行比较的真值。


这是一个非常有趣的问题,所以我进行了一些测试。

我创建了一个包含 100 万行日期的空数据框。

starting_date = datetime(200, 1, 1, 00, 00)
end_date = datetime(3000,1, 1, 00, 00)
index = 1

date_values = []

def daterange(start_date, end_date):
    for n in range(int ((end_date - start_date).days)):
        yield start_date + timedelta(n)

date_values = [_date for _date in daterange(starting_date, end_date)]

date_col = {'Col1': date_values}
df = pd.DataFrame(date_col)

我们将进入未来的男孩。

现在,我运行的两个测试比较了 OP 提供的解决方案的功能运行时间,以及我在下面发布的解决方案。

我们假设日期是有序的

测试 1 - OP 的解决方案

start_time = time.time()

df['output'] = df['Col1'].apply(lambda x: 1 if x > today else 0) 

print("--- %s seconds ---" % (time.time() - start_time))

测试 2 - 我的解决方案

start_time = time.time()

df['output'] = 1

df.loc[df['Col1'] < today, 'output'] = 0

print("--- %s seconds ---" % (time.time() - start_time))

结果

每个函数运行 10 次后,第二个解决方案每次都获胜。为什么?老实说,我不知道。

我认为我们可以很好地猜测,在根据条件为列分配常量值时,pandas 并没有执行线性搜索,如第二个解决方案所示。

Soltuion 1
--- 0.36346006393432617 seconds ---
Solution 2
--- 0.13942289352416992 seconds ---
Soltuion 1
--- 0.4605379104614258 seconds ---
Solution 2
--- 0.12388873100280762 seconds ---
Soltuion 1
--- 0.34688305854797363 seconds ---
Solution 2
--- 0.0912778377532959 seconds ---
Soltuion 1
--- 0.2879600524902344 seconds ---
Solution 2
--- 0.08435988426208496 seconds ---
Soltuion 1
--- 0.3161609172821045 seconds ---
Solution 2
--- 0.0965569019317627 seconds ---
Soltuion 1
--- 0.31951212882995605 seconds ---
Solution 2
--- 0.08857107162475586 seconds ---
Soltuion 1
--- 0.2996959686279297 seconds ---
Solution 2
--- 0.16647815704345703 seconds ---
Soltuion 1
--- 0.5074219703674316 seconds ---
Solution 2
--- 0.13281011581420898 seconds ---
Soltuion 1
--- 0.3716299533843994 seconds ---
Solution 2
--- 0.0970299243927002 seconds ---
Soltuion 1
--- 0.29851794242858887 seconds ---
Solution 2
--- 0.08089780807495117 seconds ---

需要考虑的事项 - 两个测试中的日期都是有序的。如果您以完整、随机的顺序收到它们会怎样?

我们首先随机化数据集:

df = df.sample(frac=1)

然后运行完全相同的测试。

Soltuion 1
--- 0.6548967361450195 seconds ---
Solution 2
--- 0.22769808769226074 seconds ---
Soltuion 1
--- 0.7096188068389893 seconds ---
Solution 2
--- 0.28220510482788086 seconds ---
Soltuion 1
--- 0.7588798999786377 seconds ---
Solution 2
--- 0.25870585441589355 seconds ---
Soltuion 1
--- 0.6285257339477539 seconds ---
Solution 2
--- 0.3373727798461914 seconds ---
Soltuion 1
--- 0.7623891830444336 seconds ---
Solution 2
--- 0.18880391120910645 seconds ---
Soltuion 1
--- 0.5125689506530762 seconds ---
Solution 2
--- 0.23384499549865723 seconds ---
Soltuion 1
--- 0.6188468933105469 seconds ---
Solution 2
--- 0.25000977516174316 seconds ---
Soltuion 1
--- 0.6692302227020264 seconds ---
Solution 2
--- 0.5207180976867676 seconds ---
Soltuion 1
--- 1.2534172534942627 seconds ---
Solution 2
--- 0.2665679454803467 seconds ---
Soltuion 1
--- 0.6374101638793945 seconds ---
Solution 2
--- 0.2108619213104248 seconds ---

解决办法

由于您所做的只是检查日期是否小于今天的日期,因此创建一个新列并添加一个常数 1 或 0。

让我们首先将常量添加到列中。

df['Output'] = 1

现在,我们要做的就是找到日期小于当前日期的点。

首先,我们应该将 Col1 的日期类型更改为日期时间,以确保我们可以进行正确的比较。

df['Col1'] = pd.to_datetime(df['Col1'], format="%Y-%M-%d)

然后,我们查看每个小于今天的日期,并将输出更改为 0。

df.loc[df['Col1'] < today.date(), 'Output'] = 0

【讨论】:

  • 排序是 O(N log N) 而指定的任务只有 O(N)
  • 好消息@CraigHicks。我在运行一些测试后编辑了我的答案。
  • @ALollz 您介意提供您的解决方案吗?因为 OP 正在将数组与标量值进行比较。这就是 apply 正在做的事情。
  • 如果您可以对不同的表大小进行比较(每次增加 2 倍),我们可以看到时间是否呈线性增长,或者 OP 编码是否导致高于线性增长,在这种情况下,底层实现可能存在设计缺陷。
  • 嗯.. 只花了几秒钟!!谢谢!
【解决方案2】:

虽然我们仍在等待有关该问题的更多信息,但我目前掌握的信息如下:

import pandas as pd


df = pd.DataFrame(
    data={
        "col_1": ["2019-03-19", "2019-03-20", "2030-01-01", "2019-05-15", "2019-07-15"],
        "col_2": [1, 2, 3, 4, 5],
    }
)

df["col_1"] = pd.to_datetime(df["col_1"], infer_datetime_format=True, utc=True)

print(df, end='\n\n')

curr_time = pd.Timestamp.utcnow()

print(curr_time, end='\n\n')

df["col_3"] = df["col_1"] > curr_time

print(df)

输出:

                      col_1  col_2
0 2019-03-19 00:00:00+00:00      1
1 2019-03-20 00:00:00+00:00      2
2 2030-01-01 00:00:00+00:00      3
3 2019-05-15 00:00:00+00:00      4
4 2019-07-15 00:00:00+00:00      5

2020-02-12 02:11:37.212849+00:00

                      col_1  col_2  col_3
0 2019-03-19 00:00:00+00:00      1  False
1 2019-03-20 00:00:00+00:00      2  False
2 2030-01-01 00:00:00+00:00      3   True
3 2019-05-15 00:00:00+00:00      4  False
4 2019-07-15 00:00:00+00:00      5  False

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    相关资源
    最近更新 更多