Необъяснимое поведение с Pandas Split (group) + Apply + Rejoin (concat), но только при сортировке - PullRequest
0 голосов
/ 28 января 2020

Я пытаюсь вычислить расстояния между столбцом и его задержкой (смещением) для групп в кадре данных Pandas. Группы должны быть отсортированы так, чтобы сдвиг был на один шаг раньше. Стандартный способ сделать это - .groupby() (он же Split), затем .apply() с функцией расстояния по каждой группе, а затем воссоединиться с .concat(). Это работает нормально, но только когда я не сортирую явно сгруппированный фрейм данных. когда я сортирую сгруппированный фрейм данных, я получаю сообщение об ошибке на этапе присоединения.

Вот мой пример кода, для которого мне удалось воспроизвести неожиданное поведение:

import pandas as pd

def dist_apply(group):

    # when commented out, this code will run to completion (!)
    group.sort_values(by='T',inplace=True)

    group['shift'] = group['Y'].shift()
    group['dist'] = group['Y'] - group['shift']
    return group

df = pd.DataFrame(pd.DataFrame({'X': ['A', 'B', 'A', 'B', 'A', 'B'], 'T': [0.9, 0.8, 0.7, 0.9, 0.8, 0.7], 'Y': [7, 1, 8, 3, 9, 5]}))
print(df)

# split
df_g = df.groupby(['X'])
# apply
df_g = df_g.apply(dist_apply)
print(df_g)

# rejoin
df = pd.concat([df,df_g],axis=1)
print(df)

Когда код который сортирует сгруппированный фрейм данных, закомментированный, затем код печатает это, что ожидается:

   X    T  Y
0  A  0.9  7
1  B  0.8  1
2  A  0.7  8
3  B  0.9  3
4  A  0.8  9
5  B  0.7  5

   X    T  Y  shift  dist
0  A  0.9  7    NaN   NaN
1  B  0.8  1    NaN   NaN
2  A  0.7  8    7.0   1.0
3  B  0.9  3    1.0   2.0
4  A  0.8  9    8.0   1.0
5  B  0.7  5    3.0   2.0

   X    T  Y  X    T  Y  shift  dist
0  A  0.9  7  A  0.9  7    NaN   NaN
1  B  0.8  1  B  0.8  1    NaN   NaN
2  A  0.7  8  A  0.7  8    7.0   1.0
3  B  0.9  3  B  0.9  3    1.0   2.0
4  A  0.8  9  A  0.8  9    8.0   1.0
5  B  0.7  5  B  0.7  5    3.0   2.0

С помощью линии сортировки трассировка выглядит так:

Traceback (most recent call last):
  File "test.py", line 19, in <module>
    df = pd.concat([df,df_g],axis=1)
  File "/Users/me/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/concat.py", line 229, in concat
    return op.get_result()
  File "/Users/me/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/concat.py", line 420, in get_result
    indexers[ax] = obj_labels.reindex(new_labels)[1]
  File "/Users/me/miniconda3/lib/python3.7/site-packages/pandas/core/indexes/multi.py", line 2236, in reindex
    target = MultiIndex.from_tuples(target)
  File "/Users/me/miniconda3/lib/python3.7/site-packages/pandas/core/indexes/multi.py", line 396, in from_tuples
    arrays = list(lib.tuples_to_object_array(tuples).T)
  File "pandas/_libs/lib.pyx", line 2287, in pandas._libs.lib.tuples_to_object_array
TypeError: object of type 'int' has no len()

Сортировка, но не работает concat выводит мне это для df_g:

     X    T  Y  shift  dist
X                          
A 2  A  0.7  8    NaN   NaN
  4  A  0.8  9    8.0   1.0
  0  A  0.9  7    9.0  -2.0
B 5  B  0.7  5    NaN   NaN
  1  B  0.8  1    5.0  -4.0
  3  B  0.9  3    1.0   2.0

, которое показывает, что оно сгруппировано иначе, чем печать df_g без сортировки (см. выше), но не ясно, как происходит сбой concat в этом случае.


update : я решил, что решил эту проблему, переименовав поврежденный столбец (в данном случае 'X') и также используя .reset_index() на сгруппированном кадре данных перед объединением.

df_g.columns = ['X_g','T','Y','shift','dist']
df = pd.concat([df,df_g.reset_index()],axis=1)

работает как положено, и печатает это:

   X    T  Y  X  level_1 X_g    T  Y  shift  dist
0  A  0.9  7  A        2   A  0.7  8    NaN   NaN
1  B  0.8  1  A        4   A  0.8  9    8.0   1.0
2  A  0.7  8  A        0   A  0.9  7    9.0  -2.0
3  B  0.9  3  B        5   B  0.7  5    NaN   NaN
4  A  0.8  9  B        1   B  0.8  1    5.0  -4.0
5  B  0.7  5  B        3   B  0.9  3    1.0   2.0

Но если присмотреться, этот столбец показывает, что слияние выполнено неправильно:

    1  B  0.8  1  A        4   A  0.8  9    8.0   1.0

Я использую Ма c OSX с Python 3.7.6 | упаковано в conda-forge | (по умолчанию, 7 января 2020 г., 22:05:27)

Pandas 0.24.2 + Numpy 1.17.3, а также попытались обновить до Pandas 0.25.3 и Numpy 1.17.5 с тем же результатом.

1 Ответ

0 голосов
/ 29 января 2020

Это условно работает.

Переименование столбцов, чтобы избежать дублирования:

df_g.columns = ['X_g','T','Y','shift','dist']

Сброс индекса в единичный индекс из мультииндекса :

df_g = df_g.reset_index(level=[0,1])

Простое объединение, сначала поставьте df_g, если хотите сохранить порядок отсортированной группы:

df = pd.merge(df_g,df)

дает мне

   X  level_1 X_g    T  Y  shift  dist
0  A        2   A  0.7  8    NaN   NaN
1  A        4   A  0.8  9    8.0   1.0
2  A        0   A  0.9  7    9.0  -2.0
3  B        5   B  0.7  5    NaN   NaN
4  B        1   B  0.8  1    5.0  -4.0
5  B        3   B  0.9  3    1.0   2.0

Полный код:

import pandas as pd

def dist_apply(group):

    group.sort_values(by='T',inplace=True)

    group['shift'] = group['Y'].shift()
    group['dist'] = group['Y'] - group['shift']
    return group

df = pd.DataFrame(pd.DataFrame({'X': ['A', 'B', 'A', 'B', 'A', 'B'], 'T': [0.9, 0.8, 0.7, 0.9, 0.8, 0.7], 'Y': [7, 1, 8, 3, 9, 5]}))
print(df)
df_g = df.groupby(['X'])

df_g = df_g.apply(dist_apply)

#print(df_g)

df_g.columns = ['X_g','T','Y','shift','dist']
df_g = df_g.reset_index(level=[0,1])

#print(df_g)
df = pd.merge(df_g,df)

print(df)
...