Можно использовать pd.merge_asof
, чтобы найти опции до и после.
before_df = pd.merge_asof(df1, df2, left_on='start', right_on='end', suffixes=['', '_before'])
before_df
# start end start_before end_before
# 0 200 300 NaN NaN
# 1 600 900 350.0 550.0
# 2 950 1050 650.0 800.0
after_df = pd.merge_asof(df2, df1, left_on='start', right_on='end', suffixes=['_after', ''])
# start_after end_after start end
# 0 350 550 200 300
# 1 650 800 200 300
# 2 900 1100 600 900
Но это не так легко сделать, или вычисления подмножества и надмножества. Для тех, кто хотел бы воспользоваться этим алгоритмом, который может работать за один проход.
def range_intersect(lh_ranges, rh_ranges):
all_ranges = sorted(
[(b, e, 'lh') for b, e in lh_ranges] +
[(b, e, 'rh') for b, e in rh_ranges]
)
res = []
max_b, max_e = None, None
for b, e, which in all_ranges:
if which == 'rh':
if max_e is None or e > max_e:
max_b, max_e = b, e
elif max_e is not None and e <= max_e:
res.append((b, e, max_b, max_e))
return res
Это находит элементы lh
, которые являются подмножествами элементов в rh
. Чтобы найти суперсет, его можно запустить в обратном порядке. Для простоты он принимает список диапазонов вместо DataFrame
s. Преобразование является простым.
lh = df1.to_dict('split')['data']
rh = df2.to_dict('split')['data']
lh
# [[200, 300], [600, 900], [950, 1050]]
rh
# [[350, 550], [650, 800], [900, 1100]]
После этого желаемый результат DataFrame
находится всего в нескольких слияниях.
# Compute supersets, then run in reverse to get the subsets.
superset_df = pd.DataFrame(range_intersect(lh, rh), columns=['start', 'end', 'start_superset', 'end_superset'])
subset_df = pd.DataFrame(range_intersect(rh, lh), columns=['start_subset', 'end_subset', 'start', 'end'])
# Merge all the results together.
result = df1.merge(subset_df, how='left').merge(superset_df, how='left').merge(before_df, how='left').merge(after_df, how='left')
# The reversed operations, after and subset, can have many matches in df1.
result.drop_duplicates(['start', 'end'])
# start end start_subset end_subset start_superset end_superset start_before end_before start_after end_after
# 0 200 300 NaN NaN NaN NaN NaN NaN 350.0 550.0
# 2 600 900 650.0 800.0 NaN NaN 350.0 550.0 900.0 1100.0
# 3 950 1050 NaN NaN 900.0 1100.0 650.0 800.0 NaN NaN