Utworzenie pustej ramki danych Pandas, a następnie wypełnienie jej?

461

Zaczynam od dokumentów DataFrame pand tutaj: http://pandas.pydata.org/pandas-docs/stable/dsintro.html

Chciałbym iteracyjnie wypełnić DataFrame wartościami w rodzaju obliczeń szeregów czasowych. Zasadniczo chciałbym zainicjować ramkę danych z kolumnami A, B i wierszami znaczników czasu, wszystkie 0 lub wszystkie NaN.

Następnie dodałbym wartości początkowe i przejrzałem te dane, obliczając nowy wiersz z rzędu przed, powiedzmy row[A][t] = row[A][t-1]+1lub tak.

Obecnie używam kodu jak poniżej, ale czuję, że jest to trochę brzydkie i musi istnieć sposób, aby to zrobić bezpośrednio z DataFrame, lub po prostu lepszy sposób w ogóle. Uwaga: używam Python 2.7.

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

    valdict = {}
    symbols = ['A','B', 'C']
    for symb in symbols:
        valdict[symb] = pd.Series( s.zeros( len(dates)), dates )

    for thedate in dates:
        if thedate > dates[0]:
            for symb in valdict:
                valdict[symb][thedate] = 1+valdict[symb][thedate - dt.timedelta(days=1)]

    print valdict
Matthias Kauer
źródło
5
Nigdy nie rozwijaj DataFrame! Zawsze taniej jest dołączyć do listy python, a następnie przekonwertować ją na DataFrame na końcu, zarówno pod względem pamięci, jak i wydajności.
cs95
@ cs95 Czym różni się funkcjonalnie .appendw pd od dołączania listy? Wiem, że .appendw pandach kopiuje cały zestaw danych do nowego obiektu ´, czy dołączane pytony działają inaczej?
Lamma
@ Lamma proszę znaleźć szczegóły w mojej odpowiedzi poniżej. Dołączając do df, nowa DataFrame jest tworzona za każdym razem w pamięci, zamiast używać istniejącej, co jest dość szczerze mówiąc marnotrawstwem.
cs95

Odpowiedzi:

330

Oto kilka sugestii:

Użyj date_rangedla indeksu:

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

Uwaga: możemy utworzyć pustą ramkę danych (z NaNs), pisząc:

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # with 0s rather than NaNs

Aby wykonać tego typu obliczenia dla danych, użyj tablicy numpy:

data = np.array([np.arange(10)]*3).T

Dlatego możemy stworzyć DataFrame:

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]: 
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9
Andy Hayden
źródło
2
pd.date_range () nie działa dla mnie. Próbowałem z DateRange (z autouzupełniania Eclipse), ale to działa z ciągami jako format daty, prawda? Ogólne podejście działa jednak (zmieniłem indeks na coś innego).
Matthias Kauer,
2
date_range to fabryczna funkcja do tworzenia indeksów datetime i była nową funkcją w wersji 0.8.0 , zdecydowanie polecam aktualizację do najnowszej stabilnej wersji (0.9.1), istnieje wiele poprawek błędów i nowych funkcji. :)
Andy Hayden,
26
Z moich doświadczeń wynika, że ​​tworzenie ramki danych o niezbędnym rozmiarze wypełnionej NaN, a następnie wypełnianie wartościami jest znacznie wolniejsze niż tworzenie ramki danych o wymiarach indexx 0( columns = []) i dołączanie jednej kolumny w każdym zwoju pętli. Mam na myśli df[col_name] = pandas.Series([...])w pętli iterującej nazwy kolumn. W pierwszym przypadku nie tylko przydział pamięci zajmuje dużo czasu, ale zastępowanie NaN nowymi wartościami wydaje się niezwykle powolne.
Deeenes
5
@deeenes zdecydowanie. ta odpowiedź powinna prawdopodobnie uczynić to bardziej zrozumiałym - bardzo rzadko (jeśli w ogóle) chcesz utworzyć pustą ramkę danych (NaN).
Andy Hayden
1
Zgodnie z tą odpowiedzią stackoverflow.com/a/30267881/2302569 Musisz przypisać wynik funkcji fillna lub przekazać parametr w miejsce = True
JayJay
169

Jeśli chcesz po prostu utworzyć pustą ramkę danych i wypełnić ją później niektórymi ramkami danych przychodzących, spróbuj tego:

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

W tym przykładzie używam tego dokumentu pandy do utworzenia nowej ramki danych, a następnie używam append do zapisu do newDF z danymi ze staregoDF.

Jeśli muszę dodawać nowe dane do tego nowego pliku z więcej niż jednego starego pliku, po prostu używam pętli for, aby iterować po pandas.DataFrame.append ()

geekidharsh
źródło
14
Należy pamiętać, że append(i podobnie concat) za każdym razem kopiuje pełny zestaw danych do nowego obiektu, dlatego iteracja i dołączanie może i spowoduje znaczny spadek wydajności. Aby uzyskać więcej informacji, patrz: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAAtta
4
@MoustafaAAtta Jakie są alternatywy, aby dołączyć iteracyjnie dane do ramki danych?
MysteryGuy,
2
@MoustafaAAtta Czy Fred odpowiedział w tym poście: stackoverflow.com/questions/10715965/... lepiej z tego punktu widzenia?
MysteryGuy,
@MoustafaAAtta możesz być może dołączyć tylko wiersze do ramki danych, nadal będzie tworzyć nowy obiekt, ale dla mniejszych zestawów danych może być przydatne. pandas.pydata.org/pandas-docs/stable/user_guide/…
geekidharsh
135

Właściwy sposób ™ do utworzenia DataFrame

TLDR; (po prostu przeczytaj pogrubiony tekst)

Większość odpowiedzi tutaj powie ci, jak utworzyć pustą ramkę danych i wypełnić ją, ale nikt nie powie ci, że to źle.

Oto moja rada: poczekaj, aż będziesz mieć pewność, że masz wszystkie dane, z którymi musisz pracować. Użyj listy, aby zebrać swoje dane, a następnie zainicjuj ramkę DataFrame, gdy będziesz gotowy.

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

To jest zawsze tańsze, aby dołączyć do listy i utworzyć DataFrame za jednym razem , niż jest, aby utworzyć pusty DataFrame (lub jedną z Nans) i dołączyć do niej w kółko. Listy zajmują również mniej pamięci i są znacznie lżejszą strukturą danych do pracy , dołączania i usuwania (w razie potrzeby).

Inną zaletą tej metody jest dtypesautomatyczne wnioskowanie (zamiast przypisywania objectich wszystkim).

Ostatnią zaletą jest to, że jest tworzony automatycznie dla swoich danych , więc jest to jeden mniej rzeczy się martwić (spójrz na biednych i metodach poniżej, widać zarówno elementy, które wymagają obsługi indeksu odpowiednio).RangeIndexappendloc


Rzeczy, których NIE powinieneś robić

appendlub concatwewnątrz pętli

Oto największy błąd, jaki widziałem od początkujących:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

Pamięć jest przydzielana ponownie dla każdej operacji appendlub concatoperacji. Połącz to z pętlą, a otrzymasz kwadratową operację złożoności . Ze strony df.appenddokumentu :

Iteracyjne dołączanie wierszy do DataFrame może być bardziej wymagające obliczeniowo niż pojedynczy konkatenat. Lepszym rozwiązaniem jest dodanie tych wierszy do listy, a następnie połączenie listy z oryginalną ramką DataFrame jednocześnie.

Innym błędem związanym z df.appendtym jest to, że użytkownicy często zapominają, że append nie jest funkcją lokalną , więc wynik należy przypisać z powrotem. Musisz także martwić się o typy:

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

Radzenie sobie z kolumnami obiektów nigdy nie jest dobrą rzeczą, ponieważ pandy nie mogą wektoryzować operacji na tych kolumnach. Musisz to zrobić, aby to naprawić:

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc wewnątrz pętli

Widziałem również locużywane do dołączania do DataFrame, który został utworzony pusty:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

Tak jak poprzednio, nie przydzielono wstępnie wymaganej ilości pamięci za każdym razem, więc pamięć jest odnawiana za każdym razem, gdy tworzysz nowy wiersz . Jest tak samo zły, jak appendi jeszcze bardziej brzydki.

Pusta ramka danych dla NaNs

Następnie powstaje ramka danych NaNs i wszystkie związane z tym zastrzeżenia.

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

Tworzy ramkę danych kolumn obiektowych, podobnie jak inne.

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

Dołączanie nadal ma wszystkie problemy, jak powyższe metody.

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]

Dowód jest w budyniu

Pomiar tych metod jest najszybszym sposobem, aby zobaczyć, jak bardzo różnią się one pod względem pamięci i użyteczności.

wprowadź opis zdjęcia tutaj

Kod porównawczy w celach informacyjnych.

cs95
źródło
6
Dołączanie
9
Trzeba to ocenić milion razy więcej. Nigdy nie rozwijaj ramki danych!
Buggy,
3
@ user3293236 Szkoda, trzeba zacząć od dołu za każdym razem odpowiedzieć starą pytanie;)
CS95
2
To jedna z rzeczy, których najbardziej nienawidzę. Te wiele razy widzisz 𝒓𝒆𝒂𝒍 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 𝒂𝒏𝒔𝒘𝒆𝒓, który po prostu pozostaje gdzieś z niewielką liczbą głosów i nigdy nie został zaakceptowany. Brakuje mi kodu 𝚍𝚏 = 𝚙𝚍.𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]), aby utworzyć pustą ramkę danych pandy. Poprawienie tej odpowiedzi. Doskonałe wyjaśnienie, @ cs95!
jonathan
1
Jest to dosłownie w dokumentacji. „Iteracyjne dołączanie wierszy do DataFrame może być bardziej wymagające obliczeniowo niż pojedynczy konkatenat. Lepszym rozwiązaniem jest dodanie tych wierszy do listy, a następnie konkatenacja listy z oryginalną DataFrame jednocześnie”. pandas.pydata.org/pandas-docs/version/0.21/generated/...
endolit
132

Zainicjuj pustą ramkę za pomocą nazw kolumn

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

Dodaj nowy rekord do ramki

my_df.loc[len(my_df)] = [2, 4, 5]

Możesz także przekazać słownik:

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

Dołącz kolejną ramkę do istniejącej ramki

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

Uwagi dotyczące wydajności

Jeśli dodajesz wiersze wewnątrz pętli, rozważ problemy z wydajnością. W przypadku około 1000 pierwszych rekordów wydajność „my_df.loc” jest lepsza, ale stopniowo staje się wolniejsza przez zwiększenie liczby rekordów w pętli.

Jeśli planujesz robić cienkie elementy w dużej pętli (powiedzmy, że około 10M‌ rekordów), lepiej jest użyć kombinacji tych dwóch; wypełnij ramkę danych iloc, aż rozmiar osiągnie około 1000, następnie dołącz ją do oryginalnej ramki danych i opróżnij tymczasową ramkę danych. Zwiększy to twoją wydajność około 10 razy.

Afshin Amiri
źródło
my_df = my_df.append(my_df2)nie działa dla mnie, chyba że podam ignore_index=True.
Nasif Imtiaz Ohi
0

Załóżmy ramkę danych z 19 wierszami

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

Utrzymywanie kolumny A jako stałej

test['A']=10

Zachowanie kolumny b jako zmiennej podanej przez pętlę

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

Możesz zamienić pierwsze x na pd.Series([x], index = [x])dowolną wartość

Ajay Ohri
źródło