【问题标题】:Pandas left join between datetimes熊猫在日期时间之间离开加入
【发布时间】:2021-07-01 22:41:33
【问题描述】:

我必须使用数据框 - dfgdf

from datetime import datetime
import pandas as pd

data = [['foo', datetime(2020,1,1,0,0,0) ], ['foo', datetime(2020,2,1,0,0,0)], ['foo', datetime(2020,3,1,0,0,0)],
       ['bar', datetime(2020,4,1,0,0,0)],['bar', datetime(2020,5,1,0,0,0)],['bar', datetime(2020,6,1,0,0,0)]]
df = pd.DataFrame(data, columns = ['id', 'timestamp'])

data = [['A', datetime(2020,1,15,0,0,0), datetime(2020,3,15,0,0,0) ], ['B', datetime(2020,4,15,0,0,0),datetime(2020,6,15,0,0,0)]]
gdf = pd.DataFrame(data, columns = ['geoid', 'starttime', 'endtime'])


df
    id  timestamp
0   foo 2020-01-01
1   foo 2020-02-01
2   foo 2020-03-01
3   bar 2020-04-01
4   bar 2020-05-01
5   bar 2020-06-01

gdf
    geoid starttime     endtime
0   A     2020-01-15    2020-03-15
1   B     2020-04-15    2020-06-15

我的目标是在df 上左加入gdf,其中timestamp 介于starttimeendtime 之间,因此输出如下所示:

res
    id  timestamp   geoid
0   foo 2020-01-01  None
1   foo 2020-02-01  A
2   foo 2020-03-01  A
3   bar 2020-04-01  None
4   bar 2020-05-01  B
5   bar 2020-06-01  B

据我研究,pandas 中唯一存在的时间连接方法是 pandas.merge_asof(),它不适合这个用例,因为目标是在时间戳之间合并,而不是最接近的。

pandas(不使用 sqllite)中基于重叠时间戳将一个表与另一个表(左连接)合并的正确方法是什么?

【问题讨论】:

    标签: python pandas dataframe merge left-join


    【解决方案1】:

    您可以创建一个虚拟列并使用df.merge

    In [1460]: df['tmp'] = 1
    In [1461]: gdf['tmp'] = 1
    
    In [1463]: x = df.merge(gdf) # merge on `tmp` column.
    
    # assign None to geoid where timestamp is not in range
    In [1465]: import numpy as np
    In [1466]: x['geoid'] = np.where(x['timestamp'].between(x.starttime, x.endtime), x.geoid, None) 
    
    # groupby and pick the correct geoid
    In [1477]: ans = x.groupby(['id', 'timestamp'])['geoid'].first().reset_index()
    
    In [1478]: ans
    Out[1478]: 
        id  timestamp geoid
    0  bar 2020-04-01  None
    1  bar 2020-05-01     B
    2  bar 2020-06-01     B
    3  foo 2020-01-01  None
    4  foo 2020-02-01     A
    5  foo 2020-03-01     A
    

    【讨论】:

      【解决方案2】:

      如果可能,使用由gdf 列创建的IntervalIndex,然后通过Index.get_indexer 获取位置并通过在numpy 中使用None 索引来获取geoid,如果-1(不匹配):

      s = pd.IntervalIndex.from_arrays(gdf['starttime'], gdf['endtime'], closed='both')
      
      arr = gdf['geoid'].to_numpy()
      pos = s.get_indexer(df['timestamp'])
      
      df['new'] = np.where(pos != -1, arr[pos], None)
      print (df)
          id  timestamp   new
      0  foo 2020-01-01  None
      1  foo 2020-02-01     A
      2  foo 2020-03-01     A
      3  bar 2020-04-01  None
      4  bar 2020-05-01     B
      5  bar 2020-06-01     B
          
              
      

      或使用交叉连接的解决方案,将df 的索引转换为reset_index 列以避免丢失索引值并在Series.between 中过滤DataFrame.loc,最后通过DataFrame.set_index 添加新列以匹配@987654337带有df.index的@列:

      df1 = df.reset_index().assign(a=1).merge(gdf.assign(a=1), on='a')
      df1 = df1.loc[df1['timestamp'].between(df1['starttime'], df1['endtime']), ['index','geoid']]
      
      df['geoid'] = df1.set_index('index')['geoid']
      print (df)
          id  timestamp geoid
      0  foo 2020-01-01   NaN
      1  foo 2020-02-01     A
      2  foo 2020-03-01     A
      3  bar 2020-04-01   NaN
      4  bar 2020-05-01     B
      5  bar 2020-06-01     B
      

      【讨论】:

      • 使用IntervalIndex 太棒了!
      猜你喜欢
      • 2020-10-19
      • 2016-11-09
      • 2016-07-05
      • 2013-06-05
      • 2021-05-08
      • 2019-07-19
      • 2022-10-12
      • 2017-02-23
      相关资源
      最近更新 更多