Как преобразовать несколько pandas фреймов данных в массив в ограничениях памяти? - PullRequest
0 голосов
/ 25 февраля 2020

Данная проблема: у меня есть папки с именами от folder1 до folder999. В каждой папке есть паркетные файлы - от 1.parquet до 999.parquet. Каждый паркет состоит из pandas данных с заданной структурой:

id   |title   |a
1    |abc     |1
1    |abc     |3
1    |abc     |2
2    |abc     |1
...  |def     | ...

Где столбец a может быть значением диапазона от a1 до a3.

Частичное 1009 * шаг для получения структуры:

id | title | a1 | a2 | a3
1  | abc   | 1  | 1  | 1
2  | abc   | 1  | 0  | 0
...

Для получения окончательной формы:

    title
id | abc | def | ...
1  | 3   | ... |
2  | 1   | ... |

где значения столбца ab c - это сумма столбцов a1, a2 и a3.

Цель состоит в том, чтобы получить окончательную форму, рассчитанную на все файлы паркета во всех папках.

Теперь ситуация, в которой я нахожусь теперь выглядит примерно так: я знаю, как получить окончательную форму частичным шагом, например, с помощью sparse.coo_matrix (), как описано в Как сделать полную матрицу из плотного pandas dataframe .

Проблема в том, что из-за ограничений памяти я не могу просто прочитать все паркет за раз.

У меня есть три вопроса:

  1. Как эффективно добраться, если у меня много данных (предположим, что каждый файл паркета состоит из 500 МБ)?

  2. Могу ли я преобразовать каждый паркет в окончательную форму по отдельности, а затем каким-то образом объединить их? Если да, как я могу это сделать?

  3. Есть ли способ пропустить шаг частичный ?

1 Ответ

1 голос
/ 25 февраля 2020

Для каждого кадра данных в файлах вы, похоже,

  • Группируете данные по столбцам id, title
  • Теперь суммируйте данные в столбце a для каждой группы

Создание полной матрицы для задачи не требуется, так же как и шаг partial.

Я не уверен, сколько уникальных комбинаций id , title существует в файле и / или всех них. Безопасным шагом будет обработка файлов в пакетном режиме, сохранение их результатов и последующее объединение всех результатов

, что похоже на

import pandas as pd
import numpy as np
import string

def gen_random_data(N, M):
    # N = 100
    # M = 10

    titles = np.apply_along_axis(lambda x: ''.join(x), 1, np.random.choice(list(string.ascii_lowercase), 3*M).reshape(-1, 3))
    titles = np.random.choice(titles, N)
    _id = np.random.choice(np.arange(M) + 1, N)
    val = np.random.randint(M, size=(N,))

    df = pd.DataFrame(np.vstack((_id, titles, val)).T, columns=['id', 'title', 'a'])
    df = df.astype({'id': np.int64, 'title': str, 'a': np.int64})

    return df

def combine_results(grplist):
    # stitch into one dataframe
    comb_df = pd.concat(dflist, axis=1)

    # Sum over common axes i.e. id, titles
    comb_df = comb_df.apply(lambda row: np.nansum(row), axis=1)

    # Return a data frame with sum of a's
    return comb_df.to_frame('sum_of_a')

totalfiles = 10
batch      = 2
filelist   = []
for counter,nfiles in enumerate(range(0, totalfiles, batch)):
    # Read data from files. generate random data
    dflist = [gen_random_data(100, 2) for _ in range(nfiles)]

    # Process the data in memory
    dflist = [_.groupby(['id', 'title']).agg(['sum']) for _ in dflist]

    collection = combine_results(dflist)

    # write intermediate results to file and repeat the process for the rest of the files
    intermediate_result_file_name = f'resfile_{counter}'
    collection.to_parquet(intermediate_result_file_name, index=True)
    filelist.append(intermediate_result_file_name)

# Combining result files.
collection = [pd.read_parquet(file) for file in filelist]
totalresult = combine_results(collection)
...