Ulepsz pandy (PyTables?) Wydajność zapisu tabeli HDF5

[[12]] używam pand do badań od około dwóch miesięcy z wielkim skutkiem. Dzięki dużej liczbie średnich zbiorów danych zdarzeń śledzenia, pandas + PyTables (interfejs HDF5) wykonuje ogromną pracę, pozwalając mi przetwarzać heterogeniczne dane za pomocą wszystkich narzędzi Pythona, które znam i kocham.

Ogólnie rzecz biorąc, używam formatu Fixed (dawniej "Storer") w PyTables, ponieważ mój przepływ pracy to write-once, read-many, a wiele moich zbiorów danych jest takich rozmiarów, że mogę załadować 50-100 z nich do pamięć na raz bez poważnych wad. (Uwaga: większość mojej pracy wykonuję na maszynach klasy serwerowej Opteron z pamięcią systemową 128GB+.)

Jednak w przypadku dużych zbiorów danych (500MB i większych), chciałbym móc korzystać z bardziej skalowalnych możliwości dostępu losowego i zapytań formatu Pytables "tabele", tak że mogę wykonywać moje zapytania poza pamięcią, a następnie załadować znacznie mniejszy zestaw wyników do pamięci do przetwarzania. Dużą przeszkodą tutaj jest jednak wydajność pisania. Tak, jak ja powiedział, mój workflow to write-once, read-many, ale względne czasy są nadal nie do przyjęcia.

Jako przykład, niedawno przeprowadziłem dużą faktoryzację Cholesky, która zajęła 3 minuty, 8 sekund (188 sekund) na mojej maszynie z rdzeniem 48. To wygenerowało plik śledzenia ~2.2 GB-ślad jest generowany równolegle z programem, więc nie ma dodatkowego " czasu tworzenia śledzenia."

Początkowa konwersja mojego binarnego pliku śledzenia do formatu pandy / PyTables zajmuje przyzwoity kawałek czasu, ale głównie dlatego, że format binarny jest celowo niezgodny z porządkiem, aby zmniejszyć wpływ samego generatora śladów na wydajność. Jest to również nieistotne dla utraty wydajności podczas przechodzenia z formatu magazynu do formatu tabeli.

Moje testy były początkowo uruchamiane z pandas 0.12, numpy 1.7.1, PyTables 2.4.0 i numexpr 0.20.1. Moja 48-rdzeniowa maszyna pracuje z częstotliwością 2,8 GHz na rdzeń i piszę do systemu plików ext3, który prawdopodobnie (ale nie na pewno) jest na dysku SSD.

I może zapisać cały zestaw danych do pliku HDF5 w formacie Storer (Rozmiar pliku wynikowego: 3,3 GB) W 7,1 sekundy. Ten sam zbiór danych, zapisany do formatu tabeli (wynikowy rozmiar pliku również wynosi 3,3 GB), zajmuje 178,7 sekundy.

Kod jest następujący:

with Timer() as t:
    store = pd.HDFStore('test_storer.h5', 'w')
    store.put('events', events_dataset, table=False, append=False)
print('Fixed format write took ' + str(t.interval))
with Timer() as t:
    store = pd.HDFStore('test_table.h5', 'w')
    store.put('events', events_dataset, table=True, append=False)
print('Table format write took ' + str(t.interval))

I wyjście jest po prostu

Fixed format write took 7.1
Table format write took 178.7

Mój zbiór danych ma 28,880,943 wierszy, a kolumny są podstawowymi typami danych:

node_id           int64
thread_id         int64
handle_id         int64
type              int64
begin             int64
end               int64
duration          int64
flags             int64
unique_id         int64
id                int64
DSTL_LS_FULL    float64
L2_DMISS        float64
L3_MISS         float64
kernel_type     float64
dtype: object

... więc myślę, że nie powinno być żadnych problemów specyficznych dla danych z prędkością zapisu.

Próbowałem również dodać kompresję BLOSC, aby wykluczyć wszelkie dziwne problemy We / Wy, które mogą mieć wpływ na jeden lub drugi scenariusz, ale kompresja wydaje się zmniejszać wydajność obu jednakowo.

Teraz zdaję sobie sprawę, że dokumentacja pandy mówi, że format Storer oferuje znacznie szybszy zapis i nieco szybszy odczyt. (Doświadczam szybszego odczytu, ponieważ odczyt formatu Storer zajmuje około 2,5 sekundy, podczas gdy odczyt formatu tabeli zajmuje około 10 sekund.) Ale naprawdę wydaje się przesadą, że zapis w formacie tabeli powinien trwać 25 razy dłużej niż zapis w formacie magazynu.

Czy któraś z osób zaangażowanych w PyTables lub pandy może wyjaśnić architektoniczne (lub inne) powody, dla których pisanie do formatu queryable (który wyraźnie wymaga bardzo mało dodatkowych danych) powinno trwać o rząd wielkości dłużej? I czy jest jakaś nadzieja na poprawę tego w przyszłości? Chciałbym wskoczyć do udziału w jednym lub drugim projekcie, jako mój dziedziną jest high performance computing i widzę znaczący przypadek użycia dla obu projektów w tej dziedzinie.... ale byłoby pomocne, aby uzyskać pewne wyjaśnienia na temat związanych z tym problemów, i / lub kilka porad na temat tego, jak przyspieszyć rzeczy od tych, którzy wiedzą, jak system jest zbudowany.

EDIT:

Uruchomienie poprzednich testów z % prun w IPython daje następujące (nieco zmniejszone dla czytelności) wyjście profilu dla formatu Storer / Fixed:

%prun -l 20 profile.events.to_hdf('test.h5', 'events', table=False, append=False)

3223 function calls (3222 primitive calls) in 7.385 seconds

Ordered by: internal time
List reduced from 208 to 20 due to restriction <20>

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    6    7.127    1.188    7.128    1.188 {method '_createArray' of 'tables.hdf5Extension.Array' objects}
    1    0.242    0.242    0.242    0.242 {method '_closeFile' of 'tables.hdf5Extension.File' objects}
    1    0.003    0.003    0.003    0.003 {method '_g_new' of 'tables.hdf5Extension.File' objects}
   46    0.001    0.000    0.001    0.000 {method 'reduce' of 'numpy.ufunc' objects}

Oraz następujące dla format tabel:

   %prun -l 40 profile.events.to_hdf('test.h5', 'events', table=True, append=False, chunksize=1000000)

   499082 function calls (499040 primitive calls) in 188.981 seconds

   Ordered by: internal time
   List reduced from 526 to 40 due to restriction <40>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       29   92.018    3.173   92.018    3.173 {pandas.lib.create_hdf_rows_2d}
      640   20.987    0.033   20.987    0.033 {method '_append' of 'tables.hdf5Extension.Array' objects}
       29   19.256    0.664   19.256    0.664 {method '_append_records' of 'tables.tableExtension.Table' objects}
      406   19.182    0.047   19.182    0.047 {method '_g_writeSlice' of 'tables.hdf5Extension.Array' objects}
    14244   10.646    0.001   10.646    0.001 {method '_g_readSlice' of 'tables.hdf5Extension.Array' objects}
      472   10.359    0.022   10.359    0.022 {method 'copy' of 'numpy.ndarray' objects}
       80    3.409    0.043    3.409    0.043 {tables.indexesExtension.keysort}
        2    3.023    1.512    3.023    1.512 common.py:134(_isnull_ndarraylike)
       41    2.489    0.061    2.533    0.062 {method '_fillCol' of 'tables.tableExtension.Row' objects}
       87    2.401    0.028    2.401    0.028 {method 'astype' of 'numpy.ndarray' objects}
       30    1.880    0.063    1.880    0.063 {method '_g_flush' of 'tables.hdf5Extension.Leaf' objects}
      282    0.824    0.003    0.824    0.003 {method 'reduce' of 'numpy.ufunc' objects}
       41    0.537    0.013    0.668    0.016 index.py:607(final_idx32)
    14490    0.385    0.000    0.712    0.000 array.py:342(_interpret_indexing)
       39    0.279    0.007   19.635    0.503 index.py:1219(reorder_slice)
        2    0.256    0.128   10.063    5.031 index.py:1099(get_neworder)
        1    0.090    0.090  119.392  119.392 pytables.py:3016(write_data)
    57842    0.087    0.000    0.087    0.000 {numpy.core.multiarray.empty}
    28570    0.062    0.000    0.107    0.000 utils.py:42(is_idx)
    14164    0.062    0.000    7.181    0.001 array.py:711(_readSlice)

EDIT 2:

Uruchomiony ponownie z przedpremierową kopią pandy 0.13 (wyciągnięty 20 listopada 2013 około 11: 00 EST), czasy zapisu dla formatu tabel znacznie się poprawiają, ale nadal nie porównują "rozsądnie" do prędkości zapisu formatu przechowującego/stałego.

%prun -l 40 profile.events.to_hdf('test.h5', 'events', table=True, append=False, chunksize=1000000)

         499748 function calls (499720 primitive calls) in 117.187 seconds

   Ordered by: internal time
   List reduced from 539 to 20 due to restriction <20>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      640   22.010    0.034   22.010    0.034 {method '_append' of 'tables.hdf5Extension.Array' objects}
       29   20.782    0.717   20.782    0.717 {method '_append_records' of 'tables.tableExtension.Table' objects}
      406   19.248    0.047   19.248    0.047 {method '_g_writeSlice' of 'tables.hdf5Extension.Array' objects}
    14244   10.685    0.001   10.685    0.001 {method '_g_readSlice' of 'tables.hdf5Extension.Array' objects}
      472   10.439    0.022   10.439    0.022 {method 'copy' of 'numpy.ndarray' objects}
       30    7.356    0.245    7.356    0.245 {method '_g_flush' of 'tables.hdf5Extension.Leaf' objects}
       29    7.161    0.247   37.609    1.297 pytables.py:3498(write_data_chunk)
        2    3.888    1.944    3.888    1.944 common.py:197(_isnull_ndarraylike)
       80    3.581    0.045    3.581    0.045 {tables.indexesExtension.keysort}
       41    3.248    0.079    3.294    0.080 {method '_fillCol' of 'tables.tableExtension.Row' objects}
       34    2.744    0.081    2.744    0.081 {method 'ravel' of 'numpy.ndarray' objects}
      115    2.591    0.023    2.591    0.023 {method 'astype' of 'numpy.ndarray' objects}
      270    0.875    0.003    0.875    0.003 {method 'reduce' of 'numpy.ufunc' objects}
       41    0.560    0.014    0.732    0.018 index.py:607(final_idx32)
    14490    0.387    0.000    0.712    0.000 array.py:342(_interpret_indexing)
       39    0.303    0.008   19.617    0.503 index.py:1219(reorder_slice)
        2    0.288    0.144   10.299    5.149 index.py:1099(get_neworder)
    57871    0.087    0.000    0.087    0.000 {numpy.core.multiarray.empty}
        1    0.084    0.084   45.266   45.266 pytables.py:3424(write_data)
        1    0.080    0.080   55.542   55.542 pytables.py:3385(write)

Zauważyłem podczas wykonywania tych testów, że są długie okresy, w których zapis wydaje się "pauzować" (plik na dysku nie rośnie aktywnie), A jednak jest też niskie zużycie procesora podczas niektóre z tych okresów.

Zaczynam podejrzewać, że niektóre znane ograniczenia ext3 mogą źle oddziaływać z pand lub PyTables. Ext3 i inne systemy plików nie bazujące na zakresie czasami mają problemy z szybkim odłączeniem dużych plików, a podobna wydajność systemu (niskie zużycie procesora, ale długie czasy oczekiwania) jest widoczna nawet podczas zwykłego " rm " pliku 1GB, na przykład.

Aby wyjaśnić, w każdym przypadku testowym, upewniłem się, aby usunąć istniejący plik, jeśli w ogóle, przed rozpoczęciem testu, tak aby nie aby ponieść karę usunięcia/nadpisania pliku ext3.

Jednak przy ponownym uruchomieniu testu z index=None, wydajność drastycznie się poprawia (~50s vs ~120 podczas indeksowania). Wydaje się więc, że albo proces ten jest nadal związany z procesorem (mój system ma stosunkowo stare procesory AMD Opteron Istanbul pracujące @ 2.8 GHz, choć ma również 8 gniazd z 6 rdzeniami w każdym, z których wszystkie oprócz jednego oczywiście siedzą bezczynnie podczas zapisu), albo że istnieje jakiś konflikt między sposobem zapisu a procesorem. PyTables lub pandy próbują manipulować/odczytywać / analizować plik, gdy już częściowo lub w całości znajduje się na systemie plików, co powoduje patologicznie złe zachowanie we / wy podczas indeksowania.

EDIT 3:

@sugerowane przez Jeffa testy na mniejszym zbiorze danych (1,3 GB na dysku), po aktualizacji PyTables z 2.4 do 3.0.0, dotarły do mnie tutaj:

In [7]: %timeit f(df)
1 loops, best of 3: 3.7 s per loop

In [8]: %timeit f2(df) # where chunksize= 2 000 000
1 loops, best of 3: 13.8 s per loop

In [9]: %timeit f3(df) # where chunksize= 2 000 000
1 loops, best of 3: 43.4 s per loop

W rzeczywistości, moja wydajność wydaje się bić jego we wszystkich scenariuszach z wyjątkiem gdy indeksowanie jest włączone (domyślne). Jednakże, indeksowanie nadal wydaje się być zabójcze i jeśli sposób, w jaki interpretuję wyniki z top i ls podczas uruchamiania tych testów jest poprawny, pozostają okresy, w których nie ma ani znaczącego przetwarzania, ani żadnego zapisu plików (tzn. użycie procesora w Pythonie jest bliskie 0, a Rozmiar pliku pozostaje stały). Mogę tylko założyć, że to są odczyty plików. Dlaczego odczyt plików powodowałby spowolnienie jest dla mnie trudne do zrozumienia, ponieważ mogę niezawodnie załadować cały plik 3 + GB z tego dysku do pamięci w mniej niż 3 sekundy. Jeśli nie są to odczyty plików, to na co "czeka" system? (Nikt inny nie jest zalogowany do maszyny i nie ma innej aktywności systemu plików.)

W tym momencie, z ulepszonymi wersjami odpowiednich modułów Pythona, wydajność mojego oryginalnego zestawu danych jest ograniczona do następujących liczb. Szczególnie interesujące są czas systemowy, który, jak zakładam, jest co najmniej górną granicą czasu spędzonego na wykonywaniu IO, i czas ściany, który wydaje się być może rozlicz te tajemnicze okresy bez zapisu / bez aktywności procesora.

In [28]: %time f(profile.events)
CPU times: user 0 ns, sys: 7.16 s, total: 7.16 s
Wall time: 7.51 s

In [29]: %time f2(profile.events)
CPU times: user 18.7 s, sys: 14 s, total: 32.7 s
Wall time: 47.2 s

In [31]: %time f3(profile.events)
CPU times: user 1min 18s, sys: 14.4 s, total: 1min 32s
Wall time: 2min 5s

Niemniej jednak wydaje się, że indeksowanie powoduje znaczne spowolnienie mojego przypadku użycia. Może powinienem spróbować ograniczyć pola indeksowane zamiast po prostu wykonywać domyślną wielkość liter (która może być indeksowana na wszystkich polach w ramce danych)? Nie jestem pewien, w jaki sposób może to wpłynąć na czasy zapytań, zwłaszcza w przypadkach, gdy zapytanie wybiera się na podstawie nieindeksowanego pola.

Per Prośba Jeffa, zrzut pliku wynikowego.

ptdump -av test.h5
/ (RootGroup) ''
  /._v_attrs (AttributeSet), 4 attributes:
   [CLASS := 'GROUP',
    PYTABLES_FORMAT_VERSION := '2.1',
    TITLE := '',
    VERSION := '1.0']
/df (Group) ''
  /df._v_attrs (AttributeSet), 14 attributes:
   [CLASS := 'GROUP',
    TITLE := '',
    VERSION := '1.0',
    data_columns := [],
    encoding := None,
    index_cols := [(0, 'index')],
    info := {1: {'type': 'Index', 'names': [None]}, 'index': {}},
    levels := 1,
    nan_rep := 'nan',
    non_index_axes := 
    [(1, ['node_id', 'thread_id', 'handle_id', 'type', 'begin', 'end', 'duration', 'flags', 'unique_id', 'id', 'DSTL_LS_FULL', 'L2_DMISS', 'L3_MISS', 'kernel_type'])],
    pandas_type := 'frame_table',
    pandas_version := '0.10.1',
    table_type := 'appendable_frame',
    values_cols := ['values_block_0', 'values_block_1']]
/df/table (Table(28880943,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "values_block_0": Int64Col(shape=(10,), dflt=0, pos=1),
  "values_block_1": Float64Col(shape=(4,), dflt=0.0, pos=2)}
  byteorder := 'little'
  chunkshape := (4369,)
  autoindex := True
  colindexes := {
    "index": Index(6, medium, shuffle, zlib(1)).is_csi=False}
  /df/table._v_attrs (AttributeSet), 15 attributes:
   [CLASS := 'TABLE',
    FIELD_0_FILL := 0,
    FIELD_0_NAME := 'index',
    FIELD_1_FILL := 0,
    FIELD_1_NAME := 'values_block_0',
    FIELD_2_FILL := 0.0,
    FIELD_2_NAME := 'values_block_1',
    NROWS := 28880943,
    TITLE := '',
    VERSION := '2.7',
    index_kind := 'integer',
    values_block_0_dtype := 'int64',
    values_block_0_kind := ['node_id', 'thread_id', 'handle_id', 'type', 'begin', 'end', 'duration', 'flags', 'unique_id', 'id'],
    values_block_1_dtype := 'float64',
    values_block_1_kind := ['DSTL_LS_FULL', 'L2_DMISS', 'L3_MISS', 'kernel_type']]

I kolejny % prun z zaktualizowanymi modułami i pełnym zestawem danych:

%prun -l 25  %time f3(profile.events)
CPU times: user 1min 14s, sys: 16.2 s, total: 1min 30s
Wall time: 1min 48s

        542678 function calls (542650 primitive calls) in 108.678 seconds

   Ordered by: internal time
   List reduced from 629 to 25 due to restriction <25>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      640   23.633    0.037   23.633    0.037 {method '_append' of 'tables.hdf5extension.Array' objects}
       15   20.852    1.390   20.852    1.390 {method '_append_records' of 'tables.tableextension.Table' objects}
      406   19.584    0.048   19.584    0.048 {method '_g_write_slice' of 'tables.hdf5extension.Array' objects}
    14244   10.591    0.001   10.591    0.001 {method '_g_read_slice' of 'tables.hdf5extension.Array' objects}
      458    9.693    0.021    9.693    0.021 {method 'copy' of 'numpy.ndarray' objects}
       15    6.350    0.423   30.989    2.066 pytables.py:3498(write_data_chunk)
       80    3.496    0.044    3.496    0.044 {tables.indexesextension.keysort}
       41    3.335    0.081    3.376    0.082 {method '_fill_col' of 'tables.tableextension.Row' objects}
       20    2.551    0.128    2.551    0.128 {method 'ravel' of 'numpy.ndarray' objects}
      101    2.449    0.024    2.449    0.024 {method 'astype' of 'numpy.ndarray' objects}
       16    1.789    0.112    1.789    0.112 {method '_g_flush' of 'tables.hdf5extension.Leaf' objects}
        2    1.728    0.864    1.728    0.864 common.py:197(_isnull_ndarraylike)
       41    0.586    0.014    0.842    0.021 index.py:637(final_idx32)
    14490    0.292    0.000    0.616    0.000 array.py:368(_interpret_indexing)
        2    0.283    0.142   10.267    5.134 index.py:1158(get_neworder)
      274    0.251    0.001    0.251    0.001 {method 'reduce' of 'numpy.ufunc' objects}
       39    0.174    0.004   19.373    0.497 index.py:1280(reorder_slice)
    57857    0.085    0.000    0.085    0.000 {numpy.core.multiarray.empty}
        1    0.083    0.083   35.657   35.657 pytables.py:3424(write_data)
        1    0.065    0.065   45.338   45.338 pytables.py:3385(write)
    14164    0.065    0.000    7.831    0.001 array.py:615(__getitem__)
    28570    0.062    0.000    0.108    0.000 utils.py:47(is_idx)
       47    0.055    0.001    0.055    0.001 {numpy.core.multiarray.arange}
    28570    0.050    0.000    0.090    0.000 leaf.py:397(_process_range)
    87797    0.048    0.000    0.048    0.000 {isinstance}
Author: Peter Gaultney, 2013-11-20

2 answers

Ciekawa dyskusja. Myślę, że Peter jest coraz niesamowite wydajności dla stałego formatu, ponieważ format zapisuje w jednym ujęciu, a także, że ma naprawdę dobry SSD(może pisać na więcej niż 450 MB / s).

Dołączanie do tabeli jest bardziej złożoną operacją(zbiór danych musi zostać powiększony, a nowe rekordy muszą być sprawdzane, abyśmy mogli upewnić się, że są zgodne ze schematem tabeli). Dlatego dodawanie wierszy w tabelach jest na ogół wolniejsze (ale i tak uzyskanie ~ 70 MB / s, co jest całkiem dobre). To, że Jeff ma większą prędkość niż Peter, wynika prawdopodobnie z faktu, że ma lepszy procesor.

Wreszcie, indeksowanie w PyTables używa jednego procesora, tak, i to zwykle jest kosztowna operacja, więc naprawdę powinieneś ją wyłączyć, jeśli nie zamierzasz odpytywać danych na dysku.

 5
Author: Francesc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-12 12:38:25

Oto podobne porównanie, które właśnie zrobiłem. Jego około 1/3 danych 10M rzędów. Ostateczny rozmiar to około 1.3 GB

Definiuję 3 funkcje czasowe:

Przetestuj stały format (zwany Storer w 0.12). Zapisuje się w formacie tablicy PyTables

def f(df):
    store = pd.HDFStore('test.h5','w')
    store['df'] = df
    store.close()

Zapis w formacie tabeli, przy użyciu formatu tabeli PyTables. Nie twórz indeksu.

def f2(df):
    store = pd.HDFStore('test.h5','w')
    store.append('df',df,index=False)
    store.close()

To samo co f2, ale Utwórz indeks (który jest normalnie wykonywany)

def f3(df):
    store = pd.HDFStore('test.h5','w')
    store.append('df',df)
    store.close()

Utwórz ramkę

In [25]: df = concat([DataFrame(np.random.randn(10000000,10)),DataFrame(np.random.randint(0,10,size=50000000).reshape(10000000,5))],axis=1)

In [26]: df
Out[26]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 10000000 entries, 0 to 9999999
Columns: 15 entries, 0 to 4
dtypes: float64(10), int64(5)


v0.12.0

In [27]: %timeit f(df)
1 loops, best of 3: 14.7 s per loop

In [28]: %timeit f2(df)
1 loops, best of 3: 32 s per loop

In [29]: %timeit f3(df)
1 loops, best of 3: 40.1 s per loop

master/v0.13.0

In [5]: %timeit f(df)
1 loops, best of 3: 12.9 s per loop

In [6]: %timeit f2(df)
1 loops, best of 3: 17.5 s per loop

In [7]: %timeit f3(df)
1 loops, best of 3: 24.3 s per loop

Timing Uruchamia się z tym samym plikiem, co podany przez OP (link znajduje się poniżej)

In [4]: df = pd.read_hdf('test.h5','df')

In [5]: df
Out[5]: 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 28880943 entries, 0 to 28880942
Columns: 14 entries, node_id to kernel_type
dtypes: float64(4), int64(10)

Jak f1, stały format

In [6]: %timeit df.to_hdf('test.hdf','df',mode='w')
1 loops, best of 3: 36.2 s per loop

Jak f2, Format tabeli, Brak Indeksu

In [7]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',index=False)
1 loops, best of 3: 45 s per loop

In [8]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',index=False,chunksize=2000000)
1 loops, best of 3: 44.5 s per loop

Jak f3, Format tabeli z indeksem

In [9]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',chunksize=2000000)
1 loops, best of 3: 1min 36s per loop

Jak f3, Format tabeli z indeksem, skompresowany za pomocą blosc

In [10]: %timeit df.to_hdf('test.hdf','df',mode='w',format='table',chunksize=2000000,complib='blosc')
1 loops, best of 3: 46.5 s per loop

In [11]: %timeit pd.read_hdf('test.hdf','df')
1 loops, best of 3: 10.8 s per loop

Pokaż oryginalny plik (test.h5 i skompresowany, test.hdf)

In [13]: !ls -ltr test.h*
-rw-r--r-- 1 jreback users 3471518282 Nov 20 18:20 test.h5
-rw-rw-r-- 1 jreback users  649327780 Nov 20 21:17 test.hdf

Kilka punktów do odnotowania.

  • Nie utworzenie indeksu może zrobić nietrywialną różnicę w czasie. I uwierz również, że jeśli masz indeks oparty na łańcuchu znaków, może to znacznie skrócić czas zapisu. To powiedziawszy, zawsze chcesz utworzyć indeks, aby wyszukiwanie było bardzo szybkie.

    Nie podałeś, jaki jest Twój indeks, ani czy jest on posortowany (chociaż myślę, że to robi małą różnicę).

  • Kara zapisu w moich przykładach jest około 2x (choć widziałem, że jest nieco większa, gdy obejmuje czas indeksu). Tak więc twoje 7s( 1/2 mojego czasu), za 3x liczbę jestem pisanie jest dość podejrzane. Używam dość szybkiej macierzy dyskowej. Jeśli używasz dysku opartego na pamięci flash, jest to możliwe.

  • Master / v0.13. 0 (wydanie wkrótce), znacznie poprawia czasy zapisu na tabelach.

  • Możesz spróbować ustawić parametr chunksize na większą liczbę podczas zapisu danych (jego domyślna wartość to 100000). Celem 'relatywnie' niskiej liczby jest stałe zużycie pamięci. (np. jeśli jest większy, użyjesz więcej pamięci, teoretycznie powinna jednak pisać szybciej).

  • Tabele oferują 2 zalety w porównaniu ze stałym formatem: 1) wyszukiwanie zapytań i 2) przydatność. Czytanie całej tabeli nie korzysta z żadnej z nich, więc jeśli chcesz tylko czytać całą tabelę, zaleca się stały format. (Z mojego doświadczenia elastyczność tabel znacznie przewyższa karę zapisu, ale YMMV)

Najważniejsze jest powtórzenie timingów (użyj ipythona, ponieważ uruchomi wiele testów). Jeśli możesz odtworzyć swoje wyniki, a następnie pls post % prun, a ja rzucę okiem.

Aktualizacja:

Więc zalecanym sposobem dla tabeli tego rozmiaru jest skompresowanie za pomocą blosc i użycie pandas master / 0.13.0 wraz z PyTables 3.0.0

 8
Author: Jeff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-21 02:25:29