Страницы

Поиск по вопросам

четверг, 2 января 2020 г.

Чтение и перезапись больших файлов csv

#python #pandas #dataframe #csv


Прошу совета. У меня есть большое число csv файлов с данными по котировкам (180 Гб),
Вкаждом файле лежат данные за 1 день по типу ticker, цена1, цена2.
Пример: за 13082008.csv лежит:

A 1 2 3 4
A 2 2 2 2
A 3 4 5 6
B 1 1 1 1
B 2 3 3 3
C 1 1 1 1
C 1 1 1 1


Я хочу перенести данные из этих файлов в новые CSV так, чтобы у меня лежали данные
по каждому тикеру. То есть по примеру у меня создадутся файлы A.csv B.csv C.csv. У
меня есть решение, но оно довольно медленное, я открываю 13082008.csv, вписываю его
в датафрейм, прохожусь по строкам и записываю каждую строку в нужный файл. 

Не посоветуете решение, которое будет намного быстрее? 

Пример моего кода:

def read_and_rewrite_options(path_out, folder, options):

import pandas as pd
import os
import csv   
header = ['UnderlyingSymbol', 'UnderlyingPrice', 'Exchange', 'OptionSymbol', 'OptionExt',
'Type', 'Expiration', 'DataDate', 
                  'Strike', 'Last', 'Bid', 'Ask', 'Volume', 'OpenInterest', 'IV',
'Delta', 'Gamma', 'Theta', 'Vega', 'AKA']
print('folder: ' + folder)
for ops in options:
    data = read_l2_file(path_out + folder + '/' + ops)
    for index, row in data.iterrows():
        #print(row['UnderlyingSymbol'])
        path = path_in + options_folder + 'options_' + row['UnderlyingSymbol'] + '.csv'
                  # df = df.append({'symbol' : row['symbol'], 'quotedate' : row['quotedate'],
'open' : row['open'] , 'high' : row['high'], 
       #                 'low' : row['low'], 'close' : row['close'], 'volume' : row['volume'], 
       #                 'adjustedclose' : row['adjustedclose']}, ignore_index = True)
        if row['UnderlyingSymbol'] + '.csv' not in os.listdir(path_in + options_folder):    
            with open(path, 'a', newline='') as f:
                writer = csv.writer(f)
                writer.writerow(header)
                writer.writerow(row)
        else:
             with open(path, 'a', newline='') as f:
                writer = csv.writer(f)
                writer.writerow(row)
    print('options by ' + ops + ' are written')
print('---------------------------------------------')


(Тут считываю данные за месяц)
    


Ответы

Ответ 1



Мне кажется @Akina дал очень дельный совет - можно сохранить все данные в одну SQL таблицу с индексами ("date", "symbol") и далее выбирать по индексу нужные данные. Это можно сделать приблизительно так: import pandas as pd from sqlalchemy import create_engine from pathlib import Path engine = create_engine('sqlite:///c:/temp/stock.db') conn = engine.connect() path = Path(r"/path/to/data_directory") for f in path.glob("*.csv"): df = pd.read_csv(f, ...) df.set_index(["Date", "Symbol"]).to_sql("stocks", conn, if_exists="append", index=True) после этого вы можете читать данные следующим образом: df = pd.read_sql("select * from stocks where symbol == 'AAPL'", conn) PS вместо SQLite можно использовать любую БД, которую поддерживает SQL Alchemy. рабочий пример с данными из Yahoo Finance: import pandas as pd import pandas_datareader.data as web from sqlalchemy import create_engine engine = create_engine('sqlite:///c:/temp/stocks.db') conn = engine.connect() f = web.DataReader(['IBM', 'GOOG', 'AAPL'], 'yahoo', '2001-01-01', '2019-01-01') получим следующий датасет: In [42]: f.stack() Out[42]: Attributes High Low Open Close Volume Adj Close Date Symbols 2001-01-02 AAPL 1.089286 1.040179 1.062500 1.062500 113078000.0 0.930781 IBM 87.500000 84.125000 84.500000 84.812500 8007200.0 57.459831 2001-01-03 AAPL 1.191964 1.031250 1.035714 1.169643 204268400.0 1.024641 IBM 95.000000 83.750000 83.750000 94.625000 12769900.0 64.107704 2001-01-04 AAPL 1.321429 1.200893 1.295759 1.218750 184849000.0 1.067660 IBM 99.750000 92.687500 94.750000 93.187500 15420500.0 63.133808 2001-01-05 AAPL 1.241071 1.147321 1.209821 1.169643 103089000.0 1.024641 ... ... ... ... ... ... ... 2018-12-27 IBM 113.779999 109.470001 109.989998 113.779999 6045600.0 111.149292 2018-12-28 AAPL 158.520004 154.550003 157.500000 156.229996 42291400.0 154.966034 GOOG 1055.560059 1033.099976 1049.619995 1037.079956 1414800.0 1037.079956 IBM 114.800003 112.500000 114.220001 113.029999 5117500.0 110.416634 2018-12-31 AAPL 159.360001 156.479996 158.529999 157.740005 35003500.0 156.463837 GOOG 1052.699951 1023.590027 1050.959961 1035.609985 1493300.0 1035.609985 IBM 114.349998 112.419998 113.330002 113.669998 4982800.0 111.041840 запишем его в SQLite DB: f.stack().to_sql("stock", conn, if_exists="append", index=True) conn.close() выбираем данные по индексу из БД: engine = create_engine('sqlite:///c:/temp/stocks.db') conn = engine.connect() df = pd.read_sql("select * from stock where symbols == 'AAPL' and date between '2018-01-01' and '2018-12-31'", conn) результат: In [46]: df Out[46]: Date Symbols High Low Open Close Volume Adj Close 0 2018-01-02 00:00:00.000000 AAPL 172.300003 169.259995 170.160004 172.259995 25555900.0 168.339050 1 2018-01-03 00:00:00.000000 AAPL 174.550003 171.960007 172.529999 172.229996 29517900.0 168.309738 2 2018-01-04 00:00:00.000000 AAPL 173.470001 172.080002 172.539993 173.029999 22434600.0 169.091522 3 2018-01-05 00:00:00.000000 AAPL 175.369995 173.050003 173.440002 175.000000 23660000.0 171.016678 4 2018-01-08 00:00:00.000000 AAPL 175.610001 173.929993 174.350006 174.350006 20567800.0 170.381485 5 2018-01-09 00:00:00.000000 AAPL 175.059998 173.410004 174.550003 174.330002 21584000.0 170.361954 6 2018-01-10 00:00:00.000000 AAPL 174.300003 173.000000 173.160004 174.289993 23959900.0 170.322845 .. ... ... ... ... ... ... ... ... 243 2018-12-19 00:00:00.000000 AAPL 167.449997 159.089996 166.000000 160.889999 49047300.0 159.588348 244 2018-12-20 00:00:00.000000 AAPL 162.110001 155.300003 160.399994 156.830002 64773000.0 155.561188 245 2018-12-21 00:00:00.000000 AAPL 158.160004 149.630005 156.860001 150.729996 95744600.0 149.510544 246 2018-12-24 00:00:00.000000 AAPL 151.550003 146.589996 148.149994 146.830002 37169200.0 145.642090 247 2018-12-26 00:00:00.000000 AAPL 157.229996 146.720001 148.300003 157.169998 58582500.0 155.898438 248 2018-12-27 00:00:00.000000 AAPL 156.770004 150.070007 155.839996 156.149994 53117100.0 154.886688 249 2018-12-28 00:00:00.000000 AAPL 158.520004 154.550003 157.500000 156.229996 42291400.0 154.966034 [250 rows x 8 columns]

Ответ 2



Решение в лоб через grep. Бысрее наверное только в индексируемой БД: grep -rh "^A" /path/with/csv/ > A.csv

Ответ 3



Я не настоящий сварщик, но если условие без использования БД, вот мой вариант фильтрации силами только pandas: import pandas as pd if __name__ == '__main__': files = ['1.csv', '2.csv', ...] chunksize = 10 ** 6 for file in files: for chunk in pd.read_csv(file, chunksize=chunksize, header=None, delimiter=' ', names=['ticker', 'num1', 'num2', 'num3', 'num4']): tickers = chunk.ticker.unique() for ticker in tickers: target = chunk[chunk.ticker == ticker] target.to_csv(f'out/{ticker}.csv', mode='a', header=False, index=False, sep=' ') За исходный формат взят формат файла в вопросе. chunksize настраивается в зависимости от доступной памяти, можно и без него, если все исходные файлы небольшие по размеру. Тогда просто chunk = pd.read_csv(...) и дальше по коду без изменений.

Комментариев нет:

Отправить комментарий