Pandas - Слияние строк столбцов не работает (ошибка?)
Я пытаюсь сделать простое слияние между двумя файлами данных. Они исходят из двух разных таблиц SQL, где соединяющие ключи являются строками:
>>> df1.col1.dtype
dtype('O')
>>> df2.col2.dtype
dtype('O')
Я пытаюсь объединить их, используя это:
>>> merge_res = pd.merge(df1, df2, left_on='col1', right_on='col2')
Результат внутреннего объединения пуст, и это сначала подсказывало мне, что на пересечении могут быть не какие-либо записи:
>>> merge_res.shape
(0, 19)
Но когда я пытаюсь сопоставить один элемент, я вижу это действительно странное поведение.
# Pick random element in second dataframe
>>> df2.iloc[5,:].col2
'95498208100000'
# Manually look for it in the first dataframe
>>> df1[df1.col1 == '95498208100000']
0 rows × 19 columns
# Empty, which makes sense given the above merge result
# Now look for the same value as an integer
>>> df1[df1.col1 == 95498208100000]
1 rows × 19 columns
# FINDS THE ELEMENT!?!
Итак, столбцы определяются с типом 'object'. Поиск их в виде строк не дает никаких результатов. Поиск их как целых чисел возвращает результат, и я думаю, что это причина, по которой слияние не работает выше.
Любые идеи, что происходит?
Это почти так, как думал Pandas преобразует df1.col1
в целое число только потому, что он может, хотя он должен рассматриваться как строка при сопоставлении.
(Я попытался реплицировать это, используя образцы данных, но для небольших примеров я не вижу этого. Любые предложения о том, как я могу найти более описательный пример, также будут оценены.)
Ответы
Ответ 1
Проблема заключалась в том, что dtpe object
вводит в заблуждение. Я думал, это означает, что все предметы были струнами. Но, видимо, при чтении файла pandas было преобразование некоторых элементов в int и оставление остатков в виде строк.
Решение заключалось в том, чтобы каждое поле было строкой:
>>> df1.col1 = df1.col1.astype(str)
>>> df2.col2 = df2.col2.astype(str)
Затем слияние работает так, как ожидалось.
(Я хотел бы указать способ dtype
str
...)
Ответ 2
Я столкнулся с ситуацией, когда решение df.col = df.col.astype(str)
не работало. Оказывается, проблема была в кодировке.
Мои исходные данные выглядели следующим образом:
In [72]: df1['col1'][:3]
Out[73]:
col1
0 dustin pedroia
1 kevin youkilis
2 david ortiz
In [72]: df2['col2'][:3]
Out[73]:
col2
0 dustin pedroia
1 kevin youkilis
2 david ortiz
И после использования .astype(str)
слияние все еще не работало, поэтому я выполнил следующее:
df1.col1 = df1.col1.str.encode('utf-8')
df2.col2 = df2.col2.str.encode('utf-8')
и смог найти разницу:
In [95]: df1
Out[95]:
col1
0 b'dustin\xc2\xa0pedroia'
1 b'kevin\xc2\xa0youkilis'
2 b'david\xc2\xa0ortiz'
In [95]: df2
Out[95]:
col2
0 b'dustin pedroia'
1 b'kevin youkilis'
2 b'david ortiz'
В этот момент все, что мне нужно было сделать, было запустить df1.col1 = df1.col1.str.replace('\xa0',' ')
в декодированной переменной df1.col1 (т.е. перед запуском .str.encode('utf-8')
), и слияние отлично работало.
ПРИМЕЧАНИЕ. Независимо от того, что я заменяю, я всегда использовал .str.encode('utf-8')
, чтобы проверить, работает ли он.
В качестве альтернативы
Используя регулярные выражения и проводник переменных в IDE Spyder для Anaconda, я нашел следующее различие.
import re
#places the raw string into a list
df1.col1 = df1.col1.apply(lambda x: re.findall(x, x))
df2.col2 = df2.col2.apply(lambda x: re.findall(x, x))
где мои данные df1 превратились в это (скопировано и вставлено из Spyder):
['dustin\xa0pedroia']
['kevin\xa0youkilis']
['david\xa0ortiz']
который имеет немного другое решение. Я не знаю, в каком случае первый пример не сработает, а второй будет, но я хотел бы предоставить как на всякий случай, если кто-то столкнется с ним:)
Ответ 3
Спасибо, @seeiespi the..str.encode('utf-8') помог мне понять, что моя строка должна быть удалена, как показано ниже
20 b'Belize ' ... 0,612
21 b'Benin ' ... 0,546
Решение было использовать полосу
df1.col1 = df1.col1.str.strip()
df1.col1 = df1.col1.str.strip()
Ответ 4
Ни одно из вышеперечисленных решений не помогло мне, потому что слияние действительно было выполнено правильно, но индексация была испорчена. Удаление индекса решило это для меня:
df['sth'] = df.merge(df2, how='left', on=['x', 'y'])['sth'].values