Pandas: ОШИБКА: concat нежелательно сортирует имена столбцов DataFrame, если они отличаются

Созданный на 17 авг. 2013  ·  36Комментарии  ·  Источник: pandas-dev/pandas

При объединении DataFrames имена столбцов сортируются по алфавиту, если между ними есть какие-либо различия. Если они идентичны в DataFrames, они не сортируются.
Этот вид недокументирован и нежелателен. Конечно, поведение по умолчанию не должно быть сортировкой. РЕДАКТИРОВАТЬ: стандартный порядок, как в SQL, будет: столбцы из df1 (тот же порядок, что и в df1), столбцы (однозначно) из df2 (за вычетом общих столбцов) (тот же порядок, что и в df2). Пример:

df4a = DataFrame(columns=['C','B','D','A'], data=np.random.randn(3,4))
df4b = DataFrame(columns=['C','B','D','A'], data=np.random.randn(3,4))
df5  = DataFrame(columns=['C','B','E','D','A'], data=np.random.randn(3,5))

print "Cols unsorted:", concat([df4a,df4b])
# Cols unsorted:           C         B         D         A

print "Cols sorted", concat([df4a,df5])
# Cols sorted           A         B         C         D         E
``'
API Design Reshaping

Самый полезный комментарий

Это действительно довольно неожиданное поведение, и я тоже наткнулся на него.

 >>> df = pd.DataFrame()

>>> df['b'] = [1,2,3]
>>> df['c'] = [1,2,3]
>>> df['a'] = [1,2,3]
>>> print(df)
   b  c  a
0  1  1  1
1  2  2  2
2  3  3  3

[3 rows x 3 columns]
>>> df2 = pd.DataFrame({'a':[4,5]})
>>> df3 = pd.concat([df, df2])

Наивно можно было бы ожидать, что порядок столбцов сохранится. Вместо этого столбцы сортируются:

>>> print(df3)
   a   b   c
0  1   1   1
1  2   2   2
2  3   3   3
0  4 NaN NaN
1  5 NaN NaN

[5 rows x 3 columns]

Это можно исправить, переиндексировав исходные столбцы следующим образом:

>>> df4 = df3.reindex_axis(df.columns, axis=1)
>>> print(df4)
    b   c  a
0   1   1  1
1   2   2  2
2   3   3  3
0 NaN NaN  4
1 NaN NaN  5

[5 rows x 3 columns]

Тем не менее кажется нелогичным, что эта автоматическая сортировка имеет место и не может быть отключена, насколько мне известно.

Все 36 Комментарий

Глядя на это вкратце, я _ думаю_, что это происходит из Index.intersection, чья строка документации гласит:

Сформируйте пересечение двух объектов Index. Сортировка результата не гарантируется.

Не уверен, в каких случаях они появляются / сортируются, но случай, когда столбцы равны (в вашем первом), является специальным случаем, чтобы вернуть тот же результат ...

@smcierney, какой заказ вы ожидаете вместо этого?

Я обнаружил, что автосортировка тоже немного раздражает (ну, я бы сказал, зависит от вашей цели), потому что я пытался объединить фрейм в пустой фрейм в цикле (например, добавить элемент в список). Затем я понял, что порядок моих столбцов изменился. Это изменение также относится к индексу, если вы выполняете конкатенацию по оси = 1.

В случае, аналогичном случаю @smcinerney , я ожидаю окончательный заказ CBDAE. E появляется последним, потому что заказ CBDA появляется первым при конкатенации.

Поэтому я написал "хак" (хотя и глупо)

sorted = pd.concat (frameList, axis = axis, join = join, join_axes = join_axes, ignore_index = False, keys = None, levels = None, names = None, verify_integrity = False)

if join_axes:
    return sorted
elif sort:
    return sorted
else:
    # expand all original orders in each frame
    sourceOrder = []
    for frame in frameList:
        sourceOrder.extend(frame.Columns()) if axis == 0 else sourceOrder.extend(frame.Indices())
    sortedOrder = sorted.Columns() if axis == 0 else sorted.Indices()

    positions = []
    positionsSorted = []
    for i in sortedOrder:
        positions.append(sourceOrder.index(i))
        positionsSorted.append(sourceOrder.index(i))
    positionsSorted.sort()

    unsortedOrder = []
    for i in positionsSorted:
        unsortedOrder.append(sortedOrder[positions.index(i)])

    return sorted.ReorderCols(unsortedOrder) if axis == 0 else sorted.ReorderRows(unsortedOrder)

Функция включена в мой личный модуль под названием kungfu! Любой желающий может принять вышеуказанный алгоритм или взглянуть на мой модуль на https://github.com/jerryzhujian9/kungfu

Наконец, я очень ценю работу команды разработчиков над этим замечательным модулем!

Это действительно довольно неожиданное поведение, и я тоже наткнулся на него.

 >>> df = pd.DataFrame()

>>> df['b'] = [1,2,3]
>>> df['c'] = [1,2,3]
>>> df['a'] = [1,2,3]
>>> print(df)
   b  c  a
0  1  1  1
1  2  2  2
2  3  3  3

[3 rows x 3 columns]
>>> df2 = pd.DataFrame({'a':[4,5]})
>>> df3 = pd.concat([df, df2])

Наивно можно было бы ожидать, что порядок столбцов сохранится. Вместо этого столбцы сортируются:

>>> print(df3)
   a   b   c
0  1   1   1
1  2   2   2
2  3   3   3
0  4 NaN NaN
1  5 NaN NaN

[5 rows x 3 columns]

Это можно исправить, переиндексировав исходные столбцы следующим образом:

>>> df4 = df3.reindex_axis(df.columns, axis=1)
>>> print(df4)
    b   c  a
0   1   1  1
1   2   2  2
2   3   3  3
0 NaN NaN  4
1 NaN NaN  5

[5 rows x 3 columns]

Тем не менее кажется нелогичным, что эта автоматическая сортировка имеет место и не может быть отключена, насколько мне известно.

Я тоже только что наткнулся на это.

new_data = pd.concat([churn_data, numerical_data])

Создан DataFrame:

     churn  Var1  Var10  Var100  Var101 
0      -1   NaN    NaN     NaN     NaN     
1      -1   NaN    NaN     NaN     NaN

Казалось бы, более естественным было бы объединить числовой DataFrame без предварительной сортировки !!

ну, это немного нужно исправить. но запросы на вытягивание приняты!

Просто наткнулся на ту же проблему, когда конкатенировал DataFrames . Если вы не знаете об этой проблеме, это немного раздражает, но на самом деле есть быстрое решение:

скажем, dfs - это список DataFrames вы хотите объединить, вы можете просто взять исходный порядок столбцов и передать его обратно:

df = pd.concat(dfs, axis=0)
df = df[dfs[0].columns]

Я считаю, что append вызывает такое же поведение, к вашему сведению

Это стандартное поведение по всем направлениям. Например, если вы примените функцию f к groupby (), которая возвращает различное количество столбцов, конкатенация, происходящая за сценой, также автоматически сортирует столбцы.

df.groupby (some_ts) .apply (f)

Вероятно, потому что известный порядок столбцов открыт для интерпретации.

Однако это также происходит для MultiIndices и всех иерархий в MultiIndices. Таким образом, вы можете объединить фреймы данных, которые согласовывают столбцы level0 и все столбцы уровня level1, и все уровни MultiIndices будут автоматически отсортированы из-за одного несоответствия в одном столбце level0. Я не думаю, что это желательно.

Я хотел бы помочь, но, к сожалению, решить эту проблему не в моих силах. Всем спасибо за тяжелую работу.

+1 за эту функцию

Согласовано, +1. У меня все время происходит неожиданная сортировка.

+1, это был неприятный сюрприз!

+1, я ненавижу сортировку столбцов после каждого append .

+1 от меня тоже.

Потому что, даже если бы я действительно хотел вручную изменить порядок после конкатенации, когда я пытаюсь распечатать 60+ имен столбцов и позиций в моем фреймворке данных:

 for id, value in enumerate(df.columns):
      print id, value

Все 60+ столбцов выводятся в алфавитном порядке, а не в их фактическом положении во фрейме данных.

Это означает, что после объединения мне нужно вручную ввести список из 60 столбцов, чтобы изменить их порядок. Ой.

Пока я здесь, есть ли у кого-нибудь способ распечатать имя и позицию столбца, которые мне не хватает?

+1 за эту функцию, только что сам наткнулся на такую ​​же сделку.

@summerela Получить индекс столбца, а затем повторно проиндексировать новый фрейм данных, используя исходный индекс столбца

# assuming you have two dataframes, `df_train` & `df_test` (with the same columns) 
# that you want to concatenate

# get the columns from one of them
all_columns = df_train.columns

# concatenate them
df_concat = pd.concat([df_train,
                       df_test])

# finally, re-index the new dataframe using the original column index
df_concat = df_concat.ix[:, all_columns]

И наоборот, если вам нужно повторно проиндексировать меньшее подмножество столбцов, вы можете использовать эту функцию, которую я сделал. Он также может работать с относительными индексами. Например, если вы хотите переместить столбец в конец фрейма данных, но не уверены, сколько столбцов может остаться после предыдущих этапов обработки в вашем скрипте (например, вы удаляете столбцы с нулевой дисперсией), вы можете передать относительную позицию индекса в new_indices -> new_indices = [-1] и он позаботится обо всем остальном.

def reindex_columns(dframe=None, columns=None, new_indices=None):
    """
    Reorders the columns of a dataframe as specified by
    `reorder_indices`. Values of `columns` should align with their
    respective values in `new_indices`.

    `dframe`: pandas dataframe.

    `columns`: list,pandas.core.index.Index, or numpy array; columns to
    reindex.

    `reorder_indices`: list of integers or numpy array; indices
    corresponding to where each column should be inserted during
    re-indexing.
    """
    print("Re-indexing columns.")
    try:
        df = dframe.copy()

        # ensure parameters are of correct type and length
        assert isinstance(columns, (pd.core.index.Index,
                                    list,
                                    np.array)),\
        "`columns` must be of type `pandas.core.index.Index` or `list`"

        assert isinstance(new_indices,
                          list),\
        "`reorder_indices` must be of type `list`"

        assert len(columns) == len(new_indices),\
        "Length of `columns` and `reorder_indices` must be equal"

        # check for negative values in `new_indices`
        if any(idx < 0 for idx in new_indices):

            # get a list of the negative values
            negatives = [value for value
                         in new_indices
                         if value < 0]

            # find the index location for each negative value in
            # `new_indices`
            negative_idx_locations = [new_indices.index(negative)
                                      for negative in negatives]

            # zip the lists
            negative_zipped = list(zip(negative_idx_locations,
                                       negatives))

            # replace the negatives in `new_indices` with their
            # absolute position in the index
            for idx, negative in negative_zipped:
                new_indices[idx] = df.columns.get_loc(df.columns[
                                                          negative])

        # re-order the index now
        # get all columns
        all_columns = df.columns

        # drop the columns that need to be re-indexed
        all_columns = all_columns.drop(columns)

        # now re-insert them at the specified locations
        zipped_columns = list(zip(new_indices,
                                  columns))

        for idx, column in zipped_columns:
            all_columns = all_columns.insert(idx,
                                             column)
        # re-index the dataframe
        df = df.ix[:, all_columns]

        print("Successfully re-indexed dataframe.")

    except Exception as e:
        print(e)
        print("Could not re-index columns. Something went wrong.")

    return df

Изменить: использование будет выглядеть следующим образом:

# move 'Column_1' to the end, move 'Column_2' to the beginning
df = reindex_columns(dframe=df,
                     columns=['Column_1', 'Column_2'],
                     new_indices=[-1, 0])

Я столкнулся с этим (с 0.13.1) из не упомянутого пограничного случая: объединение фреймов данных, каждый из которых содержит уникальные столбцы. Наивное переназначение имен столбцов не сработало:

dat = pd.concat([out_dust, in_dust, in_air, out_air])
dat.columns = [out_dust.columns + in_dust.columns + in_air.columns + out_air.columns]

Столбцы по-прежнему сортируются. Однако использование списков промежуточных решений:

Изменить: я заговорил слишком рано ..


Последующие действия: fwiw, порядок столбцов можно сохранить с помощью связанных вызовов .join для отдельных объектов:

df1.join([df2, df3]) # sorts columns
df1.join(df2).join(df3) # column order retained

Может ли быть параметр при создании dataFrame о порядке столбцов? Like order = False. Большое спасибо

просто столкнулся с этим при создании фрейма данных из словаря. Совершенно удивил меня, был нелогичен и разрушил всю мою цель ...

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

@patricktokeeffe
Спасибо за указатель на join . У объектов серии нет этого метода, поэтому я написал функцию:

def concat_fixed(ndframe_seq, **kwargs):
    """Like pd.concat but fixes the ordering problem.

    Converts Series objects to DataFrames to access join method
    Use kwargs to pass through to repeated join method
    """
    indframe_seq = iter(ndframe_seq)
    # Use the first ndframe object as the base for the final
    final_df = pd.DataFrame(next(indframe_seq))
    for dataframe in indframe_seq:
        if isinstance(dataframe, pd.Series):
            dataframe = pd.DataFrame(dataframe)
        # Iteratively build final table
        final_df = final_df.join(dataframe, **kwargs)
    return final_df

Как там эффективность?

В среду, 30 августа 2017 г., в 13:58, Брайс Гуинта [email protected]
написал:

@patricktokeeffe https://github.com/patricktokeeffe
Спасибо за указатель на присоединение. У объектов серии нет этого метода, поэтому
В итоге я написал функцию:

def concat_fixed (ndframe_seq, ** kwargs):
"" "То же, что и pd.concat, но устраняет проблему с упорядочением.

Converts Series objects to DataFrames to access join method
Use kwargs to pass through to repeated join method
"""
indframe_seq = iter(ndframe_seq)
# Use the first ndframe object as the base for the final
final_df = pd.DataFrame(next(indframe_seq))
for dataframe in indframe_seq:
    if isinstance(dataframe, pd.Series):
        # Convert Series objects into DataFrames since
        # series objects do not have a join method
        dataframe = pd.DataFrame(dataframe)
    # Iteratively build final table
    final_df = final_df.join(dataframe, **kwargs)
return final_df

-
Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/pandas-dev/pandas/issues/4588#issuecomment-326086636 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AG999MucF-NH5vHuKe-Zczq-jy9ziYkRks5sdbDogaJpZM4A6TeA
.

@ MikeTam1021

Я не собираюсь тестировать его atm, но думаю, что это будет функцией размера ваших ndframes, их количества. Он действительно создает новый фрейм данных для каждого ndframe, поэтому я полагаю, что он намного менее эффективен, чем pd.concat .

Он отлично работает для моих целей, но я использую небольшое количество ndframes (около 10 1 ) и относительно небольшое количество records для каждого ndframe (около 10 2 ).

Моя цель - включить все записи из каждого фрейма данных при сохранении порядка этих записей, даже если не все ndframes содержат данные для данной записи.

Я не понимаю, почему сохранение порядка столбцов (насколько это возможно) не является поведением concat () по умолчанию.

В моем обходном пути используется unique_everseen из рецептов Itertools .

columns = unique_everseen([column for df in dfs for column in df.columns])
df = pd.concat(dfs)[columns]

Есть ли обновления в статусе этой ветки? В настоящее время я использую версию 0.22.0, и, похоже, до сих пор нет подходящего решения. Промедление здесь кажется серьезной проблемой ...

Я также хотел бы отметить, что аналогичное поведение может быть обнаружено при объединении столбцов, то есть axis=1 , но только при передаче фреймов данных в словаре:

>>> df4a = DataFrame(columns=['C','B','D','A'], data=np.random.randn(3,4))
>>> df4b = DataFrame(columns=['C','B','D','A'], data=np.random.randn(3,4))
>>> df5  = DataFrame(columns=['C','B','E','D','A'], data=np.random.randn(3, 5))

>>> pd.concat([df4a, df5], axis=1).columns
Index(['C', 'B', 'D', 'A', 'C', 'B', 'E', 'D', 'A'], dtype='object')
>>> pd.concat({'df4a': df4a, 'df4b': df4b}, axis=1).columns.levels
FrozenList([['df4a', 'df4b'], ['C', 'B', 'D', 'A']])
>>> pd.concat({'df4a': df4a, 'df5': df5}, axis=1).columns.levels
FrozenList([['df4a', 'df5'], ['A', 'B', 'C', 'D', 'E']])

Есть ли обновления в статусе этой ветки?

Все еще открыт.

Промедление здесь кажется серьезной проблемой ...

Прокрастинация? У нас много нерешенных вопросов. Если вы хотите, чтобы это было исправлено в следующем выпуске, лучше всего составить PR. Сообщите нам, если вам понадобится помощь в начале работы.

@jtratner : если это не было очевидно из моих примеров вверху, я бы ожидал, что порядок будет следующим:

  • общие столбцы, несортированные
  • столбцы, уникальные для df1, несортированные (т. е. в том порядке, в котором они встречаются в df1)
  • столбцы, уникальные для df2, несортированные (т. е. в том порядке, в котором они встречаются в df2)

Это то, что вы получаете в других пакетах или языках, например, в SQL. Никогда не должно быть нежелательной автоматической сортировки. Если пользователь хочет отсортировать имена столбцов, позвольте ему сделать это вручную.

Привет, ребята, 2 вещи. 1) Добро пожаловать в панды! Я предлагаю просто использовать больше нативных типов Python, таких как словари. Прекратите пытаться превратить Python (или любой другой язык) в SQL. 2) Технически это не ошибка. Это просто нежелательный эффект кода. Вы можете легко преодолеть это вне контекста пакета, и это то, что я считаю правильным ответом, если только кто-то здесь не возьмет его на себя.

@ MikeTam1021, пожалуйста, объясните, как решить эту

Я почти уверен, что это именно то, что обсуждали в этой ветке. Выше я вижу множество хороших решений, которые должны сработать.

@ MikeTam1021 Речь идет не о превращении панд в SQL (не дай

Никогда не должно быть нежелательной автоматической сортировки. Если пользователь хочет отсортировать имена столбцов, позвольте ему сделать это вручную.

Объединение DataFrames должно иметь тот же эффект, что и «запись их рядом друг с другом», и такая неявная сортировка определенно нарушает принцип наименьшего удивления.

Я согласен. Не должно. Он также предполагает порядок столбцов, который является SQLish, а не чисто информатикой. Вы действительно должны знать, где находятся ваши данные.

Я почти не использую pandas после того, как обнаружил эту и многие другие проблемы. Это сделало меня лучшим программистом.

+1 по этому поводу

Это работает для меня:

cols = list(df1)+list(df2)
df1 = pd.concat([df1, df2])
df1 = df1.loc[:, cols]

Я должен жаловаться на то, как выкатывается этот патч. Вы одновременно изменили сигнатуру функции concat И ввели предупреждение об использовании. Все в одном коммите.

Проблема в том, что мы используем панды на нескольких серверах и не можем гарантировать, что на всех серверах всегда будет одна и та же версия панд. Итак, теперь у нас меньше технических пользователей, которые видят предупреждения от программ, которые они никогда раньше не видели, и не уверены, является ли предупреждение признаком проблемы.

Я могу легко определить, ОТКУДА появляется предупреждение, но я не могу добавить ни один из предложенных вариантов, потому что это нарушит работу программы на любом сервере, на котором работает более старая версия pandas.

Было бы предпочтительнее, если бы вы поместили возможность сортировки в 0,23 и добавили предупреждение в более позднюю версию. Я знаю, что это больно, но довольно неприятно предполагать, что пользователи могут немедленно обновить все развертывания до последнего кода.

Похоже, вы можете просто установить глобальный фильтр для этого предупреждения, а затем
брось это, когда все будут обновлены.

Функционально это то же самое, правда?

Четверг, 4 октября 2018 г., в 9:18 DavidEscott [email protected] написал:

Я должен жаловаться на то, как выкатывается этот патч. У вас есть
одновременно изменил сигнатуру функции concat И ввел
предупреждение об использовании. Все в одном коммите.

Проблема в том, что мы используем панды на нескольких серверах и не можем
гарантировать, что на всех серверах есть одна и та же версия панд на всех
раз. Так что теперь у нас меньше технических пользователей, которые видят предупреждения от программ.
они никогда раньше не видели и не уверены, является ли предупреждение признаком
проблема.

Я легко могу определить, ОТКУДА появляется предупреждение, но не могу добавить
любой из предложенных вариантов, потому что это нарушит работу программы на любом
сервер под управлением более старой версии pandas.

Было бы предпочтительнее, если бы вы поместили возможность сортировки в
0.23, и добавили предупреждение в некоторые более поздние версии. Я знаю, что это боль, но
довольно неприятно предполагать, что пользователи могут сразу обновить все
развертывания до последней версии кода.

-
Вы получаете это, потому что изменили состояние открытия / закрытия.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/pandas-dev/pandas/issues/4588#issuecomment-427036391 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/ABQHItEhYfv5kqB-R-pDX4zyIh45hF7kks5uhhiWgaJpZM4A6TeA
.

@TomAugspurger Мы на нашей стороне можем решить эту

  1. Мне пришлось бы добавить фильтр к нескольким программам
  2. Не лучший способ указать конкретное предупреждение для фильтрации:

    • Я могу фильтровать по модулям и лентам, но это не стабильная ссылка,

    • Я могу фильтровать по модулю и FutureWarning но тогда я бы вообще не получал никаких предупреждений от панд и был бы удивлен другими изменениями,

    • или я могу отфильтровать ваше длинное многострочное сообщение

  3. И затем не забудьте снять этот фильтр, когда все будет обновлено, и это больше не имеет значения.

В любом случае недостатки модуля warnings , конечно же, не являются чем-то, что я могу положить в основу команды pandas.

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

Мне просто кажется немного странным, что вы были так обеспокоены возможным изменением поведения конечных элементов, видимых пользователем, что добавили эту опцию сортировки к тому, что ранее было недоопределенным API, и при этом одновременно выдали предупреждение программисту ... и то, и другое. предупреждение и предлагаемое изменение в поведении сортировки составляют в моей книге «поведение, видимое пользователем», только разной степени серьезности.

Я ответил на связанный с этим вопрос о SO.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги