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