Przepływy pracy „dużych danych” przy użyciu pand


980

Przez wiele miesięcy próbowałem znaleźć odpowiedź na to pytanie, ucząc się pand. Używam SAS do mojej codziennej pracy i jest świetny, ponieważ zapewnia wsparcie poza rdzeniem. Jednak SAS jest okropny jako oprogramowanie z wielu innych powodów.

Pewnego dnia mam nadzieję, że zastąpię korzystanie z SAS pythonem i pandami, ale obecnie brakuje mi nieszablonowego przepływu pracy dla dużych zestawów danych. Nie mówię o „dużych danych”, które wymagają sieci rozproszonej, ale raczej o plikach zbyt dużych, aby zmieściły się w pamięci, ale wystarczająco małych, aby zmieściły się na dysku twardym.

Moją pierwszą myślą jest HDFStoreprzechowywanie dużych zestawów danych na dysku i pobieranie tylko potrzebnych elementów do ramek danych do analizy. Inni wspominali MongoDB jako łatwiejszą w użyciu alternatywę. Moje pytanie brzmi:

Jakie są najlepsze praktyki dla realizacji następujących celów:

  1. Ładowanie płaskich plików do stałej struktury bazy danych na dysku
  2. Zapytanie do tej bazy danych w celu pobrania danych do struktury danych pandy
  3. Aktualizacja bazy danych po manipulowaniu elementami w pandach

Przykłady z prawdziwego świata byłyby bardzo mile widziane, zwłaszcza od każdego, kto używa pand na „dużych danych”.

Edytuj - przykład tego, jak chciałbym, aby to działało:

  1. Iteracyjnie importuj duży plik płaski i przechowuj go w stałej strukturze bazy danych na dysku. Te pliki są zwykle zbyt duże, aby zmieścić się w pamięci.
  2. Aby korzystać z Pand, chciałbym przeczytać podzbiory tych danych (zwykle tylko kilka kolumn jednocześnie), które mogą zmieścić się w pamięci.
  3. Chciałbym utworzyć nowe kolumny, wykonując różne operacje na wybranych kolumnach.
  4. Musiałbym wtedy dołączyć te nowe kolumny do struktury bazy danych.

Próbuję znaleźć najlepszy sposób wykonywania tych kroków. Czytanie linków o pandach i pytaniach wydaje się, że dodanie nowej kolumny może być problemem.

Edycja - Odpowiadając w szczególności na pytania Jeffa:

  1. Buduję modele ryzyka kredytowego konsumentów. Rodzaje danych obejmują cechy telefonu, SSN i ​​adresu; wartości nieruchomości; obraźliwe informacje, takie jak rejestry kryminalne, bankructwa itp. ... Zestawy danych, z których korzystam na co dzień, mają średnio prawie 1000 do 2000 pól o mieszanych typach danych: ciągłe, nominalne i porządkowe zmiennych danych liczbowych i znaków. Rzadko dołączam wiersze, ale wykonuję wiele operacji, które tworzą nowe kolumny.
  2. Typowe operacje obejmują połączenie kilku kolumn przy użyciu logiki warunkowej w nową kolumnę złożoną. Na przykład if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B'. Wynikiem tych operacji jest nowa kolumna dla każdego rekordu w moim zbiorze danych.
  3. Na koniec chciałbym dołączyć te nowe kolumny do struktury danych na dysku. Powtórzę krok 2, eksplorując dane za pomocą tabel przestawnych i statystyk opisowych, próbując znaleźć interesujące, intuicyjne relacje do modelowania.
  4. Typowy plik projektu ma zwykle około 1 GB. Pliki są zorganizowane w taki sposób, że wiersz składa się z zapisu danych konsumenta. Każdy wiersz ma taką samą liczbę kolumn dla każdego rekordu. Tak będzie zawsze.
  5. Rzadko zdarza się, że przy tworzeniu nowej kolumny dzieliłem wiersze według wierszy. Jednak często zdarza mi się podzbiór wierszy podczas tworzenia raportów lub generowania statystyk opisowych. Na przykład może chciałbym utworzyć prostą częstotliwość dla konkretnej branży, np. Karty kredytowe. Aby to zrobić, wybrałbym tylko te rekordy, w których branża = sprzedaż detaliczna oprócz kolumn, które chcę zgłosić. Jednak podczas tworzenia nowych kolumn wyciągałbym wszystkie wiersze danych i tylko kolumny potrzebne do operacji.
  6. Proces modelowania wymaga przeanalizowania każdej kolumny, znalezienia interesujących relacji z pewną zmienną wynikową i utworzenia nowych kolumn złożonych opisujących te relacje. Kolumny, które eksploruję, są zwykle wykonywane w małych zestawach. Na przykład skupię się na zestawie powiedzmy 20 kolumn, które dotyczą tylko wartości nieruchomości i obserwuję, jak odnoszą się one do niespłacania pożyczki. Po ich zbadaniu i utworzeniu nowych kolumn przechodzę do innej grupy kolumn, mówię o szkolnictwie wyższym i powtarzam ten proces. To, co robię, to tworzenie zmiennych kandydujących, które wyjaśniają związek między moimi danymi a niektórymi wynikami. Na samym końcu tego procesu stosuję techniki uczenia się, które tworzą równanie z tych kolumn złożonych.

Rzadko dodawałem wiersze do zestawu danych. Prawie zawsze będę tworzyć nowe kolumny (zmienne lub cechy w statystyce / języku uczenia maszynowego).


1
Czy stosunek rdzenia rozmiar / pełny rozmiar 1%, 10%? Czy to ma znaczenie - jeśli mógłbyś skompresować cols do int8 lub odfiltrować zaszumione wiersze, czy zmieniłoby to twoją pętlę obliczeniowo-myślową z powiedz godziny na minuty? (Dodaj także tag duże dane.)
den

1
Przechowywanie float32 zamiast float64 i int8 tam, gdzie to możliwe, powinno być trywialne (chociaż nie wiem, jakie narzędzia / funkcje wykonują float64 wewnętrznie)
denis

czy możesz podzielić swoje zadanie na części pracy?
Andrew Scott Evans,

1
fajne rozwiązanie 2019 do robienia pand, takich jak operacje na „średnich” danych, które nie mieszczą się w pamięci, jest dask
lunguini

Istnieją alternatywy dla Python + pandy, które warto rozważyć, gdy dopiero zaczynasz. Weź pod uwagę fakt, że Python jest językiem programowania ogólnego przeznaczenia (a nie DSL do mungowania i analizy danych) oraz że pandy są biblioteką dodatkową. Rozważałbym spojrzenie na R lub kdb.
Henry Henrinson

Odpowiedzi:


621

Rutynowo używam dziesiątek gigabajtów danych w ten właśnie sposób, np. Mam tabele na dysku, które czytam za pomocą zapytań, tworzę dane i dołączam z powrotem.

Warto przeczytać dokumenty i przeczytać w dalszej części tego wątku, aby uzyskać kilka sugestii dotyczących sposobu przechowywania danych.

Szczegóły, które będą miały wpływ na sposób przechowywania danych, takie jak:
Podaj jak najwięcej szczegółów; i mogę pomóc ci rozwinąć strukturę.

  1. Rozmiar danych, liczba wierszy, kolumn, typy kolumn; dodajesz wiersze, czy tylko kolumny?
  2. Jak będą wyglądały typowe operacje. Na przykład wykonaj zapytanie dotyczące kolumn, aby wybrać wiązkę wierszy i określonych kolumn, a następnie wykonaj operację (w pamięci), utwórz nowe kolumny, zapisz je.
    (Podanie przykładu zabawki może umożliwić nam zaoferowanie bardziej szczegółowych zaleceń).
  3. Po tym przetwarzaniu co robisz? Czy krok 2 jest ad hoc, czy powtarzalny?
  4. Wprowadź płaskie pliki: ile, przybliżony całkowity rozmiar w Gb. Jak są one zorganizowane np. Według rejestrów? Czy każde z nich zawiera inne pola, czy ma jakieś rekordy na plik ze wszystkimi polami w każdym pliku?
  5. Czy kiedykolwiek wybierasz podzbiory wierszy (rekordów) na podstawie kryteriów (np. Wybierasz wiersze o polu A> 5)? a następnie zrobić coś, czy po prostu wybierasz pola A, B, C ze wszystkimi rekordami (a następnie coś robisz)?
  6. Czy „pracujesz” nad wszystkimi swoimi kolumnami (w grupach), czy też istnieje dobry odsetek, którego możesz użyć tylko do raportów (np. Chcesz zachować dane w pobliżu, ale nie musisz pobierać widoczności tej kolumny, dopóki czas ostatecznych wyników)?

Rozwiązanie

Upewnij się, że masz przynajmniej pandy0.10.1 zainstalowane.

Czytaj pliki iteracyjne fragment po kawałku i wiele zapytań tabelowych .

Ponieważ pytania są zoptymalizowane do pracy w wierszach (o które pytasz), utworzymy tabelę dla każdej grupy pól. W ten sposób łatwo jest wybrać małą grupę pól (która będzie działać z dużym stołem, ale bardziej efektywne jest to zrobić w ten sposób ... Myślę, że mogę w przyszłości naprawić to ograniczenie ... to jest w każdym razie bardziej intuicyjne):
(Poniżej znajduje się pseudokod.)

import numpy as np
import pandas as pd

# create a store
store = pd.HDFStore('mystore.h5')

# this is the key to your storage:
#    this maps your fields to a specific group, and defines 
#    what you want to have as data_columns.
#    you might want to create a nice class wrapping this
#    (as you will want to have this map and its inversion)  
group_map = dict(
    A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
    B = dict(fields = ['field_10',......        ], dc = ['field_10']),
    .....
    REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),

)

group_map_inverted = dict()
for g, v in group_map.items():
    group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))

Odczytywanie plików i tworzenie magazynu (zasadniczo robienie tego, co append_to_multiplerobi):

for f in files:
   # read in the file, additional options may be necessary here
   # the chunksize is not strictly necessary, you may be able to slurp each 
   # file into memory in which case just eliminate this part of the loop 
   # (you can also change chunksize if necessary)
   for chunk in pd.read_table(f, chunksize=50000):
       # we are going to append to each table by group
       # we are not going to create indexes at this time
       # but we *ARE* going to create (some) data_columns

       # figure out the field groupings
       for g, v in group_map.items():
             # create the frame for this group
             frame = chunk.reindex(columns = v['fields'], copy = False)    

             # append it
             store.append(g, frame, index=False, data_columns = v['dc'])

Teraz masz wszystkie tabele w pliku (w rzeczywistości możesz je przechowywać w osobnych plikach, jeśli chcesz, prawdopodobnie musisz dodać nazwę pliku do mapy_grupy, ale prawdopodobnie nie jest to konieczne).

W ten sposób otrzymujesz kolumny i tworzysz nowe:

frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
#     select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows

# do calculations on this frame
new_frame = cool_function_on_frame(frame)

# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)

Kiedy będziesz gotowy na post_processing:

# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)

O kolumnach danych nie ma potrzeby definiowania ŻADNYCH kolumn danych; pozwalają na podselekcję wierszy na podstawie kolumny. Np. Coś takiego:

store.select(group, where = ['field_1000=foo', 'field_1001>0'])

Mogą być dla Ciebie najbardziej interesujące na końcowym etapie generowania raportu (zasadniczo kolumna danych jest oddzielona od innych kolumn, co może nieco wpłynąć na wydajność, jeśli dużo zdefiniujesz).

Możesz także chcieć:

  • utwórz funkcję, która pobiera listę pól, wyszukuje grupy w mapach grup, następnie je wybiera i łączy wyniki, aby uzyskać wynikową ramkę (w zasadzie robi to select_as_multiple). W ten sposób struktura będzie dla ciebie całkiem przezroczysta.
  • indeksuje określone kolumny danych (znacznie przyspiesza podzestaw wierszy).
  • włączyć kompresję.

Daj mi znać, jeśli masz pytania!


5
Dzięki za linki. Drugi link trochę mnie martwi, że nie mogę dołączyć nowych kolumn do tabel w HDFStore? Czy to jest poprawne? Dodałem także przykład, w jaki sposób skorzystam z tej konfiguracji.
Zelazny7

4
Rzeczywista struktura w hdf zależy od Ciebie. Pytables jest zorientowane na wiersze, ze stałymi kolumnami w czasie tworzenia. Nie można dołączyć kolumn po utworzeniu tabeli. Możesz jednak utworzyć nową tabelę indeksowaną tak samo jak istniejącą. (zobacz przykłady select_as_multiple w dokumentacji). W ten sposób możesz tworzyć obiekty o dowolnej wielkości, mając jednocześnie dość wydajne zapytania. Sposób korzystania z danych ma kluczowe znaczenie dla tego, jak powinny być zorganizowane na dysku. Wyślij mi e-mail spoza listy z pseudo kodem bardziej szczegółowego przykładu.
Jeff

1
Zaktualizowałem moje pytanie, aby odpowiedzieć na szczegółowe informacje. Będę pracował na przykładzie, aby wysłać cię poza listę. Dzięki!
Zelazny7

12
@Jeff, przy Pandach w wersji 0.17.x, czy problemy opisane powyżej zostały rozwiązane w Pandach?
ctrl-alt-delete

5
@Jeff chciał dodać szybką aktualizację swojej odpowiedzi, aby promować dask?
Boud

137

Myślę, że w powyższych odpowiedziach brakuje prostego podejścia, które uważam za bardzo przydatne.

Kiedy mam plik, który jest zbyt duży, aby go załadować do pamięci, dzielę go na wiele mniejszych plików (według wiersza lub kols)

Przykład: w przypadku 30-dniowego handlu danymi o wielkości ~ 30 GB, dzielę je na plik dziennie o wielkości ~ 1 GB. Następnie przetwarzam każdy plik osobno i na końcu agreguję wyniki

Jedną z największych zalet jest to, że umożliwia równoległe przetwarzanie plików (wielu wątków lub procesów)

Inną zaletą jest to, że manipulowanie plikami (np. Dodawanie / usuwanie dat w przykładzie) może być wykonywane za pomocą zwykłych poleceń powłoki, co nie jest możliwe w bardziej zaawansowanych / skomplikowanych formatach plików

To podejście nie obejmuje wszystkich scenariuszy, ale jest bardzo przydatne w wielu z nich


39
Zgoda. Przy całym szumie łatwo jest zapomnieć, że narzędzia wiersza polecenia mogą być 235 razy szybsze niż klaster Hadoop
zelusp


66

Jeśli twoje zestawy danych mają od 1 do 20 GB, powinieneś dostać stację roboczą z 48 GB pamięci RAM. Następnie Pandy mogą przechowywać cały zestaw danych w pamięci RAM. Wiem, że nie jest to odpowiedź, której tu szukasz, ale wykonywanie obliczeń naukowych na notebooku z 4 GB pamięci RAM nie jest rozsądne.


7
„wykonywanie obliczeń naukowych na notebooku z 4 GB pamięci RAM nie jest rozsądne” Zdefiniuj rozsądne. Myślę, że UNIVAC przyjąłby inne zdanie. arstechnica.com/tech-policy/2011/09/…
grisaitis

2
Zgoda! spróbuj kontynuować pracę w pamięci, nawet jeśli kosztuje ona $$ z góry. Jeśli Twoja praca prowadzi do zwrotu finansowego, z czasem odzyskasz wydatki dzięki zwiększonej wydajności.
ansonw

2
Robienie obliczeń naukowych na stacji roboczej z 48 GB pamięci RAM nie jest rozsądne.
Yaroslav Nikitenko

4
@YaroslavNikitenko R4.2xlarge z 61 GB / RAM to 0,532 USD / godzinę. Jaki rodzaj naukowego przetwarzania danych nie jest tak cenny? Brzmi nietypowo, jeśli nie nieracjonalnie.
rjurney

4
@ rjurney przepraszam, może powinienem usunąć swój komentarz. Twoja ocena „nieuzasadnionego” komputera naukowego wydaje się bardzo subiektywna. Od lat wykonuję obliczenia naukowe na laptopach, co wydaje mi się wystarczające, ponieważ przez większość czasu piszę kod. Moje algorytmy są znacznie trudniejsze z punktu widzenia programowania niż z obliczeniowego. Jestem też całkiem pewien, że przy pisaniu skalowalnych algorytmów nie należy polegać na obecnych ograniczeniach sprzętowych. Twój komentarz na temat komputerów innych ludzi może brzmieć nieco obraźliwie (poza subiektywnością), czy mógłbyś usunąć te kilka słów?
Jarosław Nikitenko,

58

Wiem, że to stary wątek, ale myślę, że warto sprawdzić bibliotekę Blaze . Jest zbudowany dla tego rodzaju sytuacji.

Z dokumentów:

Blaze rozszerza użyteczność NumPy i Pandas na przetwarzanie rozproszone i poza rdzeniem. Blaze zapewnia interfejs podobny do interfejsu NumPy ND-Array lub Pandas DataFrame, ale mapuje te znane interfejsy na wiele innych silników obliczeniowych, takich jak Postgres lub Spark.

Edycja: Nawiasem mówiąc, jest obsługiwany przez ContinuumIO i Travisa Oliphanta, autora NumPy.


Kolejną biblioteką, na którą warto zwrócić uwagę, jest GraphLab Create: ma wydajną strukturę podobną do DataFrame, która nie jest ograniczona pojemnością pamięci. blog.dato.com/…
wodoodporny

52

Tak jest w przypadku pymongo. Mam również prototypowanie przy użyciu serwera SQL, SQLite, HDF, ORM (SQLAlchemy) w Pythonie. Przede wszystkim pymongo jest bazą danych opartą na dokumencie, więc każda osoba byłaby dokumentem ( dictatrybutów). Wiele osób tworzy kolekcję i możesz mieć wiele kolekcji (ludzie, giełda, dochód).

pd.dateframe -> pymongo Uwaga: Używam chunksizein, read_csvaby zachować od 5 do 10 000 rekordów (pymongo upuszcza gniazdo, jeśli jest większe)

aCollection.insert((a[1].to_dict() for a in df.iterrows()))

zapytanie: gt = większe niż ...

pd.DataFrame(list(mongoCollection.find({'anAttribute':{'$gt':2887000, '$lt':2889000}})))

.find()zwraca iterator, więc zwykle używam ichunkeddo krojenia na mniejsze iteratory.

Co powiesz na sprzężenie, ponieważ zwykle otrzymuję 10 źródeł danych do wklejenia

aJoinDF = pandas.DataFrame(list(mongoCollection.find({'anAttribute':{'$in':Att_Keys}})))

potem (w moim przypadku czasami muszę aJoinDFnajpierw zacząć się agresywnie, zanim zostanie „scalony”).

df = pandas.merge(df, aJoinDF, on=aKey, how='left')

Następnie możesz zapisać nowe informacje do swojej głównej kolekcji za pomocą poniższej metody aktualizacji. (gromadzenie logiczne a fizyczne źródła danych).

collection.update({primarykey:foo},{key:change})

W przypadku mniejszych wyszukiwań po prostu denormalizuj. Na przykład masz kod w dokumencie i po prostu dodajesz tekst kodu pola i dictwyszukujesz podczas tworzenia dokumentów.

Teraz masz ładny zestaw danych oparty na osobie, możesz uwolnić swoją logikę w każdej sprawie i zrobić więcej atrybutów. Na koniec możesz wczytać do pandy swoje kluczowe wskaźniki maks. Od 3 do pamięci i przeprowadzić eksplorację osi / ag / danych. Działa to dla 3 milionów rekordów z liczbami / dużym tekstem / kategoriami / kodami / liczbami zmiennoprzecinkowymi / ...

Możesz także użyć dwóch metod wbudowanych w MongoDB (MapReduce i agregacja frameworka). Zobacz tutaj, aby uzyskać więcej informacji na temat struktury agregacji , ponieważ wydaje się ona łatwiejsza niż MapReduce i wydaje się przydatna do szybkiej pracy z agregacją. Zauważ, że nie musiałem definiować swoich pól ani relacji i mogę dodawać elementy do dokumentu. W obecnym stanie szybko zmieniającego się zestawu narzędzi numpy, pand, python MongoDB pomaga mi po prostu zabrać się do pracy :)


Cześć, mam zabawy ze swoim przykładzie, jak również i biegnę do tego błędu, gdy próbujesz wstawić do bazy danych: In [96]: test.insert((a[1].to_dict() for a in df.iterrows())) --------------- InvalidDocument: Cannot encode object: 0. Jakieś pomysły, co może być nie tak? Moja ramka danych składa się ze wszystkich typów int64 i jest bardzo prosta.
Zelazny7

2
Tak, zrobiłem to samo dla prostego zakresu DF, a int64 z numpy wydaje się niepokoić pymongo. Wszystkie dane, na których grałem, były konwertowane z CSV (w przeciwieństwie do sztucznie przez range ()), a typy są długie i dlatego nie ma problemów. W Numpy możesz konwertować, ale uważam to za przeszkodę. Muszę przyznać, że 10.1 elementów HDF wygląda ekscytująco.
brian_the_bungler

43

Zauważyłem to trochę później, ale pracuję z podobnym problemem (modele przedterminowej spłaty kredytu hipotecznego). Moim rozwiązaniem było pominięcie warstwy pandy HDFStore i użycie prostych pytań. Zapisuję każdą kolumnę jako osobną tablicę HDF5 w moim końcowym pliku.

Moim podstawowym obiegiem pracy jest najpierw pobranie pliku CSV z bazy danych. Zgaduję, więc to nie jest tak duże. Następnie przekonwertowałem to na plik HDF5 zorientowany na wiersze, iterując go w pythonie, konwertując każdy wiersz na prawdziwy typ danych i zapisując go do pliku HDF5. Zajmuje to kilkadziesiąt minut, ale nie wykorzystuje pamięci, ponieważ działa tylko wiersz po rzędzie. Następnie „transponuję” zorientowany na wiersze plik HDF5 na zorientowany na kolumny plik HDF5.

Tabela transpozycji wygląda następująco:

def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
    # Get a reference to the input data.
    tb = h_in.getNode(table_path)
    # Create the output group to hold the columns.
    grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
    for col_name in tb.colnames:
        logger.debug("Processing %s", col_name)
        # Get the data.
        col_data = tb.col(col_name)
        # Create the output array.
        arr = h_out.createCArray(grp,
                                 col_name,
                                 tables.Atom.from_dtype(col_data.dtype),
                                 col_data.shape)
        # Store the data.
        arr[:] = col_data
    h_out.flush()

Ponowne czytanie to wygląda następująco:

def read_hdf5(hdf5_path, group_path="/data", columns=None):
    """Read a transposed data set from a HDF5 file."""
    if isinstance(hdf5_path, tables.file.File):
        hf = hdf5_path
    else:
        hf = tables.openFile(hdf5_path)

    grp = hf.getNode(group_path)
    if columns is None:
        data = [(child.name, child[:]) for child in grp]
    else:
        data = [(child.name, child[:]) for child in grp if child.name in columns]

    # Convert any float32 columns to float64 for processing.
    for i in range(len(data)):
        name, vec = data[i]
        if vec.dtype == np.float32:
            data[i] = (name, vec.astype(np.float64))

    if not isinstance(hdf5_path, tables.file.File):
        hf.close()
    return pd.DataFrame.from_items(data)

Teraz generalnie uruchamiam to na maszynie z dużą ilością pamięci, więc może nie być wystarczająco ostrożny z użyciem pamięci. Na przykład domyślnie operacja ładowania odczytuje cały zestaw danych.

To na ogół działa dla mnie, ale jest trochę niezgrabne i nie mogę użyć fantazyjnej magii pytable.

Edycja: Prawdziwą zaletą tego podejścia, w porównaniu do domyślnego pytania-tablicy tablic rekordów, jest to, że mogę następnie załadować dane do R przy użyciu h5r, który nie obsługuje tabel. A przynajmniej nie udało mi się załadować heterogenicznych tabel.


czy mógłbyś podzielić się ze mną częścią swojego kodu? Interesuje mnie, w jaki sposób ładujesz dane z jakiegoś płaskiego formatu tekstu, nie znając typów danych przed wypchnięciem do pytable. Wygląda na to, że pracujesz tylko z danymi jednego typu. Czy to jest poprawne?
Zelazny7

1
Przede wszystkim zakładam, że znam typy kolumn przed załadowaniem, zamiast próbować zgadywać na podstawie danych. Zapisuję plik „specyfikacji danych” JSON z nazwami i typami kolumn i używam go podczas przetwarzania danych. (Plik jest zwykle okropnym wyjściem BCP bez żadnych etykiet.) Typy danych, których używam, to ciągi, liczby zmiennoprzecinkowe, liczby całkowite lub daty miesięczne. Zamieniam ciągi na ints zapisując tabelę wyliczeń i przekształcam daty na ints (miesiące po 2000 r.), Więc pozostały mi tylko ints i zmienne w moich danych plus wyliczenie. Zapisuję floaty jako float64, ale eksperymentowałem z float32.
Johann Hibschman

1
jeśli masz czas, proszę wypróbuj tę zewnętrzną kompatybilność z R: pandas.pydata.org/pandas-docs/dev/… , a jeśli masz trudności, być może uda nam się to poprawić
Jeff

Spróbuję, jeśli będę mógł. rhdf5 jest problemem, ponieważ jest to pakiet bioprzewodników, a nie tylko CRAN jak h5r. Jestem na łasce naszego zespołu ds. Architektury technicznej, a ostatnio pojawił się problem z rhdf5. W każdym razie, po prostu wydaje się błędem iść zorientowany na wiersze, a nie na kolumny ze sklepem OLAP, ale teraz wędruję.
Johann Hibschman

38

Jedną sztuczką, która okazała się pomocna w przypadkach użycia dużych danych, jest zmniejszenie objętości danych poprzez zmniejszenie precyzji float do 32-bit. Nie ma zastosowania we wszystkich przypadkach, ale w wielu aplikacjach 64-bitowa precyzja to przesada, a 2x oszczędność pamięci jest tego warta. Aby uczynić oczywisty punkt jeszcze bardziej oczywistym:

>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB

>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB

26

Jak zauważyli inni, po kilku latach pojawił się ekwiwalent pand „poza rdzeniem”: dask . Chociaż dask nie zastępuje pand, a cała jego funkcjonalność wyróżnia się z kilku powodów:

Dask to elastyczna biblioteka obliczeń równoległych do obliczeń analitycznych, zoptymalizowana pod kątem dynamicznego planowania zadań dla interaktywnych obciążeń obliczeniowych zbiorów „Big Data”, takich jak tablice równoległe, ramki danych i listy, które rozszerzają popularne interfejsy, takie jak NumPy, Pandas lub iteratory Python, na większe- środowiska niż pamięciowe lub rozproszone i skalowane od laptopów do klastrów.

Dask podkreśla następujące zalety:

  • Znajomy: Zapewnia równoległą tablicę NumPy i obiekty Pandas DataFrame
  • Elastyczność: udostępnia interfejs planowania zadań dla większej liczby niestandardowych obciążeń i integracji z innymi projektami.
  • Natywny: Umożliwia przetwarzanie rozproszone w Pure Python z dostępem do stosu PyData.
  • Szybka: Działa z niskim narzutem, małym opóźnieniem i minimalną serializacją niezbędną do szybkich algorytmów numerycznych
  • Skalowanie w górę: Działa sprężyście w klastrach z tysiącami rdzeni Skalowanie w dół: Trywialne konfigurowanie i uruchamianie na laptopie w jednym procesie
  • Responsive: Zaprojektowany z myślą o komputerach interaktywnych, zapewnia szybką informację zwrotną i diagnostykę, aby pomóc ludziom

i dodać prosty przykładowy kod:

import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()

zastępuje niektóre kody pand takie jak to:

import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()

i, co szczególnie godne uwagi, zapewnia za pośrednictwem concurrent.futuresinterfejsu ogólną infrastrukturę do przesyłania niestandardowych zadań:

from dask.distributed import Client
client = Client('scheduler:port')

futures = []
for fn in filenames:
    future = client.submit(load, fn)
    futures.append(future)

summary = client.submit(summarize, futures)
summary.result()

Dodałem tę odpowiedź, ponieważ post @Private regularnie pojawia się na liście sugerowanych do usunięcia treści i długości.
wp78de

17

Warto również wspomnieć o tym Ray ,
jest to rozproszona platforma obliczeniowa, która ma własną implementację dla pand w sposób rozproszony.

Po prostu zastąp import pand, a kod powinien działać tak, jak jest:

# import pandas as pd
import ray.dataframe as pd

#use pd as usual

więcej szczegółów tutaj:

https://rise.cs.berkeley.edu/blog/pandas-on-ray/


16

Jeszcze jedna odmiana

Wiele operacji wykonywanych w pandach można również wykonać jako zapytanie db (sql, mongo)

Korzystanie z RDBMS lub mongodb pozwala na wykonanie niektórych agregacji w zapytaniu DB (które jest zoptymalizowane dla dużych danych i efektywnie wykorzystuje pamięć podręczną i indeksy)

Później możesz wykonać przetwarzanie końcowe za pomocą pand.

Zaletą tej metody jest to, że zyskujesz optymalizacje DB do pracy z dużymi danymi, a jednocześnie definiujesz logikę w deklaratywnej składni wysokiego poziomu - i nie musisz zajmować się szczegółami decydowania, co robić w pamięci, a co robić rdzeniowy.

I chociaż język zapytań i pandy są różne, zwykle nie jest skomplikowane tłumaczenie części logiki z jednej na drugą.


11

Rozważ Ruffus, jeśli pójdziesz prostą ścieżką tworzenia potoku danych, który jest podzielony na wiele mniejszych plików.


9

Ostatnio natknąłem się na podobny problem. Znalazłem po prostu czytanie danych w kawałkach i dołączanie ich, gdy piszę je w kawałkach do tego samego pliku csv, działa dobrze. Moim problemem było dodanie kolumny daty na podstawie informacji w innej tabeli, przy użyciu wartości niektórych kolumn w następujący sposób. Może to pomóc osobom zdezorientowanym przez dask i hdf5, ale lepiej znającym pandy takie jak ja.

def addDateColumn():
"""Adds time to the daily rainfall data. Reads the csv as chunks of 100k 
   rows at a time and outputs them, appending as needed, to a single csv. 
   Uses the column of the raster names to get the date.
"""
    df = pd.read_csv(pathlist[1]+"CHIRPS_tanz.csv", iterator=True, 
                     chunksize=100000) #read csv file as 100k chunks

    '''Do some stuff'''

    count = 1 #for indexing item in time list 
    for chunk in df: #for each 100k rows
        newtime = [] #empty list to append repeating times for different rows
        toiterate = chunk[chunk.columns[2]] #ID of raster nums to base time
        while count <= toiterate.max():
            for i in toiterate: 
                if i ==count:
                    newtime.append(newyears[count])
            count+=1
        print "Finished", str(chunknum), "chunks"
        chunk["time"] = newtime #create new column in dataframe based on time
        outname = "CHIRPS_tanz_time2.csv"
        #append each output to same csv, using no header
        chunk.to_csv(pathlist[2]+outname, mode='a', header=None, index=None)

8

Chciałbym zwrócić uwagę na pakiet Vaex.

Vaex to biblioteka Pythona dla leniwych Out-of-Core DataFrames (podobnych do Pand), do wizualizacji i eksploracji dużych tabelarycznych zestawów danych. Może obliczyć statystyki, takie jak średnia, suma, liczba, odchylenie standardowe itp., Na siatce N-wymiarowej do miliarda ( 109 ) obiektów / wierszy na sekundę. Wizualizacja odbywa się za pomocą histogramów, wykresów gęstości i renderowania objętości 3D, umożliwiając interaktywną eksplorację dużych danych. Vaex używa mapowania pamięci, zasady zerowego kopiowania pamięci i leniwych obliczeń w celu uzyskania najlepszej wydajności (bez marnowania pamięci).

Zajrzyj do dokumentacji: https://vaex.readthedocs.io/en/latest/ Interfejs API jest bardzo zbliżony do interfejsu API pand.


0

Dlaczego Pandy? Próbowałeś już Standard Python ?

Zastosowanie standardowego pytona bibliotecznego. Pandy podlegają częstym aktualizacjom, nawet w najnowszej wersji stabilnej wersji.

Za pomocą standardowej biblioteki python Twój kod będzie zawsze działał.

Jednym ze sposobów na zrobienie tego jest wyobrażenie sobie, w jaki sposób chcesz, aby twoje dane były przechowywane, i jakie pytania chcesz rozwiązać w odniesieniu do danych. Następnie narysuj schemat, w jaki sposób możesz uporządkować swoje dane (tabele myślowe), które pomogą ci zapytać o dane, niekoniecznie normalizację.

Możesz dobrze wykorzystać:

  • lista słowników do przechowywania danych w pamięci, przy czym jeden słownik to jeden wiersz,
  • generatory przetwarzające dane wiersz po wierszu, aby nie przepełnić pamięci RAM,
  • sporządzić listę ze zrozumieniem w celu przeszukiwania danych,
  • skorzystaj z Counter, DefaultDict, ...
  • przechowuj dane na dysku twardym, korzystając z dowolnego wybranego przez Ciebie rozwiązania do przechowywania, json może być jednym z nich.

Z czasem RAM i HDD stają się coraz tańsze, a standardowy python 3 jest powszechnie dostępny i stabilny.


-1

W tej chwili pracuję „jak” ty, tylko na niższą skalę, dlatego nie mam PoC dla mojej sugestii.

Wydaje mi się jednak, że odnajduję sukces w używaniu marynaty jako systemu buforowania i outsourcingu wykonywania różnych funkcji do plików - wykonywanie tych plików z mojego pliku komandosów / głównego; Na przykład używam pliku przygotuj_użytkownika.py do konwersji typów obiektów, podzielenia zestawu danych na testowy, sprawdzania poprawności i prognozowania.

Jak działa buforowanie marynowane? Używam ciągów w celu uzyskania dostępu do plików dynamicznych, które są tworzone dynamicznie, w zależności od tego, które parametry i zestawy danych zostały przekazane (dzięki temu próbuję przechwycić i ustalić, czy program był już uruchomiony, używając .shape dla zestawu danych, dykta dla przekazanego parametry). Przestrzegając tych środków, otrzymuję ciąg, aby spróbować znaleźć i odczytać plik .pickle i mogę, jeśli zostanie znaleziony, pominąć czas przetwarzania, aby przejść do wykonania, nad którym teraz pracuję.

Korzystając z baz danych, napotkałem podobne problemy i dlatego czerpałem radość z korzystania z tego rozwiązania, jednak - na pewno istnieje wiele ograniczeń - na przykład przechowywanie ogromnych zestawów pikli z powodu redundancji. Aktualizowanie tabeli przed i po transformacji można wykonać przy użyciu odpowiedniego indeksowania - sprawdzanie poprawności informacji otwiera całą inną książkę (próbowałem skonsolidować przeszukane dane czynszu i po 2 godzinach przestałem korzystać z bazy danych - ponieważ chciałbym po niej wrócić każdy proces transformacji)

Mam nadzieję, że moje 2 centy ci w jakiś sposób pomogą.

Pozdrowienia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.