#python #pandas #dataframe
#python #панды #фрейм данных
Вопрос:
Я помещаю 1424 фрейма данных в список следующим образом:
import os
df = []
i = 0
for filename in os.listdir(output_path):
if filename.endswith(".csv"):
df.append(pd.read_csv(os.path.join(output_path, filename)))
else:
continue
Я хочу объединить их все вместе. Вот пример, который я хочу эмулировать, используя только 2 dfs:
df1 = pd.read_csv('../output/2009/census_data_2009_p1.csv')
df2 = pd.read_csv('../output/2009/census_data_2009_p2.csv')
df1 = df1.merge(df2, how = 'left', on = ['Location Type', 'Year', 'state', 'Census_tract', 'County_name'])
Как бы я сделал последнее, но для всех из них в списке df? В частности, я хочу выполнить левое соединение для всех фреймов данных с помощью ключей 'Location Type', 'Year', 'state', 'Census_tract', 'County_name'
В настоящее время я получаю эту ошибку, хотя у меня 64 ГБ оперативной памяти.
The kernel appears to have died. It will restart automatically.
Это происходит, когда я либо запускаю этот код:
from functools import reduce
df_merged = reduce(lambda l, r: pd.merge(l, r,
how='left',
on=['Location Type',
'Year',
'state',
'Census_tract',
'County_name']), dfs)
или этот код
[
dfi.set_index(
["Location Type", "Year", "state", "Census_tract", "County_name"], inplace=True
)
for dfi in df
]
df[0].join(df[1:], how="left")
Комментарии:
1. Пожалуйста, покажите нам некоторые примеры данных с ожидаемым результатом и расскажите нам, почему
pd.concat
, как описано в ответе ниже, не сработало.
Ответ №1:
Попробуйте использовать set index и join:
[
dfi.set_index(
["Location Type", "Year", "state", "Census_tract", "County_name"], inplace=True
)
for dfi in df
]
df[0].join(df[1:], how="left")
Комментарии:
1. Где находится dfi в соединении?
2. Кроме того, я получаю эту ошибку: ядро, похоже, умерло. Он автоматически перезапустится. Я предполагаю, что мне может понадобиться больше оперативной памяти, даже если у меня 64 ГБ?
3. Похоже, мы копаемся в вашей памяти на вашем компьютере.
4. попробуйте использовать примерно 10 фреймов данных, если это сработает, увеличивайте его, пока не получите сообщение об ошибке. Может быть, сделать два меньших соединения, а затем соединить две части вместе.
Ответ №2:
Я полагаю, что одним из самых чистых вариантов является сопоставление операции слияния с использованием reduce
:
from functools import reduce
df_merged = reduce(lambda l, r: pd.merge(l, r,
how='left',
on=['Location Type',
'Year',
'state',
'Census_tract',
'County_name']), df)
Однако предполагается, что фреймы данных отсортированы желаемым образом.
Более эффективный с точки зрения памяти способ сделать это (но, возможно, менее чистый) — просто перебирать фреймы данных:
df_merged = df[0].copy() # we use the initial dataframe to start
del df[0]
for _ in range(len(df)):
df_merged = df_merged.merge(df[0],
how='left',
on=['Location Type',
'Year',
'state',
'Census_tract',
'County_name'])
# this will free up the contents of the recently merged dataframe
del df[0]
Комментарии:
1. Что вы подразумеваете под сортировкой желаемым образом?
2. Кроме того, SyntaxError: позиционный аргумент следует за аргументом ключевого слова
3. Я отредактировал свой ответ, чтобы исправить синтаксическую ошибку. Я также добавил еще один вариант (возможно, менее симпатичный), который должен занимать меньше памяти для запуска. Сортируя желаемым образом, я имею в виду, что порядок объединения по левому краю будет иметь значение (к слиянию будут добавлены только строки, соответствующие тому, что уже было объединено)
4. Чтобы немного расширить проблему порядка. Например, если в первом фрейме данных у вас нет ВСЕХ комбинаций клавиш, которые вы включаете в
on
параметр, то вы можете потерять некоторые строки, которые входят в последующие наборы данных. Именно в этом смысле порядок имеет значение.5. Да, вы можете проверить это, используя аналогичный цикл for в обычном списке
a = list(range(10))
, а затем (в цикле for, аналогичном тому, что приведен в моем ответе) выполните:print(a[0]) del a[0]
Ответ №3:
Во-первых, небольшая очистка в первом блоке кода:
import os
dfs = []
for filename in os.listdir(output_path):
if filename.endswith(".csv"):
dfs.append(pd.read_csv(os.path.join(output_path, filename)))
Для объединения списка фреймов данных в один фрейм данных:
pd.concat(dfs, join='inner')
join='inner'
Выбирает только общие столбцы из списка фреймов данных.
Короткая демонстрация:
df1 = pd.DataFrame(data=[[1,2,3], [2,3,1]], columns=['a', 'b', 'c'])
a b c
0 1 2 3
1 2 3 1
df2 = pd.DataFrame(data=[[1,2,3], [2,3,1]], columns=['b', 'c', 'd'])
b c d
0 1 2 3
1 2 3 1
pd.concat([df1, df2], join='inner')
b c
0 2 3
1 3 1
0 1 2
1 2 3
Обратите внимание на результирующий индекс. При необходимости вы можете использовать reset_index()
для сброса индекса.
Комментарии:
1. Спасибо за пример, но мне понадобились бы не только общие столбцы, но и все столбцы из каждого фрейма данных. Отсюда и выполнение левого соединения для общих имен столбцов.
2. Поможет ли объединение с использованием «внешнего», а затем удаление дубликатов?
3. Просто чтобы уточнить, вы не хотели объединять (по строкам) все фреймы данных вместе?
4. Нет, я хочу выполнить левое соединение для всех фреймов данных, поскольку все они имеют уникальные столбцы, за исключением 5, которые я перечислил выше.
5. Ваш вопрос мне непонятен.
merge()
может иметь дело только с двумя объектами фрейма данных, т. Е. Слева и справа. Левое соединение может создать больше строк, чем в левой таблице. Можете ли вы привести несколько примеров ваших таблиц?
Ответ №4:
# For the columns in the variable
columns = ['Location Type', 'Year', 'state', 'Census_tract', 'County_name']
# Set the indexes in order to join by index
for my_df in df:
my_df.set_index(columns)
# Join the dataframe
res_df = df[0]
for index in range(1, len(df)):
res_df.join(df[index], how='outer')
# Or if you only want to merge
pd.concat(df, join='outer')
Ответ №5:
Прежде всего, я бы использовал generator для сохранения памяти вашей машины время выполнения может быть больше, но ваша машина будет обрабатывать только один файл за раз
import os
import pandas as pd
def census_dataframes():
for filename in os.listdir(output_path):
if filename.endswith(".csv"):
yield pd.read_csv(os.path.join(output_path, filename))
else:
continue
dataframes = census_dataframes()
#get first dataframe from generator
df1 = next(dataframes)
for frame in dataframes:
df1 = df1.merge(frame, how = 'left', on = ['Location Type', 'Year', 'state', 'Census_tract', 'County_name'])
Если описанный выше подход не дает результата, пожалуйста, проверьте размер вашего выходного фрейма данных.
Для эффективного использования вам нужно как минимум в 2 раза больше памяти, чем требуется вашему фрейму данных.
Дальнейшая экономия памяти может быть достигнута за счет оптимизации типов данных во время чтения csv, например
yield pd.read_csv(os.path.join(output_path, filename), dtype= {‘a’: np.float32, ‘b’: np.int32, ‘c’: ....})
если у вас есть текстовые записи, которые часто повторяются в столбце (например, «Мужчина», «Женщина», «Не раскрыто», …), вы можете преобразовать их в категории и сэкономить значительный объем памяти. Однако для выполнения этого с большим количеством файлов требуется предварительная подготовка, предварительное определение категорий.
Пожалуйста, обратитесь к документации pandas по теме «Категориальные данные»
Комментарии:
1. Спасибо, я попробовал код, но он все равно выдал мне «Ядро, похоже, умерло. Он автоматически перезапустится «. Файлы составляют 5,7 ГБ памяти, и у меня 64 ГБ оперативной памяти на этом компьютере (4 16 ГБ DDR4)
2. Если ваше ядро продолжает умирать, попробуйте использовать чистый python за пределами notebook. Это может обеспечить большую стабильность. b / при использовании метода генератора вы можете сохранять промежуточные результаты каждые N файлов и перезапускать процесс с момента его последней контрольной точки. Это займет значительно больше времени.
3. Возможно ли распараллелить последнее выше?
4. Да, это возможно с использованием стандартных библиотек python. Узким местом могут быть процессы ввода-вывода на диске.