pandas.merge размножает одинаковые значения при слиянии с параметром how="inner"

Коллеги, разбирая вот этот вопрос столкнулся с поведением pandas.merge, логика которого мне совершенно недоступна. Буду благодарен коллегам за объяснение. Даже ссылки на какую-то документация будет вполне достаточно.

Дано.

Есть два датафрейма, состоящие из одной колонки и мы пытаемся их смержить по этой колонке.

import pandas as pd

dic = {'col':['1','2','3','4','5','6','7','8','9']}
dic_1 = {'col':['9','8','7','6','5','4','3','2','1']}
df_1 = pd.DataFrame.from_dict(dic)
df_2 = pd.DataFrame.from_dict(dic_1)
df_total = pd.merge(df_1, df_2, on='col', how='inner')
print(df_1)
print(df_2)
print(df_total)

и все в порядке и логично, если значения хотя бы в одном из датафреймов не повторяются

  col
0   1
1   2
2   3
3   4
4   5
5   6
6   7
7   8
8   9

Но вот вопрос.

Если одно и тоже значение повторяется в обоих датафреймах, то результирующий датафрейм будет включать это значение m*n раз, где m и n - количество повторений значения в первом и втором датафрейме соответственно:

import pandas as pd

dic = {'col':['1','2','3','3','3','6','7','8','9']}
dic_1 = {'col':['9','8','7','6','5','3','3','2','1']}
df_1 = pd.DataFrame.from_dict(dic)
df_2 = pd.DataFrame.from_dict(dic_1)
df_total = pd.merge(df_1, df_2, on='col', how='inner')
print(df_1)
print(df_2)
print(df_total)

Вот результат:

   col
0    1
1    2
2    3
3    3
4    3
5    3
6    3
7    3
8    6
9    7
10   8
11   9

Все результаты получены эмпирическим путем и о логике я могу только строить предположения.

Буду благодарен за объяснения.


Ответы (3 шт):

Автор решения: Алексей Р

Этот случай является слиянием "многие ко многим" (many_to_many) - когда и слева и справа ключи для слияния с дубликатами - и актуален как для "inner", так и для "outer", "left" и "right". Какие есть варианты слияния при таком раскладе?

  1. Взять множество ключей слева и сопоставить каждый из них с одним ключом справа. Возникает неопределенность - какой именно ключ справа взять? Первый, последний, случайный или связывать по порядку (первый с первым, второй со вторым; ... а если дубликатов ключей слева 3, а справа 7? и строки в неопределённом порядке). Такой подход не годится, поскольку недетерминирован.
  2. Взять множество ключей слева и сопоставить каждый из них с каждым ключом справа. Здесь неопределенность пропадает - будут проверены и связаны все ключи.

Поэтому оно так и работает, это просто нужно учитывать.
А если вам критично, чтобы не было такого "размножения", можно использовать аргумент validate, в котором задать допустимый вариант слияния, например "many_to_one" - тогда при варианте "many_to_many" возникнет исключение. Ну или проверять фреймы до слияния на предмет уникальности ключей.

Доп. инфо. (англ)

→ Ссылка
Автор решения: CrazyElf

inner не имеет отношения к тому, как обходиться с дубликатами, это про то, что "сопоставить каждый ключевой элемент слева с каждым ключевым элементом справа и вывести строки, где эти элементы совпадают". Если ключи повторяются, то просто каждый из повторяющихся ключей будет участвовать в сопоставлении, отсюда и получается "размножение". Хотите, чтобы не было дубликатов - разбирайтесь с ними сами либо до merge, либо после.

→ Ссылка
Автор решения: Oopss

Можно добавить столбик идентификаторов и разглядеть наглядновведите сюда описание изображения

dic = {'col':['a','a','b','b'],'zn':[1,2,3,4]}
dic_1 = {'col':['a','a','a','b'],'zn':[10,20,30,40]}
df_1 = pd.DataFrame.from_dict(dic)
df_2 = pd.DataFrame.from_dict(dic_1)
df_total = pd.merge(df_1, df_2, on='col', how='inner')
print(df_1)
print(df_2)
print(df_total)
→ Ссылка