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". Какие есть варианты слияния при таком раскладе?
- Взять множество ключей слева и сопоставить каждый из них с одним ключом справа. Возникает неопределенность - какой именно ключ справа взять? Первый, последний, случайный или связывать по порядку (первый с первым, второй со вторым; ... а если дубликатов ключей слева 3, а справа 7? и строки в неопределённом порядке). Такой подход не годится, поскольку недетерминирован.
- Взять множество ключей слева и сопоставить каждый из них с каждым ключом справа. Здесь неопределенность пропадает - будут проверены и связаны все ключи.
Поэтому оно так и работает, это просто нужно учитывать.
А если вам критично, чтобы не было такого "размножения", можно использовать аргумент validate, в котором задать допустимый вариант слияния, например "many_to_one" - тогда при варианте "many_to_many" возникнет исключение. Ну или проверять фреймы до слияния на предмет уникальности ключей.
inner не имеет отношения к тому, как обходиться с дубликатами, это про то, что "сопоставить каждый ключевой элемент слева с каждым ключевым элементом справа и вывести строки, где эти элементы совпадают". Если ключи повторяются, то просто каждый из повторяющихся ключей будет участвовать в сопоставлении, отсюда и получается "размножение". Хотите, чтобы не было дубликатов - разбирайтесь с ними сами либо до merge, либо после.
Можно добавить столбик идентификаторов и разглядеть наглядно
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)