将日期从一个数据框映射到另一数据框-大数据的优雅而有效的方法

时间:2019-08-06 06:51:04

标签: python python-3.x pandas dataframe datetime

我有两个如下所示的数据框

t1 = pd.DataFrame({'person_id':[1,2,3],'observation_date':[np.nan,np.nan,np.nan],'observation_datetime':[np.nan,np.nan,np.nan]})

t2 = pd.DataFrame({'person_id':[1,2,3],'value_as_string':['5/28/2007','5/30/2007','6/4/2007']}).set_index('person_id')['value_as_string']

它们看起来如下图

enter image description here

这就是我试图获得输出的结果

t1['observation_date'] = t1['person_id'].map(t2)
t1['observation_date'] = pd.to_datetime(t1['observation_date'])
t1['observation_datetime'] = pd.to_datetime(t1['observation_date']).dt.strftime('%m/%d/%Y %H:%M:%S')

尽管可以正常工作,但在真实数据中却要花费大量时间

请注意,我正在尝试对大小为 100万个记录的t1个数据帧和大小为 15k 个记录的t2个数据帧进行此操作。因此,任何有效的方法都会有所帮助

我希望我的输出数据帧如下图所示

enter image description here

3 个答案:

答案 0 :(得分:2)

ids = list(range(1, 15000))
dte = ['5/28/2007','5/30/2007','6/4/2007'] * 5000
t1 = pd.DataFrame({'person_id': ids})
t2 = pd.DataFrame({'person_id': ids, 
                   'value_as_string': dte)

合并方法

x = t1.merge(t2, how='left', on='person_id', how='left')
# 5.19 ms ± 408 µs per loop

加入方法

x = t1.set_index('person_id').join(df2.set_index('person_id'), how='left') 
# 3.02 ms ± 91.4 µs per loop

使用字典的映射方法

t1['observation_date'] = t1['person_id'].map(
       t2.set_index('person_id')['value_as_string'].to_dict())
# 2.73 ms ± 240 µs per loop

没有字典的地图方法

t1['observation_date'] = t1['person_id'].map(t2.set_index('person_id')['value_as_string'])
# 2.33 ms ± 260 µs per loop

所以

t1['observation_date'] = pd.to_datetime(
        t1['person_id'].map(t2.set_index('person_id')['value_as_string']))
t1['observation_datetime'] = t1['observation_date'].dt.strftime('%m/%d/%Y %H:%M:%S')

答案 1 :(得分:1)

对于您的问题,我有一种解决方法。为什么不使用映射,而不是使用大熊猫中的合并等更快的方法?我已经使用了将近一百万的记录,而且速度惊人。

合并过程始于两个数据框。尝试做

df =  t1.merge(t2, on = 'person_id', how='inner')

这将对两个数据帧(t1和t2)中两列的person_id进行内部联接。您将在结果数据框中引入一个新列。然后,您可以使用简单的列操作将值填充到目标列中。

希望有帮助。

答案 2 :(得分:1)

转换为日期时间格式也要花费大量时间,您可以通过显式指定日期时间格式作为pd.to_datetime的参数来加快转换速度。对于您的情况,它最多可以提高10倍。

模拟您的情况。

import pandas as pd

t1 = pd.DataFrame({'person_id':[i for i in range(1000000)],'observation_date':[np.nan]*1000000,'observation_datetime':[np.nan]*1000000})
t2 = pd.DataFrame({'person_id':np.random.choice(1000000, replace=False, size=15000),
                   'value_as_string':['5/28/2007','5/30/2007','6/4/2007']*5000}).set_index('person_id')['value_as_string']


def map_infere_datetime_format(t1, t2):
  t1['observation_date'] = t1['person_id'].map(t2)
  t1['observation_date'] = pd.to_datetime(t1['observation_date'])
  t1['observation_datetime'] = pd.to_datetime(t1['observation_date']).dt.strftime('%m/%d/%Y %H:%M:%S')
  return t1

# explicitly specify format instead of pandas doing the work for you
def map_explicit_datetime_format(t1, t2):
  t1['observation_date'] = t1['person_id'].map(t2)
  t1['observation_date'] = pd.to_datetime(t1['observation_date'], format='%m/%d/%Y')
  t1['observation_datetime'] = t1['observation_date'].dt.strftime('%m/%d/%Y %H:%M:%S')
  return t1

在Google colab上运行的测试结果:

%%timeit -n3
map_infere_datetime_format(t1, t2)
# 3 loops, best of 3: 2.04 s per loop

%%timeit -n3
map_explicit_datetime_format(t1, t2)
# 3 loops, best of 3: 290 ms per loop

由于t2较小,因此在进行映射之前将t2转换为日期时间是有意义的。

希望有帮助!