首页 > 解决方案 > 使用最近时间连接数据框并为特定对象填充缺失的时间戳

问题描述

我有两个数据框。

数据帧 #1 (df1):

1    object_name     time_epoch_ms      source      data
2    a               1538518822490      source_1     some_data
3    b               1538528822490      source_2     some_data
4    b               1538538822490      source_2     some_data
5    b               1538548822490      source_3     some_data
6    b               1538558822490      source_1     some_data
7    c               1538568822490      source_2     some_data
8    c               1538578822490      source_2     some_data
9    c               1538588822490      source_2     some_data
... etc etc

数据帧#2(df2):

1    object_name     time_epoch_ms      new_data
2    a               1538518722490      x
3    b               1538528822490      y
4    b               1538518922490      z
5    b               1538519922490      a
6    b               1538598822490      b
7    c               1538548822490      c
8    c               1538538822490      c
9    c               1538528822490      d
... etc etc

这两个表中的条目提供了有关不同时间点具有 object_names 的对象的信息。

我想组合这两个表,以便数据帧#2 增加了 object_name 在数据帧#1 中指定的特定时间使用的源。

问题:两个表中的时间戳不完全匹配。表 2 中存在的一些时间戳在表 1 中不存在。但是,它们应该大致对应于小时。表 1 中的一些时间戳缺失,因此“最后可用数据”是最好的。

有没有办法使用熊猫的合并能力执行以下步骤?

  1. 匹配数据帧中的数据,以便 df2 根据 object_name 和 time_stamp 从 df1 获取“源”数据,以便 time_stamp 与最近的时间匹配。
  2. 如果特定小时的数据在 df2 中,但不在 df1 中,则从最后一个可用数据小时重试源,因此根据该对象存在的任何数据“填充”它。
  3. 如果 df2 中的对象不在 df1 中,则它得到一个“null”

我的做法:

我目前使用 .apply 从 df2 获取每一行,并在 df1 中找到该对象的所有时间戳(如果存在)。然后,我返回最接近的匹配项或 null。

我想知道是否有更优雅的方法可以使用 pandas 的合并或 concat 功能来做到这一点,但我无法理解在这种情况下如何使用它们以及如何处理填充数据并根据小时进行匹配(不做单独的预处理以获得一个小时列)。

标签: pythonpandasdataframe

解决方案


一般来说,Pandas 使“等连接”变得容易,但其他类型的连接有点困难。在这种情况下你很幸运,因为有一个很好的方法可以满足merge_asof你的需要。

关于如何设置数据有点迂腐,但 MWE 是:

from io import StringIO
import pandas as pd

df1 = pd.read_table(StringIO("""1    object_name     time_epoch_ms      source      data
2    a               1538518822490      source_1     some_data_1
3    b               1538528822490      source_2     some_data_2
4    b               1538538822490      source_2     some_data_3
5    b               1538548822490      source_3     some_data_4
6    b               1538558822490      source_1     some_data_5
7    c               1538568822490      source_2     some_data_6
8    c               1538578822490      source_2     some_data_7
9    c               1538588822490      source_2     some_data_8
"""), sep=r"\s+", index_col=0)

df2 = pd.read_table(StringIO("""1    object_name     time_epoch_ms      new_data
2    a               1538518722490      x
3    b               1538528822490      y
4    b               1538518922490      z
5    b               1538519922490      a
6    b               1538598822490      b
7    c               1538548822490      c
8    c               1538538822490      c
9    c               1538528822490      d
"""), sep=r"\s+", index_col=0)

pd.merge_asof(
    df2.sort_values(['time_epoch_ms', 'object_name']),
    df1.sort_values(['time_epoch_ms', 'object_name']),
    by="object_name", on="time_epoch_ms",
    direction='forward',
).sort_values(['object_name', 'time_epoch_ms'])

回馈:

  object_name  time_epoch_ms new_data    source         data
0           a  1538518722490        x  source_1  some_data_1
1           b  1538518922490        z  source_2  some_data_2
2           b  1538519922490        a  source_2  some_data_2
3           b  1538528822490        y  source_2  some_data_2
7           b  1538598822490        b       NaN          NaN
4           c  1538528822490        d  source_2  some_data_6
5           c  1538538822490        c  source_2  some_data_6
6           c  1538548822490        c  source_2  some_data_6

另一个例子见Pandas 等效的 SQL non-equi JOIN。还有merge_ordered,但我认为这对你的情况没有帮助。


推荐阅读