Jak zrobić dobre, powtarzalne przykłady pand
Po spędzeniu przyzwoitej ilości czasu na oglądaniu zarówno znaczników r, jak i pandy na SO, mam wrażenie, że pandas
pytania z mniejszym prawdopodobieństwem zawierają odtwarzalne dane. To jest coś, że społeczność R była dobra ładna o zachęcanie, a dzięki przewodników jak to nowicjusze są w stanie uzyskać jakąś pomoc na łącząc te przykłady. Osoby, które są w stanie przeczytać te przewodniki i wrócić z odtwarzalnymi danymi, często będą miały dużo więcej szczęścia w uzyskaniu odpowiedzi na swoje pytania.
Jak możemy stworzyć dobre, powtarzalne przykłady pandas
pytań? Proste ramki danych można łączyć, np .:
import pandas as pd
df = pd.DataFrame({'user': ['Bob', 'Jane', 'Alice'],
'income': [40000, 50000, 42000]})
Jednak wiele przykładowych zbiorów danych wymaga bardziej skomplikowanej struktury, np .:
datetime
indeksy lub dane- Wiele zmiennych kategorialnych (czy istnieje odpowiednik
expand.grid()
funkcji R , która daje wszystkie możliwe kombinacje pewnych podanych zmiennych?) - Dane MultiIndex lub panelu
W przypadku zestawów danych, które są trudne do makiety przy użyciu kilku wierszy kodu, czy istnieje odpowiednik języka R, dput()
który umożliwia generowanie kodu do skopiowania i wklejania w celu ponownego wygenerowania struktury danych?
Odpowiedzi
Uwaga: Pomysły tutaj są dość ogólne dla przepełnienia stosu, a nawet pytań .
Zastrzeżenie: napisanie dobrego pytania jest TRUDNE.
Dobra:
dołącz mały * przykład DataFrame, albo jako kod do uruchomienia:
In [1]: df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])
lub ustaw go jako „kopiuj i wklej” za pomocą
pd.read_clipboard(sep='\s\s+')
, możesz sformatować tekst do podświetlenia przepełnienia stosu i użyć Ctrl+ K(lub wstawić cztery spacje na początku każdego wiersza) lub umieścić trzy tyldy powyżej i poniżej kodu bez wcięcia kodu:In [2]: df Out[2]: A B 0 1 2 1 1 3 2 4 6
sprawdź
pd.read_clipboard(sep='\s\s+')
się.* Naprawdę mam na myśli małe , zdecydowana większość przykładowych ramek DataFrames może mieć mniej niż 6 wierszy potrzebnych cytowania i założę się, że mogę to zrobić w 5 wierszach. Czy możesz odtworzyć błąd za pomocą
df = df.head()
, jeśli nie, pogarszać się, aby sprawdzić, czy możesz stworzyć małą ramkę DataFrame, która pokazuje problem, z którym masz do czynienia.* Każda reguła ma wyjątek, oczywistym jest dla kwestii wydajności ( w tym przypadku na pewno użyć% timeit i ewentualnie% prun ), gdzie należy wygenerować (rozważyć użycie np.random.seed więc mamy dokładnie taką samą ramkę)
df = pd.DataFrame(np.random.randn(100000000, 10))
. Powiedzenie, że „zrób dla mnie ten kod szybko” nie jest ściśle związane z tematem witryny ...napisz pożądany rezultat (podobnie jak powyżej)
In [3]: iwantthis Out[3]: A B 0 1 5 1 4 6
Wyjaśnij, skąd pochodzą liczby: 5 to suma kolumny B w wierszach, w których A wynosi 1.
pokaż kod , który wypróbowałeś:
In [4]: df.groupby('A').sum() Out[4]: B A 1 5 4 6
Ale powiedz, co jest nieprawidłowe: kolumna A znajduje się w indeksie, a nie w kolumnie.
pokaż, że zrobiłeś jakieś badania ( przeszukaj dokumenty , wyszukaj StackOverflow ), podaj podsumowanie:
Dokumentacja sum zawiera po prostu „Oblicz sumę wartości grupy”
Dokumentacja Groupby nie podaje na to żadnych przykładów.
Na bok: odpowiedzią jest użycie
df.groupby('A', as_index=False).sum()
.jeśli jest to istotne, że masz kolumny Timestamp, np. próbujesz ponownie próbkować lub coś w tym rodzaju, powiedz to wyraźnie i stosuj się
pd.to_datetime
do nich na wszelki wypadek **.df['date'] = pd.to_datetime(df['date']) # this column ought to be date..
** Czasami jest to sam problem: były to struny.
Źli:
nie dołączaj MultiIndex, którego nie możemy skopiować i wkleić (patrz wyżej), jest to rodzaj zażalenia z domyślnym wyświetlaniem pand, ale mimo to denerwujące:
In [11]: df Out[11]: C A B 1 2 3 2 6
Poprawnym sposobem jest dołączenie zwykłej ramki DataFrame do set_indexwywołania:
In [12]: df = pd.DataFrame([[1, 2, 3], [1, 2, 6]], columns=['A', 'B', 'C']).set_index(['A', 'B']) In [13]: df Out[13]: C A B 1 2 3 2 6
podaj wgląd w to, co to jest, dając oczekiwany wynik:
B A 1 1 5 0
Określ dokładnie, w jaki sposób otrzymałeś liczby (jakie one są) ... dokładnie sprawdź, czy są poprawne.
Jeśli kod generuje błąd, dołącz cały ślad stosu (można go później edytować, jeśli jest zbyt głośny). Pokaż numer linii (i odpowiednią linię twojego kodu, przeciwko której jest podnoszony).
Brzydki:
nie linkuj do pliku csv, do którego nie mamy dostępu (najlepiej w ogóle nie linkuj do zewnętrznego źródła ...)
df = pd.read_csv('my_secret_file.csv') # ideally with lots of parsing options
Większość danych jest zastrzeżona , otrzymujemy to: Zrób podobne dane i zobacz, czy możesz odtworzyć problem (coś małego).
nie wyjaśniaj sytuacji niejasno słowami, tak jak w przypadku ramki DataFrame, która jest „duża”, wymień niektóre nazwy kolumn na marginesie (pamiętaj, aby nie podawać ich typów). Spróbuj podać wiele szczegółów na temat czegoś, co jest całkowicie bez znaczenia, nie widząc faktycznego kontekstu. Prawdopodobnie nikt nie zamierza nawet czytać do końca tego akapitu.
Eseje są złe, łatwiej z małymi przykładami.
nie uwzględniaj ponad 10 (100+ ??) wierszy danych, zanim przejdziesz do właściwego pytania.
Proszę, widzimy tego wystarczająco dużo w naszej codziennej pracy. Chcemy pomóc, ale nie tak ... .
Wytnij wprowadzenie i po prostu pokaż odpowiednie ramki danych (lub ich małe wersje) w kroku, który sprawia Ci kłopoty.
W każdym razie baw się dobrze, ucząc się Pythona, NumPy i Pandy!
Jak tworzyć przykładowe zbiory danych
Ma to na celu głównie rozwinięcie odpowiedzi @ AndyHayden przez podanie przykładów tworzenia przykładowych ramek danych. Pandy i (szczególnie) numpy dają ci do tego różnorodne narzędzia, dzięki którym możesz ogólnie stworzyć rozsądną kopię dowolnego prawdziwego zbioru danych za pomocą zaledwie kilku linii kodu.
Po zaimportowaniu numpy i pandas, pamiętaj o podaniu losowego ziarna, jeśli chcesz, aby ludzie mogli dokładnie odtworzyć twoje dane i wyniki.
import numpy as np
import pandas as pd
np.random.seed(123)
Przykład zlewu kuchennego
Oto przykład pokazujący różne rzeczy, które możesz zrobić. Z podzbioru można utworzyć wszelkiego rodzaju przydatne przykładowe ramki danych:
df = pd.DataFrame({
# some ways to create random data
'a':np.random.randn(6),
'b':np.random.choice( [5,7,np.nan], 6),
'c':np.random.choice( ['panda','python','shark'], 6),
# some ways to create systematic groups for indexing or groupby
# this is similar to r's expand.grid(), see note 2 below
'd':np.repeat( range(3), 2 ),
'e':np.tile( range(2), 3 ),
# a date range and set of random dates
'f':pd.date_range('1/1/2011', periods=6, freq='D'),
'g':np.random.choice( pd.date_range('1/1/2011', periods=365,
freq='D'), 6, replace=False)
})
To daje:
a b c d e f g
0 -1.085631 NaN panda 0 0 2011-01-01 2011-08-12
1 0.997345 7 shark 0 1 2011-01-02 2011-11-10
2 0.282978 5 panda 1 0 2011-01-03 2011-10-30
3 -1.506295 7 python 1 1 2011-01-04 2011-09-07
4 -0.578600 NaN shark 2 0 2011-01-05 2011-02-27
5 1.651437 7 python 2 1 2011-01-06 2011-02-03
Kilka uwag:
np.repeat
inp.tile
(kolumnyd
ie
) są bardzo przydatne do tworzenia grup i indeksów w bardzo regularny sposób. W przypadku 2 kolumn można to wykorzystać do łatwego duplikowania r,expand.grid()
ale jest również bardziej elastyczne pod względem możliwości zapewnienia podzbioru wszystkich permutacji. Jednak dla 3 lub więcej kolumn składnia szybko staje się nieporęczna.- Aby uzyskać bardziej bezpośredni zamiennik r,
expand.grid()
zobaczitertools
rozwiązanie w książce kucharskiej pandy lubnp.meshgrid
rozwiązanie pokazane tutaj . Pozwolą one na dowolną liczbę wymiarów. - Możesz zrobić całkiem sporo z
np.random.choice
. Na przykład w kolumnieg
mamy losowy wybór 6 dat z 2011 roku. Dodatkowo ustawiającreplace=False
możemy zapewnić, że te daty są unikalne - bardzo przydatne, jeśli chcemy użyć tego jako indeksu z unikalnymi wartościami.
Fałszywe dane giełdowe
Oprócz pobierania podzbiorów powyższego kodu, możesz dalej łączyć techniki, aby zrobić prawie wszystko. Na przykład, oto krótki przykład, który łączy np.tile
i date_range
tworzy przykładowe dane giełdowe dla 4 akcji z tych samych dat:
stocks = pd.DataFrame({
'ticker':np.repeat( ['aapl','goog','yhoo','msft'], 25 ),
'date':np.tile( pd.date_range('1/1/2011', periods=25, freq='D'), 4 ),
'price':(np.random.randn(100).cumsum() + 10) })
Teraz mamy przykładowy zestaw danych zawierający 100 linii (25 dat na pasek ruchu), ale użyliśmy do tego tylko 4 wierszy, co ułatwia wszystkim innym reprodukcję bez kopiowania i wklejania 100 linii kodu. Następnie możesz wyświetlić podzbiory danych, jeśli pomoże to wyjaśnić pytanie:
>>> stocks.head(5)
date price ticker
0 2011-01-01 9.497412 aapl
1 2011-01-02 10.261908 aapl
2 2011-01-03 9.438538 aapl
3 2011-01-04 9.515958 aapl
4 2011-01-05 7.554070 aapl
>>> stocks.groupby('ticker').head(2)
date price ticker
0 2011-01-01 9.497412 aapl
1 2011-01-02 10.261908 aapl
25 2011-01-01 8.277772 goog
26 2011-01-02 7.714916 goog
50 2011-01-01 5.613023 yhoo
51 2011-01-02 6.397686 yhoo
75 2011-01-01 11.736584 msft
76 2011-01-02 11.944519 msft
Diary of an Answerer
Moją najlepszą radą przy zadawaniu pytań byłoby granie na psychologii ludzi, którzy odpowiadają na pytania. Będąc jedną z tych osób, mogę wyjaśnić, dlaczego odpowiadam na niektóre pytania, a na inne nie.
Motywacje
Jestem zmotywowany do odpowiadania na pytania z kilku powodów
- Stackoverflow.com był dla mnie niezwykle cennym źródłem informacji. Chciałem oddać.
- W moich wysiłkach, aby się odwdzięczyć, odkryłem, że ta witryna jest jeszcze potężniejszym źródłem niż wcześniej. Odpowiadanie na pytania to dla mnie nauka i lubię się uczyć. Przeczytaj tę odpowiedź i komentarz od innego weterynarza . Ten rodzaj interakcji mnie uszczęśliwia.
- Lubię punkty!
- Zobacz nr 3.
- Lubię ciekawe problemy.
Wszystkie moje najczystsze intencje są wielkie i wszystkie, ale czerpię satysfakcję, jeśli odpowiem na 1 pytanie lub 30. To, co kieruje moimi wyborami, na które mam odpowiedzieć, ma ogromny składnik maksymalizacji punktów.
Spędzę też czas na interesujących problemach, ale jest ich niewiele i nie pomoże pytającemu, który potrzebuje rozwiązania nieinteresującego pytania. Najlepszym sposobem, aby skłonić mnie do odpowiedzi na pytanie, jest zaserwowanie tego pytania na tacy, na której mogę odpowiedzieć przy jak najmniejszym wysiłku. Jeśli patrzę na dwa pytania, a jedno ma kod, mogę skopiować wklej, aby utworzyć wszystkie potrzebne mi zmienne ... Biorę to! Wrócę do tego drugiego, jeśli będę miał czas.
Główna rada
Ułatw ludziom odpowiadanie na pytania.
- Podaj kod, który tworzy potrzebne zmienne.
- Zminimalizuj ten kod. Jeśli moje oczy błyszczą, gdy patrzę na wpis, przechodzę do następnego pytania lub wracam do tego, co robię.
- Zastanów się, o co pytasz i bądź konkretny. Chcemy zobaczyć, co zrobiłeś, ponieważ języki naturalne (angielski) są niedokładne i zagmatwane. Próbki kodu tego, co wypróbowałeś, pomagają rozwiązać niespójności w opisie w języku naturalnym.
- PROSZĘ pokazać, czego oczekujesz !!! Muszę usiąść i spróbować różnych rzeczy. Prawie nigdy nie znam odpowiedzi na pytanie bez wypróbowania kilku rzeczy. Jeśli nie widzę przykładu tego, czego szukasz, mogę przekazać pytanie, ponieważ nie mam ochoty zgadywać.
Twoja reputacja to coś więcej niż tylko reputacja.
Lubię punkty (wspomniałem o tym powyżej). Ale te punkty nie są tak naprawdę moją reputacją. Moja prawdziwa reputacja jest połączeniem tego, co myślą o mnie inni użytkownicy witryny. Staram się być uczciwy i uczciwy i mam nadzieję, że inni to dostrzegają. Dla pytającego oznacza to, że pamiętamy zachowania pytających. Jeśli nie wybierasz odpowiedzi i nie głosujesz pozytywnie, to pamiętam. Pamiętam, jeśli zachowujesz się w sposób, który mi się nie podoba lub mi się podoba. Dotyczy to również pytań, na które odpowiem.
W każdym razie, prawdopodobnie mogę kontynuować, ale oszczędzę was wszystkich, którzy faktycznie to czytali.
Wyzwanie Jednym z najtrudniejszych aspektów odpowiadania na pytania SO jest czas potrzebny na odtworzenie problemu (w tym danych). Odpowiedzi na pytania, które nie mają jasnego sposobu odtworzenia danych, są mniej prawdopodobne. Biorąc pod uwagę, że poświęcasz czas na napisanie pytania i masz problem, z którym chciałbyś uzyskać pomoc, możesz łatwo pomóc sobie, dostarczając dane, które inni mogą następnie wykorzystać do rozwiązania Twojego problemu.
Instrukcje podane przez @Andy dotyczące pisania dobrych pytań o Pandy są doskonałym miejscem do rozpoczęcia. Aby uzyskać więcej informacji, zobacz, jak zadawać pytania i jak tworzyć przykłady minimalne, kompletne i weryfikowalne .
Prosimy o wyraźne sformułowanie pytania z góry. Po poświęceniu czasu na napisanie pytania i przykładowego kodu, spróbuj je przeczytać i dostarczyć czytelnikowi „Streszczenie”, które podsumowuje problem i jasno określa pytanie.
Oryginalne pytanie :
Mam te dane ...
Chce to zrobić...
Chcę, żeby mój wynik wyglądał tak ...
Jednak kiedy próbuję to zrobić, pojawia się następujący problem ...
Próbowałem znaleźć rozwiązania, robiąc [to] i [tamto].
Jak to naprawić?
W zależności od ilości danych, przykładowego kodu i dostarczonych stosów błędów, czytelnik musi przejść długą drogę, zanim zrozumie, na czym polega problem. Spróbuj powtórzyć pytanie, tak aby samo pytanie znajdowało się na górze, a następnie podaj niezbędne szczegóły.
Poprawione pytanie :
Pytanie: Jak mogę to zrobić?
Próbowałem znaleźć rozwiązania, robiąc [to] i [tamto].
Kiedy próbuję to zrobić, pojawia się następujący problem ...
Chciałbym, żeby moje ostateczne wyniki wyglądały tak ...
Oto minimalny kod, który może odtworzyć mój problem ...
A oto jak odtworzyć moje przykładowe dane:
df = pd.DataFrame({'A': [...], 'B': [...], ...})
PODAJ PRZYKŁADOWE DANE, JEŚLI POTRZEBNE !!!
Czasami wystarczy tylko początek lub koniec ramki DataFrame. Możesz także użyć metod zaproponowanych przez @JohnE do tworzenia większych zbiorów danych, które mogą być odtwarzane przez innych. Na jego przykładzie wygeneruj ramkę danych DataFrame zawierającą 100 wierszy:
stocks = pd.DataFrame({
'ticker':np.repeat( ['aapl','goog','yhoo','msft'], 25 ),
'date':np.tile( pd.date_range('1/1/2011', periods=25, freq='D'), 4 ),
'price':(np.random.randn(100).cumsum() + 10) })
Jeśli to były twoje rzeczywiste dane, możesz po prostu dołączyć nagłówek i / lub koniec ramki danych w następujący sposób (pamiętaj o anonimizacji wszelkich danych wrażliwych):
>>> stocks.head(5).to_dict()
{'date': {0: Timestamp('2011-01-01 00:00:00'),
1: Timestamp('2011-01-01 00:00:00'),
2: Timestamp('2011-01-01 00:00:00'),
3: Timestamp('2011-01-01 00:00:00'),
4: Timestamp('2011-01-02 00:00:00')},
'price': {0: 10.284260107718254,
1: 11.930300761831457,
2: 10.93741046217319,
3: 10.884574289565609,
4: 11.78005850418319},
'ticker': {0: 'aapl', 1: 'aapl', 2: 'aapl', 3: 'aapl', 4: 'aapl'}}
>>> pd.concat([stocks.head(), stocks.tail()], ignore_index=True).to_dict()
{'date': {0: Timestamp('2011-01-01 00:00:00'),
1: Timestamp('2011-01-01 00:00:00'),
2: Timestamp('2011-01-01 00:00:00'),
3: Timestamp('2011-01-01 00:00:00'),
4: Timestamp('2011-01-02 00:00:00'),
5: Timestamp('2011-01-24 00:00:00'),
6: Timestamp('2011-01-25 00:00:00'),
7: Timestamp('2011-01-25 00:00:00'),
8: Timestamp('2011-01-25 00:00:00'),
9: Timestamp('2011-01-25 00:00:00')},
'price': {0: 10.284260107718254,
1: 11.930300761831457,
2: 10.93741046217319,
3: 10.884574289565609,
4: 11.78005850418319,
5: 10.017209045035006,
6: 10.57090128181566,
7: 11.442792747870204,
8: 11.592953372130493,
9: 12.864146419530938},
'ticker': {0: 'aapl',
1: 'aapl',
2: 'aapl',
3: 'aapl',
4: 'aapl',
5: 'msft',
6: 'msft',
7: 'msft',
8: 'msft',
9: 'msft'}}
Możesz również podać opis ramki DataFrame (używając tylko odpowiednich kolumn). Ułatwia to innym sprawdzanie typów danych w każdej kolumnie i identyfikowanie innych typowych błędów (np. Daty jako ciąg znaków, a datetime64 i obiekt):
stocks.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100 entries, 0 to 99
Data columns (total 3 columns):
date 100 non-null datetime64[ns]
price 100 non-null float64
ticker 100 non-null object
dtypes: datetime64[ns](1), float64(1), object(1)
UWAGA: Jeśli Twoja ramka DataFrame ma MultiIndex:
Jeśli Twoja ramka DataFrame ma multiindeks, musisz najpierw zresetować ją przed wywołaniem to_dict
. Następnie musisz ponownie utworzyć indeks za pomocą set_index
:
# MultiIndex example. First create a MultiIndex DataFrame.
df = stocks.set_index(['date', 'ticker'])
>>> df
price
date ticker
2011-01-01 aapl 10.284260
aapl 11.930301
aapl 10.937410
aapl 10.884574
2011-01-02 aapl 11.780059
...
# After resetting the index and passing the DataFrame to `to_dict`, make sure to use
# `set_index` to restore the original MultiIndex. This DataFrame can then be restored.
d = df.reset_index().to_dict()
df_new = pd.DataFrame(d).set_index(['date', 'ticker'])
>>> df_new.head()
price
date ticker
2011-01-01 aapl 10.284260
aapl 11.930301
aapl 10.937410
aapl 10.884574
2011-01-02 aapl 11.780059
Oto moja wersja dput- standardowego narzędzia R do tworzenia powtarzalnych raportów - dla Pand DataFrame
. Prawdopodobnie zawiedzie w przypadku bardziej złożonych ramek, ale wydaje się, że działa w prostych przypadkach:
import pandas as pd
def dput(x):
if isinstance(x,pd.Series):
return "pd.Series(%s,dtype='%s',index=pd.%s)" % (list(x),x.dtype,x.index)
if isinstance(x,pd.DataFrame):
return "pd.DataFrame({" + ", ".join([
"'%s': %s" % (c,dput(x[c])) for c in x.columns]) + (
"}, index=pd.%s)" % (x.index))
raise NotImplementedError("dput",type(x),x)
teraz,
df = pd.DataFrame({'a':[1,2,3,4,2,1,3,1]})
assert df.equals(eval(dput(df)))
du = pd.get_dummies(df.a,"foo")
assert du.equals(eval(dput(du)))
di = df
di.index = list('abcdefgh')
assert di.equals(eval(dput(di)))
Zauważ, że daje to dużo bardziej szczegółowe dane wyjściowe niż DataFrame.to_dictnp.
pd.DataFrame({ 'foo_1':pd.Series([1, 0, 0, 0, 0, 1, 0, 1],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1)), 'foo_2':pd.Series([0, 1, 0, 0, 1, 0, 0, 0],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1)), 'foo_3':pd.Series([0, 0, 1, 0, 0, 0, 1, 0],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1)), 'foo_4':pd.Series([0, 0, 0, 1, 0, 0, 0, 0],dtype='uint8',index=pd.RangeIndex(start=0, stop=8, step=1))}, index=pd.RangeIndex(start=0, stop=8, step=1))
vs
{'foo_1': {0: 1, 1: 0, 2: 0, 3: 0, 4: 0, 5: 1, 6: 0, 7: 1}, 'foo_2': {0: 0, 1: 1, 2: 0, 3: 0, 4: 1, 5: 0, 6: 0, 7: 0}, 'foo_3': {0: 0, 1: 0, 2: 1, 3: 0, 4: 0, 5: 0, 6: 1, 7: 0}, 'foo_4': {0: 0, 1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 6: 0, 7: 0}}
do du
powyżej, ale zachowuje typy kolumn . Np. W powyższym przypadku testowym
du.equals(pd.DataFrame(du.to_dict()))
==> False
ponieważ du.dtypes
jest uint8
i pd.DataFrame(du.to_dict()).dtypes
jest int64
.