根据移动日期范围(高效)填充pandas列

时间:2018-01-10 21:58:11

标签: python-3.x performance

我有2个pandas个数据框,其中一个包含测量日期,另一个包含带有事件ID的日期。

DF1

from datetime import datetime as dt
from datetime import timedelta
import pandas as pd
import numpy as np

today = dt.now()
ndays = 10

df1 = pd.DataFrame({'Date': [today + timedelta(days = x) for x in range(ndays)], 'measurement': pd.Series(np.random.randint(1, high = 10, size = ndays))})
df1.Date = df1.Date.dt.date


   Date    measurement
2018-01-10     8
2018-01-11     2
2018-01-12     7
2018-01-13     3
2018-01-14     1
2018-01-15     1
2018-01-16     6
2018-01-17     9
2018-01-18     8
2018-01-19     4

DF2

    df2 = pd.DataFrame({'Date': ['2018-01-11', '2018-01-14', '2018-01-16', '2018-01-19'], 'letter': ['event_a', 'event_b', 'event_c', 'event_d']})
df2.Date = pd.to_datetime(df2.Date, format = '%Y-%m-%d')
df2.Date = df2.Date.dt.date

   Date     event_id
2018-01-11  event_a
2018-01-14  event_b
2018-01-16  event_c
2018-01-19  event_d

我在df1 event_id df2中提供日期,只有在两个事件日期之间。结果数据框看起来像:

DF3

today = dt.now()
ndays = 10

df3 = pd.DataFrame({'Date': [today + timedelta(days = x) for x in range(ndays)], 'measurement': pd.Series(np.random.randint(1, high = 10, size = ndays)), 'event_id': ['event_a', 'event_a', 'event_b', 'event_b', 'event_b', 'event_c', 'event_c', 'event_d', 'event_d', 'event_d']})
df3.Date = df3.Date.dt.date



  Date      event_id  measurement
2018-01-10  event_a       4
2018-01-11  event_a       2
2018-01-12  event_b       1
2018-01-13  event_b       5
2018-01-14  event_b       5
2018-01-15  event_c       4
2018-01-16  event_c       6
2018-01-17  event_d       6
2018-01-18  event_d       9
2018-01-19  event_d       6

我用来实现这个目的的代码是:

n = 1
while n <= len(list(df2.Date)) - 1 :
    for date in list(df1.Date):
        if date <= df2.iloc[n].Date and (date > df2.iloc[n-1].Date):
            df1.loc[df1.Date == date, 'event_id'] = df2.iloc[n].event_id

    n += 1

我正在使用的数据集远远大于此(几百万行),并且此方法运行时间太长。有没有更有效的方法来实现这一目标?

2 个答案:

答案 0 :(得分:0)

因此,提高性能有很多方面。 我的第一个问题是:它必须是一个大熊猫框架开始吗?意思是df1和df2只是元组列表或列表列表?

事情是,在访问项目时,pandas会增加很多开销,尤其是在单独设置值时。 Pandas在矢量化操作方面表现出色,但我现在还没有看到有效的替代方案(也许有人想出这样的答案,这将是理想的)。

现在我要做的是:

  1. 将您的df1和df2转换为记录 - &gt;例如d1 = df1.to_records()你得到的是一组元组,基本上与数据帧具有相同的结构。
  2. 现在运行您的算法,但不是对pandas数据帧进行操作,而是对元组d1d2

  3. 的数组进行操作
  4. 使用第三个元组列表d3存储新创建的数据(每个元组是一行)

  5. 现在,如果您愿意,可以将d3转换回pandas数据帧:

    df3 = pd.DataFrame.from_records(d3, myKwArgs**)

    这将显着加快您的代码速度,我假设超过100-1000%。它确实增加了内存使用量,所以如果内存不足,请尽量避免将pandas数据帧全部放在一起,或者在使用它们创建记录时取消引用未使用的pandas帧df1, df2(如果遇到问题,请调用gc手动地)。

    编辑:这是使用上述程序的代码版本:

    d3 = []
    n = 1
    while n < range(len(d2)):
        for i in range(len(d1)):
            date = d1[i][0]
            if date <= d2[n][0] and date > d2[n-1][0]:
                d3.append( (date, d2[n][1], d1[i][1]) )
        n += 1
    

答案 1 :(得分:0)

您可以尝试使用df.apply()方法来实现此目的。请参阅pandas.DataFrame.apply。我认为我的代码比你的代码更快。

我的方法:

  • 合并两个数据帧df1和df2并按
  • 创建新的df3
df3 = pd.merge(df1, df2, on='Date', how='outer')
  • 按日期对df3进行排序,以便于travserse。
df3['Date'] = pd.to_datetime(df3.Date)
df3.sort_values(by='Date')
  • 创建set_event_date()方法以应用df3中的每一行。
new_event_id = np.nan 
def set_event_date(df3):
    global new_event_id
    if df3.event_id is not np.nan:
       new_event_id = df3.event_id
    return new_event_id
  • 将set_event_method()应​​用于df3中的每一行。
df3['new_event_id'] = df3.apply(set_event_date,axis=1)

最终输出将是:

Date Measurement New_event_id
0   2018-01-11  2   event_a
1   2018-01-12  1   event_a
2   2018-01-13  3   event_a
3   2018-01-14  6   event_b
4   2018-01-15  3   event_b
5   2018-01-16  5   event_c
6   2018-01-17  7   event_c
7   2018-01-18  9   event_c
8   2018-01-19  7   event_d
9   2018-01-20  4   event_d

让我知道一旦你尝试了我的解决方案,它的工作速度比你的快。 感谢。

相关问题