Объединение нескольких фреймов данных вместе, которые имеют разные столбцы, кроме 5

#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. Узким местом могут быть процессы ввода-вывода на диске.