【问题标题】:Copy matching value from one df to another given multiple conditions将匹配值从一个df复制到另一个给定的多个条件
【发布时间】:2020-04-07 09:03:18
【问题描述】:

我有两个数据框。第一个,df1,有一个非唯一的 ID 和一个以 ms 为单位的时间戳值。另一个 df2 具有非唯一 ID、单独的唯一 ID、开始时间和结束时间(均以毫秒为单位)。

我需要从 df2 获取 df1 中每一行的正确唯一 ID。我会这样做...

  1. 将 df1 中的每个非唯一 ID 与 df2 中的相关行系列匹配
  2. 在这些行中,找到开始和结束范围包含 df1 中时间戳的行
  3. 从结果行中获取唯一 ID 并将其复制到 df1 中的新列

我认为我不能使用 pd.merge,因为我需要将 df1 时间戳与 df2 中的两个不同列进行比较。我认为 df.apply 是我的答案,但我想不通。

这是一些虚拟代码:

df1_dict = {
    'nonunique_id': ['abc','def','ghi','jkl'],
    'timestamp': [164.3,2071.2,1001.7,846.4]
}

df2_dict = {
    'nonunique_id': ['abc','abc','def','def','ghi','ghi','jkl','jkl'],
    'unique_id': ['a162c1','md85k','dk102','l394j','dj4n5','s092k','dh567','57ghed0'],
    'time_start': [160,167,2065,2089,1000,1010,840,876],
    'time_end': [166,170,2088,3000,1009,1023,875,880]
}

df1 = pd.DataFrame(data=df1_dict)
df2 = pd.DataFrame(data=df2_dict)

这里是手动测试...

df2['unique_id'][(df2['nonunique_id'].eq('abc')) & (df2['time_start']<=164.3) & (df2['time_end']>=164.3)]

...返回预期的输出(来自 df2 的相关唯一 ID):

0 a162c1

名称:unique_id,数据类型:对象

我想要一个可以自动应用上述手动测试的功能,并将结果复制到 df1 的新列中。

我试过了……

def unique_id_fetcher(nonunique_id,timestamp):
    cond_1 = df2['nonunique_id'].eq(nonunique_id)
    cond_2 = df2['time_start']<=timestamp
    cond_3 = df2['time_end']>=timestamp
    
    unique_id = df2['unique_id'][(cond_1) & (cond_2) & (cond_3)]
    
    return unique_id

df1['unique_id'] = df1.apply(unique_id_fetcher(df1['nonunique_id'],df1['timestamp']))

...但这会导致:

ValueError: 只能比较标签相同的 Series 对象

为清楚起见进行了编辑

【问题讨论】:

  • 你的预期输出是什么?
  • 使用虚拟代码,我希望:0 a162c1Name: unique_id, dtype: object(请参阅上面突出显示的文本的第一块)。一般来说,预期的输出是来自 df2 的 unique_id。
  • 明白,添加答案。

标签: python pandas dataframe apply


【解决方案1】:

IIUC,

您可以对两个数据框进行 caretsian 乘积并进行合并,然后应用您的逻辑

您创建一个 dict 并将值映射回您的 df1,使用 non_unique_id 作为键。

df1['key'] = 'var'
df2['key'] = 'var'
df3 = pd.merge(df1,df2,on=['key','nonunique_id'],how='outer')

df4 = df3.loc[
    (df3["timestamp"] >= df3["time_start"]) & (df3["timestamp"] <= df3["time_end"])
]

d = dict(zip(df4['nonunique_id'],df4['unique_id']))

df1['unique_id'] = df1['nonunique_id'].map(d)

print(df1.drop('key',axis=1))

  nonunique_id  timestamp unique_id
0          abc      164.3    a162c1
1          def     2071.2     dk102
2          ghi     1001.7     dj4n5
3          jkl      846.4     dh567

【讨论】:

  • 是的!这是预期的输出,谢谢。让我用这个方法坐一会儿。
  • 没问题,如果您需要任何额外的解释,请告诉我,这些概念一点也不难!
  • 好吧,这绝对为我指明了正确的方向!首先,我意识到先合并然后删除的两步法可能是我所追求的,但我陷入了“必须有一步法”的心理模型。现在,特定于您的代码:(1)我能够在不添加通用“var”列的情况下实现合并;你能详细说明这些目的吗?(2)在我的特定用例中,我不需要 df1 的干净版本,所以我可以在那里做所有事情,而不是创建额外的数据框。 TLDR:这就是我最终要做的事情。
  • var 的目的是创建一个合并的数据帧,其中包含两个数据帧的所有可能结果,它在 sql server 中称为交叉连接,在其他地方称为笛卡尔连接。您为什么不发布您的解决方案作为答案?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多