Dodawanie meta-informacji / metadanych do pand DataFrame

90

Czy można dodać metainformacje / metadane do pandy DataFrame?

Na przykład nazwa instrumentu używanego do pomiaru danych, odpowiedzialny instrument itp.

Jednym obejściem byłoby utworzenie kolumny z tymi informacjami, ale przechowywanie pojedynczej informacji w każdym wierszu wydaje się marnotrawstwem!

P3trus
źródło
Zwróć uwagę na odpowiedź @ryanjdillon (obecnie ukrytą na samym dole), która wspomina o zaktualizowanym atrybucie eksperymentalnym „attrs”, który może wydawać się początkiem
JohnE

Odpowiedzi:

85

Oczywiście, podobnie jak większość obiektów Pythona, możesz dołączyć nowe atrybuty do pandas.DataFrame:

import pandas as pd
df = pd.DataFrame([])
df.instrument_name = 'Binky'

Należy jednak pamiętać, że podczas gdy można dołączyć atrybuty do DataFrame, operacje wykonywane na DataFrame (takich jak groupby, pivot, joinlub loc, aby wymienić tylko kilka) może powrócić nową DataFrame bez metadanych załączeniu. Pandy nie mają jeszcze solidnej metody propagowania metadanych dołączonych do ramek DataFrames .

Możliwe jest zachowanie metadanych w pliku . Przykład przechowywania metadanych w pliku HDF5 można znaleźć tutaj .

unutbu
źródło
5
+1 za wybór nazwy instrumentu! Czy masz jakieś doświadczenie w próbie zrzucenia tych dodatkowych atrybutów do HDFStore?
Dan Allan
4
@DanAllan: Jeśli store = pd.HDFStore(...), atrybuty mogą być przechowywane z store.root._v_attrs.key = value.
unutbu
3
Dla każdego, kto mógłby z tego skorzystać: dokumenty dodały sekcję na ten temat. pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore
Dan Allan
4
W pandach 0.23.1 utworzenie nowego atrybutu przez przypisanie słownika, listy lub krotki daje ostrzeżenie (tj. df = pd.DataFrame(); df.meta = {}Produkuje UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access). (Brak ostrzeżenia, jeśli atrybut został już utworzony jak w df = pd.DataFrame(); df.meta = ''; df.meta = {}).
teichert
13

Właśnie napotkałem ten problem. Począwszy od pandy 0.13, ramki DataFrames mają atrybut _metadata, który jest trwały przez funkcje zwracające nowe ramki DataFrames. Wydaje się również, że serializacja jest w porządku (próbowałem tylko json, ale wyobrażam sobie, że hdf jest również objęty).

szaleństwo
źródło
16
_metadatanie jest częścią publicznego API, więc zdecydowanie odradzam poleganie na tej funkcjonalności.
Shoyer
@Stephan, czy możesz to rozwinąć, proszę? Dlaczego ważne jest, aby być częścią publicznego interfejsu API? Czy twoje stwierdzenie jest również prawdziwe w przypadku wersji 0.15?
TomCho
1
@TomCho tak, ta odpowiedź jest nadal aktualna. Możesz rzucić okiem na xray ( github.com/xray/xray ), aby znaleźć alternatywny przykład oznaczonej tablicy, która obsługuje metadane, zwłaszcza jeśli masz dane wielowymiarowe ( .attrsjest częścią interfejsu API xray)
shoyer
17
_metadatajest w rzeczywistości atrybutem klasy, a nie atrybutem instancji. Tak więc nowe DataFrameinstancje dziedziczą po poprzednich, o ile moduł pozostaje załadowany. Nie używaj _metadatado niczego. +1 dla xarray!
j08lue
1
_metadata - nieobsługiwana funkcja, która uratowała mi dzień! Dziękuję Ci.
joctee
12

Nie całkiem. Chociaż można dodać atrybuty zawierające metadane do klasy DataFrame, o czym wspomina @unutbu, wiele metod DataFrame zwraca nową ramkę DataFrame, więc metadane zostałyby utracone. Jeśli musisz manipulować ramką danych, najlepszym rozwiązaniem byłoby umieszczenie metadanych i ramki DataFrame w innej klasie. Zobacz tę dyskusję na GitHub: https://github.com/pydata/pandas/issues/2485

Obecnie istnieje otwarte żądanie ściągnięcia, aby dodać obiekt MetaDataFrame, który lepiej obsługiwałby metadane.

Matti John
źródło
11

Począwszy od pandy 1.0, prawdopodobnie wcześniej, istnieje teraz Dataframe.attrswłaściwość. Jest eksperymentalny, ale prawdopodobnie tego będziesz chciał w przyszłości. Na przykład:

import pandas as pd
df = pd.DataFrame([])
df.attrs['instrument_name'] = 'Binky'

Znajdź to w dokumentach tutaj .

Wypróbowanie tego z to_parqueta potem from_parquetwydaje się nie utrzymywać się, więc upewnij się, że sprawdziłeś to w swoim przypadku użycia.

ryanjdillon
źródło
Jest to interesujące i wydaje się utrzymywać w przypadku copy / loc / iloc, ale nie dla groupby.
JohnE
Tylko sugestia, ale może pokażesz przykład, jak z niego korzystać? Dokumentacja to w zasadzie nic, ale po zabawie z nią widzę, że jest zainicjowana jako pusty słownik i wydaje się, że jest skonfigurowana tak, że ma to być słownik, chociaż oczywiście można w nim zagnieździć listę, na przykład.
JohnE
1
Ta dyskusja
Stackoverflow
1
@rdmolony To świetnie. Myślę, że użycie a dataclassdla metadanych, a następnie podklasy, DataFrameaby mieć metodę wykonującą ładowanie / zrzucanie, jak w udostępnionym poście, może być dobrym rozwiązaniem.
ryanjdillon
1
To jest miłe. W przeciwieństwie do akceptowanej odpowiedzi, to zachowuje atrybuty po zapisaniu i załadowaniu z marynaty!
CGFoX
8

Najlepsza odpowiedź dotycząca dołączania dowolnych atrybutów do obiektu DataFrame jest dobra, ale jeśli używasz słownika, listy lub krotki, wyemituje błąd „Pandy nie pozwalają na tworzenie kolumn za pomocą nowej nazwy atrybutu”. Poniższe rozwiązanie działa w przypadku przechowywania dowolnych atrybutów.

from types import SimpleNamespace
df = pd.DataFrame()
df.meta = SimpleNamespace()
df.meta.foo = [1,2,3]
bscan
źródło
Ponadto, jeśli chcesz, aby to utrzymywało się na wszystkich kopiach ramki danych, musisz to zrobić pd.DataFrame._metadata += ["meta"]. Zwróć uwagę, że ta część jest atrybutem Pand, a nie atrybutem konkretnej ramki danych
bscan
To podejście nie będzie już działać, ponieważ df.metawyzwala ostrzeżenie, że Pandy nie pozwalają na generowanie nowych kolumn w ten sposób.
anishtain4
@ anishtain4, właśnie przetestowałem go z Pandami 25.1 (wydanymi ~ 2 tygodnie temu) i ten kod nadal działa dla mnie. To ostrzeżenie nie jest wyzwalane, ponieważ df.metajest to SimpleNamespace. Pandy nie będą próbowały zbudować z niego kolumny.
bscan
6

Jak wspomniano w innych odpowiedziach i komentarzach, _metadatanie jest częścią publicznego API, więc zdecydowanie nie jest dobrym pomysłem używanie go w środowisku produkcyjnym. Ale nadal możesz chcieć użyć go w prototypowaniu badawczym i zastąpić go, jeśli przestanie działać. Teraz działa z groupby/ apply, co jest pomocne. Oto przykład (którego nie mogłem znaleźć w innych odpowiedziach):

df = pd.DataFrame([1, 2, 2, 3, 3], columns=['val']) 
df.my_attribute = "my_value"
df._metadata.append('my_attribute')
df.groupby('val').apply(lambda group: group.my_attribute)

Wynik:

val
1    my_value
2    my_value
3    my_value
dtype: object
Dennis Golomazov
źródło
4

Dochodząc do tego dość późno, pomyślałem, że może to być pomocne, jeśli potrzebujesz metadanych, aby przetrwać we / wy. Jest stosunkowo nowy pakiet o nazwie h5io, którego używałem , aby to osiągnąć.

Powinien umożliwić szybki odczyt / zapis z HDF5 dla kilku popularnych formatów, z których jeden to ramka danych. Możesz więc na przykład umieścić ramkę danych w słowniku i dołączyć metadane jako pola w słowniku. Na przykład:

save_dict = dict(data=my_df, name='chris', record_date='1/1/2016')
h5io.write_hdf5('path/to/file.hdf5', save_dict)
in_data = h5io.read_hdf5('path/to/file.hdf5')
df = in_data['data']
name = in_data['name']
etc...

Inną opcją byłoby przyjrzenie się projektowi, takim jak xray , który jest pod pewnymi względami bardziej złożony, ale myślę, że pozwala na użycie metadanych i jest dość łatwy do przekonwertowania na ramkę DataFrame.

choldgraf
źródło
4

Jak wspomniał @choldgraf, stwierdziłem, że xarray jest doskonałym narzędziem do dołączania metadanych podczas porównywania danych i wykreślania wyników między kilkoma ramkami danych.

W mojej pracy często porównujemy wyniki kilku wersji oprogramowania sprzętowego i różnych scenariuszy testowych, a dodanie tych informacji jest tak proste:

df = pd.read_csv(meaningless_test)
metadata = {'fw': foo, 'test_name': bar, 'scenario': sc_01}
ds = xr.Dataset.from_dataframe(df)
ds.attrs = metadata
jtwilson
źródło
2

Szukałem rozwiązania i stwierdziłem, że rama pandy ma tę właściwość attrs

pd.DataFrame().attrs.update({'your_attribute' : 'value'})
frame.attrs['your_attribute']

Ten atrybut będzie zawsze trzymał się twojej ramki, gdy go miniesz!

Ayrat Arifullin
źródło
Zauważ, że atrs jest eksperymentalne i może ulec zmianie bez ostrzeżenia, ale jest to bardzo proste rozwiązanie. Zastanawiam się, czy atrs przesyła się do nowych ramek danych.
Liquidgenius
Niestety atrybuty nie są kopiowane do nowych ramek danych :(
Adam
1

Miałem ten sam problem i zastosowałem obejście polegające na utworzeniu nowego, mniejszego DF ze słownika z metadanymi:

    meta = {"name": "Sample Dataframe", "Created": "19/07/2019"}
    dfMeta = pd.DataFrame.from_dict(meta, orient='index')

Ten plik dfMeta można następnie zapisać wraz z oryginalnym DF w marynacie itp

Zobacz Zapisywanie i ładowanie wielu obiektów w pliku marynaty? (Odpowiedź Lutza) za doskonałą odpowiedź na temat zapisywania i pobierania wielu ramek danych za pomocą pikle

SenAnan
źródło