Загрузить фрейм данных pandas с размером блока, определяемым переменной столбца

Если у меня есть файл csv, который слишком велик для загрузки в память с помощью pandas (в данном случае 35 ГБ), я знаю, что можно обрабатывать файл кусками с размером chunksize.

Однако я хочу знать, можно ли изменить размер фрагмента на основе значений в столбце.

У меня есть столбец идентификатора, а затем несколько строк для каждого идентификатора с информацией, например:

ID,   Time,  x, y
sasd, 10:12, 1, 3
sasd, 10:14, 1, 4
sasd, 10:32, 1, 2
cgfb, 10:02, 1, 6
cgfb, 10:13, 1, 3
aenr, 11:54, 2, 5
tory, 10:27, 1, 3
tory, 10:48, 3, 5
ect...

Я не хочу разделять идентификаторы на разные части. например, будут обработаны фрагменты размером 4:

ID,   Time,  x, y
sasd, 10:12, 1, 3
sasd, 10:14, 1, 4
sasd, 10:32, 1, 2
cgfb, 10:02, 1, 6
cgfb, 10:13, 1, 3 <--this extra line is included in the 4 chunk

ID,   Time,  x, y
aenr, 11:54, 2, 5
tory, 10:27, 1, 3
tory, 10:48, 3, 5
...

Является ли это возможным?

Если нет, возможно, использовать библиотеку csv с циклом for по строкам:

for line in file:
    x += 1
    if x > 1000000 and curid != line[0]:
        break
    curid = line[0]
    #code to append line to a dataframe

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


person Josh Kidd    schedule 14.02.2017    source источник
comment
Где хранится ваш файл? Один из способов - предварительно отфильтровать ID (возможно, если вы извлекаете извлечение из базы данных, используя SELECT * FROM ... WHERE ID == BLAH), создавая разные файлы для каждого уникального значения ID.   -  person blacksite    schedule 14.02.2017
comment
К сожалению, это невозможно, у меня нет доступа к базе данных   -  person Josh Kidd    schedule 14.02.2017
comment
Это может быть PITA, но я думаю, что это сработает: что, если вы попробуете использовать chunksize прямо сейчас, потоковое воспроизведение всего файла размером 35 ГБ и создание отдельного CSV для каждого уникального значения ID (set(df['ID']))? Затем для каждой строки в вашем большом файле вы записываете (читаете: добавляете) эту строку в существующий файл идентификатора, соответствующий идентификатору этой строки? Кодировать было бы несложно, хотя я сомневаюсь, сколько времени это может занять ... Просто мысль! В противном случае, боюсь, я ничем не смогу помочь.   -  person blacksite    schedule 14.02.2017


Ответы (2)


Если вы перебираете файл csv построчно, вы можете yield фрагментов с генератором, зависящим от любого столбца.

Рабочий пример:

import pandas as pd

def iter_chunk_by_id(file):
    csv_reader = pd.read_csv(file, iterator=True, chunksize=1, header=None)
    first_chunk = csv_reader.get_chunk()
    id = first_chunk.iloc[0,0]
    chunk = pd.DataFrame(first_chunk)
    for l in csv_reader:
        if id == l.iloc[0,0]:
            id = l.iloc[0,0]
            chunk = chunk.append(l)
            continue
        id = l.iloc[0,0]
        yield chunk
        chunk = pd.DataFrame(l)
    yield chunk

## data.csv ##
# 1, foo, bla
# 1, off, aff
# 2, roo, laa
# 3, asd, fds
# 3, qwe, tre
# 3, tre, yxc   

chunk_iter = iter_chunk_by_id("data.csv")

for chunk in chunk_iter:
    print(chunk)
    print("_____")

Выход:

   0     1     2
0  1   foo   bla
1  1   off   aff
_____
   0     1     2
2  2   roo   laa
3  2   jkl   xds
_____
   0     1     2
4  3   asd   fds
5  3   qwe   tre
6  3   tre   yxc
_____
person elcombato    schedule 14.02.2017
comment
Таким образом, это создаст фрагмент для каждого идентификатора, как мне вместо этого создать фрагменты большего размера размером 1000000, а затем добавить оставшиеся строки с тем же идентификатором, чтобы гарантировать, что строки с одним и тем же идентификатором не находятся в отдельных фрагментах? - person Josh Kidd; 17.02.2017
comment
@JoshuaKidd вы поняли функцию iter_chunk_by_id()? Вы можете легко изменить его, чтобы он соответствовал вашему новому запросу: добавьте условие or в строку if id == l.iloc[0,0], чтобы продолжить добавление строк в блок, если длина блока меньше 1000000 (кстати: почему вы удалили мой ответ как принятый? ) - person elcombato; 20.02.2017

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

def iter_chunk_by_id(file_name, chunk_size=10000):
"""generator to read the csv in chunks of user_id records. Each next call of generator will give a df for a user"""

csv_reader = pd.read_csv(file_name, compression='gzip', iterator=True, chunksize=chunk_size, header=0, error_bad_lines=False)
chunk = pd.DataFrame()
for l in csv_reader:
    l[['id', 'everything_else']] = l[
        'col_name'].str.split('|', 1, expand=True)
    hits = l['id'].astype(float).diff().dropna().nonzero()[0]
    if not len(hits):
        # if all ids are same
        chunk = chunk.append(l[['col_name']])
    else:
        start = 0
        for i in range(len(hits)):
            new_id = hits[i]+1
            chunk = chunk.append(l[['col_name']].iloc[start:new_id, :])
            yield chunk
            chunk = pd.DataFrame()
            start = new_id
        chunk = l[['col_name']].iloc[start:, :]

yield chunk
person Fizi    schedule 26.10.2019