Jak połączyć serię i ramkę DataFrame

83

Jeśli przyszedłeś tutaj, szukając informacji na temat łączenia znaków DataFramei Seriesw indeksie , spójrz na tę odpowiedź .

Pierwotnym zamiarem PO było zapytanie, jak przypisać elementy serii jako kolumny do innej ramki DataFrame . Jeśli chcesz poznać odpowiedź na to pytanie, spójrz na odpowiedź zaakceptowaną przez EdChum.


Najlepsze, co mogę wymyślić, to

df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})  # see EDIT below
s = pd.Series({'s1':5, 's2':6})

for name in s.index:
    df[name] = s[name]

   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

Czy ktoś może zaproponować lepszą składnię / szybszą metodę?

Moje próby:

df.merge(s)
AttributeError: 'Series' object has no attribute 'columns'

i

df.join(s)
ValueError: Other Series must have a name

EDYTUJ Pierwsze dwie opublikowane odpowiedzi zwróciły uwagę na problem z moim pytaniem, więc do skonstruowania użyj poniższego df:

df = pd.DataFrame({'a':[np.nan, 2, 3], 'b':[4, 5, 6]}, index=[3, 5, 6])

z końcowym wynikiem

    a  b  s1  s2
3 NaN  4   5   6
5   2  5   5   6
6   3  6   5   6
Nathan Lloyd
źródło

Odpowiedzi:

26

Możesz skonstruować ramkę danych z serii, a następnie scalić ją z ramką danych. Więc określasz dane jako wartości, ale pomnóż je przez długość, ustaw kolumny na indeks i ustaw parametry dla left_index i right_index na True:

In [27]:

df.merge(pd.DataFrame(data = [s.values] * len(s), columns = s.index), left_index=True, right_index=True)
Out[27]:
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

EDYCJA dla sytuacji, w której chcesz, aby indeks utworzonego df z serii używał indeksu df, możesz wykonać następujące czynności:

df.merge(pd.DataFrame(data = [s.values] * len(df), columns = s.index, index=df.index), left_index=True, right_index=True)

Zakłada się, że indeksy odpowiadają długości.

EdChum
źródło
168

Aktualizacja
Od wersji 0.24.0 i nowszych można łączyć w DataFrame i Series, o ile seria nosi nazwę.

df.merge(s.rename('new'), left_index=True, right_index=True)
# If series is already named,
# df.merge(s, left_index=True, right_index=True)

W dzisiejszych czasach możesz po prostu przekonwertować Series na DataFrame za pomocą to_frame () . A więc (jeśli dołączasz do indeksu):

df.merge(s.to_frame(), left_index=True, right_index=True)
Nicholas Morley
źródło
6
Korzystając z definicji dfi w pytaniu s, ta odpowiedź zwraca mi pustą ramkę danych, a nie wynik żądany w pytaniu. Nie chcemy dopasowywać się do indeksu; chcemy rozgłaszać swartości do wszystkich wierszy df.
CPBL
2
To rozwiązuje inny problem: „biorąc pod uwagę ramkę DataFrame i serię, w jaki sposób można je połączyć w indeksie”. Pytanie OP brzmiało: „przypisz każdy element serii jako nową kolumnę w DataFrame”.
cs95
5

Oto jeden sposób:

df.join(pd.DataFrame(s).T).fillna(method='ffill')

Aby wyjaśnić, co się tutaj dzieje ...

pd.DataFrame(s).Ttworzy jednowierszową ramkę DataFrame, z sktórej wygląda następująco:

   s1  s2
0   5   6

Następnie joinłączy tę nową ramkę z df:

   a  b  s1  s2
0  1  3   5   6
1  2  4 NaN NaN

Na koniec NaNwartości pod indeksem 1 są wypełniane poprzednimi wartościami w kolumnie przy użyciu argumentu fillnaforward-fill ( ffill):

   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

Aby uniknąć używania fillna, można użyć pd.concatdo powtórzenia wierszy DataFrame skonstruowanych z s. W tym przypadku ogólnym rozwiązaniem jest:

df.join(pd.concat([pd.DataFrame(s).T] * len(df), ignore_index=True))

Oto kolejne rozwiązanie problemu z indeksowaniem przedstawionego w edytowanym pytaniu:

df.join(pd.DataFrame(s.repeat(len(df)).values.reshape((len(df), -1), order='F'), 
        columns=s.index, 
        index=df.index))

sjest przekształcany w DataFrame przez powtórzenie wartości i zmianę kształtu (określenie kolejności „Fortran”), a także przekazanie odpowiednich nazw kolumn i indeksu. Ta nowa ramka DataFrame jest następnie dołączana do df.

Alex Riley
źródło
Niezłe, jedno-liniowe, zastrzeżenie jest takie, że każdy NaN już w df zostanie również wypełniony.
Nathan Lloyd
@Nonth Dzięki i dobra uwaga. Edytowałem, aby uwzględnić alternatywę, która pozwala uniknąć wpisywania NaNwartości.
Alex Riley
To, co stało się z oryginalną odpowiedzią EdChums, ma wpływ na tę poprawioną odpowiedź. Jeśli skonstruuję df, powiedzmy, index=[3, 5]nowe kolumny będą zawierać nan po twoim poleceniu.
Nathan Lloyd
@Nonth Edited again! Powinien teraz spełniać Twoje nowe wymagania.
Alex Riley
Twoja odpowiedź jest 20x szybsza, ale nadal jest to różnica ~ 100 ms z df przy 1e5 rzędach. Moja pętla for jest przerażająco powolna. A tak przy okazji, w Twojej odpowiedzi 2powinno len(df)mieć zastosowanie ogólne.
Nathan Lloyd
0

Gdybym mógł zasugerować skonfigurowanie ramek danych w ten sposób (automatyczne indeksowanie):

df = pd.DataFrame({'a':[np.nan, 1, 2], 'b':[4, 5, 6]})

następnie możesz ustawić wartości s1 i s2 w ten sposób (używając shape (), aby zwrócić liczbę wierszy z df):

s = pd.DataFrame({'s1':[5]*df.shape[0], 's2':[6]*df.shape[0]})

wtedy wynik, który chcesz, jest łatwy:

display (df.merge(s, left_index=True, right_index=True))

Alternatywnie, po prostu dodaj nowe wartości do swojej ramki danych df:

df = pd.DataFrame({'a':[nan, 1, 2], 'b':[4, 5, 6]})
df['s1']=5
df['s2']=6
display(df)

Obie wracają:

     a  b  s1  s2
0  NaN  4   5   6
1  1.0  5   5   6
2  2.0  6   5   6

Jeśli masz inną listę danych (zamiast pojedynczej wartości do zastosowania) i wiesz, że jest ona w tej samej kolejności co df, np:

s1=['a','b','c']

możesz to załączyć w ten sam sposób:

df['s1']=s1

zwroty:

     a  b s1
0  NaN  4  a
1  1.0  5  b
2  2.0  6  c
James
źródło
0

Możesz łatwo ustawić kolumnę pandas.DataFrame na stałą. Ta stała może być wartością typu int, taką jak w Twoim przykładzie. Jeśli określonej kolumny nie ma w pliku df, pandy utworzą nową kolumnę o podanej nazwie. Po skonstruowaniu ramki danych (z pytania):

df = pd.DataFrame({'a':[np.nan, 2, 3], 'b':[4, 5, 6]}, index=[3, 5, 6])

Możesz po prostu biegać:

df['s1'], df['s2'] = 5, 6

Możesz napisać pętlę lub wyrażenie, aby zrobić to dla wszystkich elementów na liście krotek lub kluczy i wartości w słowniku, w zależności od tego, jak przechowujesz swoje prawdziwe dane.

Alex
źródło
0

Jeśli dfjest a, pandas.DataFrameto df['new_col']= Series list_object of length len(df)doda lub Series list_object jako kolumnę o nazwie 'new_col'. df['new_col']= scalar(na przykład 5 lub 6 w twoim przypadku) również działa i jest równoważnedf['new_col']= [scalar]*len(df)

Tak więc dwuwierszowy kod służy temu celowi:

df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})
s = pd.Series({'s1':5, 's2':6})
for x in s.index:    
    df[x] = s[x]

Output: 
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6
aishik roy chaudhury
źródło