Почему панды сливаются в NaN?

Недавно я задал вопрос относительно отсутствующих значений в панд здесь и был направлен на вопрос GitHub. После прочтения этой страницы и отсутствующих данных документации.

Мне интересно, почему merge и join рассматривают NaN как совпадение, когда "они не сравниваются равными": np.nan != np.nan

# merge example
df = pd.DataFrame({'col1':[np.nan, 'match'], 'col2':[1,2]})
df2 = pd.DataFrame({'col1':[np.nan, 'no match'], 'col3':[3,4]})
pd.merge(df,df2, on='col1')

    col1    col2    col3
0   NaN      1       3

# join example with same dataframes from above
df.set_index('col1').join(df2.set_index('col1'))

      col2  col3
col1        
NaN     1   3.0
match   2   NaN

Тем не менее, NaNs в groupby исключены:

df = pd.DataFrame({'col1':[np.nan, 'match', np.nan], 'col2':[1,2,1]})
df.groupby('col1').sum()

       col2
col1    
match   2

Конечно, вы можете dropna() или df[df['col1'].notnull()] но мне любопытно, почему NaN исключены в некоторых операциях панд, таких как groupby а не в других, таких как merge, join, update и map?

Ответ 1

Да, это определенно ошибка. См. GH22491, которая точно описывает вашу проблему, и GH22618, которая. основываясь на обсуждениях, это не похоже на предполагаемое поведение.

Быстрое погружение в источник показывает, что проблема может быть в функции _factorize_keys в core/reshape/merge.py. Эта функция, по-видимому, разделяет клавиши, чтобы определить, какие строки должны быть сопоставлены друг с другом.

В частности, эта часть

# NA group
lmask = llab == -1
lany = lmask.any()
rmask = rlab == -1
rany = rmask.any()

if lany or rany:
    if lany:
        np.putmask(llab, lmask, count)
    if rany:
        np.putmask(rlab, rmask, count)
    count += 1

... кажется виновником. Ключи NaN идентифицируются как допустимая категория (с категориальным значением, равным count).

Отказ от ответственности: я не разработчик панда, и это только мое предположение; таким образом, реальная проблема могла быть чем-то другим. Но на первый взгляд кажется, что это так.