[英]Conditional merge / join of two large Pandas DataFrames with duplicated keys based on values of multiple columns - Python
我來自 R 老實說,這是使用 R data.tables 在一行中做的最簡單的事情,而且對於大型數據表操作也相當快。 Bu 我真的很難在 Python 中實現它。前面提到的用例都不適合我的應用程序。 手頭的主要問題是 Python 解決方案中的 memory 用法,我將在下面解釋。
問題:我有兩個大數據幀 df1 和 df2(每個大約 50M-100M 行),我需要根據兩個條件將 df2 的兩列(或 n 列)合並到 df1:
1)df1.id = df2.id(合並的通常情況)
2) df2.value_2A <= df1.value_1 <= df2.value_2B
import numpy as np
import pandas as pd
df1 = pd.DataFrame({'id': [1,1,1,2,2,3], 'value_1': [2,5,7,1,3,4]})
df2 = pd.DataFrame({'id': [1,1,1,1,2,2,2,3], 'value_2A': [0,3,7,12,0,2,3,1], 'value_2B': [1,5,9,15,1,4,6,3]})
df1
Out[13]:
id value_1
0 1 2
1 1 5
2 1 7
3 2 1
4 2 3
5 3 4
df2
Out[14]:
id value_2A value_2B
0 1 0 1
1 1 3 5
2 1 7 9
3 1 12 15
4 2 0 1
5 2 2 4
6 2 3 6
7 3 1 3
desired_output
Out[15]:
id value_1 value_2A value_2B
0 1 2 NaN NaN
1 1 5 3.0 5.0
2 1 7 7.0 9.0
3 2 1 0.0 1.0
4 2 3 2.0 4.0
5 2 3 3.0 6.0
6 3 4 NaN NaN
現在,我知道這可以通過首先以“左”方式合並 df1 和 df2 然后過濾數據來完成。 但就擴展性而言,這是一個可怕的解決方案。 我有 50M x 50M 行,其中包含多個重復的 id。 這將創建一些巨大的 dataframe,我必須對其進行過濾。
## This is NOT a solution because memory usage is just too large and
## too many oprations deeming it extremely inefficient and slow at large scale
output = pd.merge(df1, df2, on='id', how='left') ## output becomes very large in my case
output.loc[~((output['value_1'] >= output['value_2A']) & (output['value_1'] <= output['value_2B'])), ['value_2A', 'value_2B']] = np.nan
output = output.loc[~ output['value_2A'].isnull()]
output = pd.merge(df1, output, on=['id', 'value_1'], how='left')
這太低效了。 我正在合並一個大型數據集兩次以獲得所需的 output 並在這樣做的同時創建大量數據幀。 呸!
將其視為兩個事件數據幀,我試圖將它們匹配在一起。 也就是說,標記 df1 的事件是否在 df2 的事件中發生。 df1 和 df2 中的每個 id 都有多個事件。 df2 的事件不是相互排斥的。 條件連接確實需要在連接時發生,而不是之后。 這在 R 中很容易完成:
## in R realm ##
require(data.table)
desired_output <- df2[df1, on=.(id, value_2A <= value_1, value_2B >= value_1)] #fast and easy operation
在 Python 中有什么辦法可以做到這一點嗎?
是的。 這是一個煩人的問題。 我通過將左邊的 DataFrame 分成塊來處理這個問題。
def merge_by_chunks(left, right, condition=None, **kwargs):
chunk_size = 1000
merged_chunks = []
for chunk_start in range(0, len(left), chunk_size):
print(f"Merged {chunk_start} ", end="\r")
merged_chunk = pd.merge(left=left[chunk_start: chunk_start+chunk_size], right=right, **kwargs)
if condition is not None:
merged_chunk = merged_chunk[condition(merged_chunk)]
merged_chunks.append(merged_chunk)
return pd.concat(merged_chunks)
然后您可以將條件提供為 function。
df1 = pd.DataFrame({'id': [1,1,1,2,2,3], 'value_1': [2,5,7,1,3,4]})
df2 = pd.DataFrame({'id': [1,1,1,1,2,2,2,3], 'value_2A': [0,3,7,12,0,2,3,1], 'value_2B': [1,5,9,15,1,4,6,3]})
def condition_func(output):
return (((output['value_1'] >= output['value_2A']) & (output['value_1'] <= output['value_2B'])))
output = merge_by_chunks(df1, df2, condition=condition_func, on='id', how='left')
merge_by_chunks(df1, output, on=['id', 'value_1'], how='left')
根據 DataFrame 的大小,它可能會非常慢,但不會用完 memory。
有趣的問題!
看起來 pandasql 可能會做你想做的事。 請參閱: 如何在 python Pandas 中進行條件連接?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.