При объединении 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
``'
Глядя на это вкратце, я _ думаю_, что это происходит из 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 : если это не было очевидно из моих примеров вверху, я бы ожидал, что порядок будет следующим:
Это то, что вы получаете в других пакетах или языках, например, в 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 Мы на нашей стороне можем решить эту
FutureWarning
но тогда я бы вообще не получал никаких предупреждений от панд и был бы удивлен другими изменениями,В любом случае недостатки модуля warnings
, конечно же, не являются чем-то, что я могу положить в основу команды pandas.
Вы также не виноваты в том, что у нас есть старый сервер, который мы не можем легко обновить, так что это было бы еще одной вещью, которую я могу сделать (просто обновите все проклятые развертывания). В конечном итоге я осознаю, что должен это сделать, и что я обязан постараться сохранить наши развертывания близко друг к другу.
Мне просто кажется немного странным, что вы были так обеспокоены возможным изменением поведения конечных элементов, видимых пользователем, что добавили эту опцию сортировки к тому, что ранее было недоопределенным API, и при этом одновременно выдали предупреждение программисту ... и то, и другое. предупреждение и предлагаемое изменение в поведении сортировки составляют в моей книге «поведение, видимое пользователем», только разной степени серьезности.
Я ответил на связанный с этим вопрос о SO.
Самый полезный комментарий
Это действительно довольно неожиданное поведение, и я тоже наткнулся на него.
Наивно можно было бы ожидать, что порядок столбцов сохранится. Вместо этого столбцы сортируются:
Это можно исправить, переиндексировав исходные столбцы следующим образом:
Тем не менее кажется нелогичным, что эта автоматическая сортировка имеет место и не может быть отключена, насколько мне известно.