Mam ramkę danych, w której niektóre komórki zawierają listy wielu wartości. Zamiast przechowywać wiele wartości w komórce, chciałbym rozszerzyć ramkę danych, aby każdy element na liście miał swój własny wiersz (z tymi samymi wartościami we wszystkich innych kolumnach). Więc jeśli mam:
import pandas as pd
import numpy as np
df = pd.DataFrame(
{'trial_num': [1, 2, 3, 1, 2, 3],
'subject': [1, 1, 1, 2, 2, 2],
'samples': [list(np.random.randn(3).round(2)) for i in range(6)]
}
)
df
Out[10]:
samples subject trial_num
0 [0.57, -0.83, 1.44] 1 1
1 [-0.01, 1.13, 0.36] 1 2
2 [1.18, -1.46, -0.94] 1 3
3 [-0.08, -4.22, -2.05] 2 1
4 [0.72, 0.79, 0.53] 2 2
5 [0.4, -0.32, -0.13] 2 3
Jak przekonwertować na długą formę, np .:
subject trial_num sample sample_num
0 1 1 0.57 0
1 1 1 -0.83 1
2 1 1 1.44 2
3 1 2 -0.01 0
4 1 2 1.13 1
5 1 2 0.36 2
6 1 3 1.18 0
# etc.
Indeks nie jest ważny, można ustawić istniejące kolumny jako indeks, a ostateczna kolejność nie jest ważna.
df.explode('samples')
do rozwiązania tego problemu.explode
na razie obsługuje tylko rozbicie jednej kolumny.Odpowiedzi:
Wynik:
PS tutaj możesz znaleźć nieco bardziej ogólne rozwiązanie
UPDATE: kilka wyjaśnień: IMO Najłatwiejszym sposobem zrozumienia tego kodu jest wykonanie go krok po kroku:
w kolejnym wierszu powtarzamy wartości w jednej kolumnie
N
razy, gdzieN
- jest długością odpowiedniej listy:można to uogólnić dla wszystkich kolumn zawierających wartości skalarne:
używając
np.concatenate()
możemy spłaszczyć wszystkie wartości wlist
kolumnie (samples
) i otrzymać wektor 1D:składając to wszystko razem:
użycie
pd.DataFrame()[df.columns]
zagwarantuje, że wybieramy kolumny w oryginalnej kolejności ...źródło
lst_col
całkowicie usuwa wiersze, które mają pustą listę ; aby zachować te wiersze i wypełnianie ichlst_col
pomocąnp.nan
, można po prostu zrobićdf[lst_col] = df[lst_col].apply(lambda x: x if len(x) > 0 else [np.nan])
przed użyciem tej metody. Najwyraźniej.mask
nie zwróci list, stąd rozszerzenie.apply
.Nieco dłużej niż się spodziewałem:
Jeśli chcesz indeksowania sekwencyjnego, możesz zastosować
reset_index(drop=True)
do wyniku.aktualizacja :
źródło
df.apply(lambda x: pd.Series(x['samples']),axis=1)
godf.samples.apply(pd.Series)
.df.explode()
jak pokazano tutaj.Pandy> = 0,25
Metody Series i DataFrame definiują
.explode()
metodę, która rozbija listy na oddzielne wiersze. Zobacz sekcję Dokumenty na temat Rozbijanie kolumny podobnej do listy .Zauważ, że obsługuje to również odpowiednio mieszane kolumny list i skalarów, a także puste listy i NaN (jest to wada
repeat
rozwiązań opartych na ).Jednak powinieneś to zauważyć
explode
działa tylko na jednej kolumnie (na razie).PS: jeśli chcesz rozbić kolumnę ciągów , musisz najpierw podzielić na separator, a następnie użyć
explode
. Zobacz tę (bardzo) powiązaną odpowiedź przeze mnie.źródło
możesz również użyć
pd.concat
ipd.melt
do tego:na koniec, jeśli potrzebujesz, możesz posortować według pierwszych trzech kolumn.
źródło
Próbując krok po kroku przeanalizować rozwiązanie Romana Pekara, aby lepiej je zrozumieć, wymyśliłem własne rozwiązanie, które
melt
pozwala uniknąć niektórych nieporozumień związanych z układaniem w stosy i resetowaniem indeksów. Nie mogę jednak powiedzieć, że jest to oczywiście jaśniejsze rozwiązanie:Wyjście (oczywiście możemy teraz usunąć oryginalną kolumnę próbek):
źródło
Dla tych, którzy szukają wersji odpowiedzi Romana Pekara, która unika ręcznego nazewnictwa kolumn:
źródło
Najprościej było:
samples
kolumnę na DataFramePokazano tutaj:
Warto zauważyć, że mogło to zadziałać tylko dlatego, że każda próba ma taką samą liczbę próbek (3). W przypadku prób z różnymi rozmiarami próbek może być konieczne coś mądrzejszego.
źródło
Bardzo późna odpowiedź, ale chcę to dodać:
Szybkie rozwiązanie wykorzystujące waniliowy Python, który obsługuje również
sample_num
kolumnę w przykładzie OP. W moim dużym zbiorze danych z ponad 10 milionami wierszy i wynikiem z 28 milionami wierszy zajmuje to tylko około 38 sekund. Przyjęte rozwiązanie całkowicie psuje się przy takiej ilości danych i prowadzi domemory error
mojego systemu, który ma 128 GB pamięci RAM.źródło
Również bardzo późno, ale tutaj jest odpowiedź od Karvy1, która zadziałała dobrze, jeśli nie masz wersji pandy> = 0.25: https://stackoverflow.com/a/52511166/10740287
Dla przykładu powyżej możesz napisać:
Test prędkości:
1,33 ms ± 74,8 μs na pętlę (średnia ± odchylenie standardowe z 7 przebiegów, po 1000 pętli każda)
4,9 ms ± 189 μs na pętlę (średnia ± odchylenie standardowe 7 przebiegów, po 100 pętli każda)
1,38 ms ± 25 μs na pętlę (średnia ± odchylenie standardowe z 7 przebiegów, po 1000 pętli każda)
źródło
Spróbuj tego w pandach> = 0.25 wersji
źródło
.str.split(',')
ponieważPrices
jest to już lista.