Bagaimana Anda membaca file besar dengan data tabel yang tidak disortir dalam potongan dengan Python?

Dec 21 2020

Saya memiliki file CSV besar (> 100 GB) yang ingin saya baca ke dalam memori dan memproses data dalam potongan. Ada dua kendala yang saya miliki:

  1. Jelas saya tidak bisa membaca seluruh file ke dalam memori. Saya hanya memiliki sekitar 8GB ram di mesin saya.
  2. Data berbentuk tabel dan tidak berurutan. Saya perlu membaca data dalam kelompok.
Jantung Tanggal Bidang1 Bidang2 Bidang3
AAPL 20201201 0 0 0
AAPL 20201202 0 0 0
AAPL 20201203 0 0 0
AAPL 20201204 0 0 0
NFLX 20201201 0 0 0
NFLX 20201202 0 0 0
NFLX 20201203 0 0 0
NFLX 20201204 0 0 0

Perhatian di sini adalah bahwa data harus dibaca dalam kelompok. Dikelompokkan berdasarkan Ticker dan tanggal. Jika saya mengatakan saya ingin membaca 10.000 rekaman di setiap batch. Batas kelompok itu tidak boleh memisahkan kelompok. yaitu Semua data AAPL untuk Desember 2020 harus berakhir di batch yang sama. Data itu tidak boleh muncul dalam dua kelompok.

Sebagian besar rekan kerja saya ketika menghadapi situasi seperti ini, mereka biasanya membuat skrip bash di mana mereka menggunakan awk, cut, sort, uniq untuk membagi data ke dalam grup dan menulis beberapa file perantara ke disk. Kemudian mereka menggunakan Python untuk memproses file-file ini. Saya bertanya-tanya apakah ada solusi Python / Pandas / Numpy yang homogen untuk ini.

Jawaban

genodeftest Dec 21 2020 at 03:11

Bagaimana dengan ini:

  1. buka file tersebut
  2. loop di atas garis bacaan: Untuk setiap baris, baca:
  • parsing ticker
  • jika belum selesai:
    • buat + buka file untuk ticker itu (" file ticker ")
    • tambahkan ke beberapa dict di mana key = ticker dan value = file handle
  • tulis baris tersebut ke file ticker
  1. tutup file ticker dan file aslinya
  2. memproses setiap file ticker
Martin Dec 21 2020 at 03:44

Saya akan melihat ke dalam dua opsi

Vaex dan Dask.

Vaex tampaknya berfokus pada apa yang Anda butuhkan. Pemrosesan yang lambat dan kumpulan data yang sangat besar. Periksa github mereka. Namun tampaknya, Anda perlu mengonversi file ke hdf5, yang mungkin memakan sedikit waktu.

Sejauh menyangkut Dask, saya tidak akan mengandalkan kesuksesan. Dask terutama difokuskan pada komputasi terdistribusi dan saya tidak begitu yakin apakah Dask dapat memproses file besar dengan malas. Tetapi Anda dapat mencoba dan melihat.

tgrandje Dec 23 2020 at 03:05

Pendekatan ini murni panda. Ini akan menggunakan dua fungsi: satu untuk menghitung indeks, satu untuk membaca satu bagian. Saya akan mengatakan itu akan gagal sama sekali jika salah satu grup Anda tidak sesuai dengan ingatan (tetapi mengingat kriteria Anda bahwa grup itu harus dibaca satu per satu, saya akan mengatakan itu akan menjadi tebakan yang pasti cocok).

Anda perlu mengulang kamus indeks (seperti yang dihitung dari fungsi pertama) untuk membaca seluruh kerangka data.

Harapan itu akan membantu ... (Jangan ragu untuk menyesuaikan nilai default chunksize dengan kebutuhan Anda).

import pandas as pd

def compute_indexes(url, cols_indexes=[], chunksize=100000, **kwargs):
    """
    Returns a dictionnary
    Keys are the pseudo indexes of the dataframe 
    Values are lists of indexes corresponding to this index
    """
    iterator = pd.read_csv(
            url, 
            usecols=cols_indexes, 
            chunksize=chunksize,
            **kwargs)
    
    dict_groups = dict()
    for df in iterator:
        groups_present = df.drop_duplicates(keep="first").values.tolist()
        df.reset_index(drop=False, inplace=True)
        df.set_index(cols_indexes, inplace=True)
        for group in groups_present:
            group = tuple(group)
            if group not in dict_groups:
                dict_groups[group] = []
            try:
                dict_groups[group] += df.loc[group]['index'].tolist()
            except TypeError:
                #only one row
                dict_groups[group] += [df.loc[group]['index']]
                
    return dict_groups

def read_csv_group(url, dict_groups, which_group, **kwargs):
    if isinstance(which_group, list):
        which_group = tuple(which_group)
    rows = dict_groups[which_group]
    def skip_rows(x):
        if x == 0:
            return False
        elif x in {x+1 for x in rows}:
            return False
        else:
            return True
    df = pd.read_csv(url, skiprows=skip_rows, **kwargs)
    return df
    
URL = "./dummy.csv"
indexes = ['Ticker', 'Date']
kwargs = {'dtype':{'Ticker':str, 'Date':int})
dict_groups = compute_indexes(URL, indexes, chunksize=100000, **kwargs)
df_one_group = read_csv_group(URL, dict_groups, ('AAPL', 20201201), **kwargs)