Panda membagi kolom daftar menjadi beberapa kolom

135

Saya memiliki DataFrame panda dengan satu kolom:

import pandas as pd

df = pd.DataFrame(
    data={
        "teams": [
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
        ]
    }
)

print(df)

Keluaran:

       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

Bagaimana cara membagi kolom daftar ini menjadi 2 kolom?

pengguna2938093
sumber

Jawaban:

243

Anda dapat menggunakan DataFramekonstruktor dengan listsdibuat oleh to_list:

import pandas as pd

d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
print (df2)
       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

df2[['team1','team2']] = pd.DataFrame(df2.teams.tolist(), index= df2.index)
print (df2)
       teams team1 team2
0  [SF, NYG]    SF   NYG
1  [SF, NYG]    SF   NYG
2  [SF, NYG]    SF   NYG
3  [SF, NYG]    SF   NYG
4  [SF, NYG]    SF   NYG
5  [SF, NYG]    SF   NYG
6  [SF, NYG]    SF   NYG

Dan untuk yang baru DataFrame:

df3 = pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
print (df3)
  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

Solusi dengan apply(pd.Series)sangat lambat:

#7k rows
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [121]: %timeit df2['teams'].apply(pd.Series)
1.79 s ± 52.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [122]: %timeit pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
1.63 ms ± 54.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
jezrael
sumber
4
Peringatan kecil, jika Anda menggunakannya pada dataframe yang ada, pastikan untuk mengatur ulang indeks, jika tidak maka indeks tidak akan ditetapkan dengan benar.
pengguna1700890
1
@ pengguna1700890 - ya, atau tentukan indeks dalam konstruktor df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
DataFrame
1
@ Catbuilts - ya, jika ada solusi vectorize yang terbaik hindari.
jezrael
1
@ Catbuilts - ya, tentu saja. Berarti vektorisasi umumnya tidak ada loop, jadi tidak berlaku, tidak untuk, tidak ada pemahaman daftar. Tapi itu tergantung kebutuhan apa sebenarnya. Mungkin juga membantu ini
jezrael
2
@Catbuilts Memang apply()mungkin lebih lambat tetapi merupakan metode masuk ketika string input dan nilai tidak sama di seluruh baris Seri asli!
CheTesta
52

Solusi yang jauh lebih sederhana:

pd.DataFrame(df2["teams"].to_list(), columns=['team1', 'team2'])

Hasil,

  team1 team2
-------------
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
7    SF   NYG

Jika Anda ingin memisahkan kolom dari string yang dipisahkan daripada daftar, Anda dapat melakukan hal yang sama:

pd.DataFrame(df["teams"].str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])
Joseph Davison
sumber
6
bagaimana jika setiap daftar memiliki jumlah elemen yang tidak merata?
ikel
Jika Anda ingin memisahkan kolom dari string yang dipisahkan daripada daftar, Anda juga dapat melakukannya: df["teams"].str.split('<delim>', expand=True) sudah mengembalikan DataFrame, jadi mungkin akan lebih mudah dengan hanya mengganti nama kolom.
AMC
26

Solusi ini mempertahankan indeks df2DataFrame, tidak seperti solusi apa pun yang menggunakan tolist():

df3 = df2.teams.apply(pd.Series)
df3.columns = ['team1', 'team2']

Inilah hasilnya:

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Kevin Markham
sumber
2
Juga salah satu yang paling lambat applyyang dapat Anda lakukan di panda. Anda harus menghindari metode ini dan menggunakan jawaban yang diterima. Dalam pengaturan waktu jawaban teratas, metode ini kira-kira 1400 xlebih lambat @rajan
Erfan
2
@Erfan Ya, tetapi terkadang pengguna tidak peduli apakah suatu operasi membutuhkan waktu 1 atau 1 md, dan sebaliknya mereka paling peduli tentang penulisan kode yang paling sederhana dan paling mudah dibaca! Saya mengakui bahwa keterbacaan / kesederhanaan itu subjektif, tetapi maksud saya hanyalah bahwa kecepatan bukanlah prioritas untuk semua pengguna setiap saat.
Kevin Markham
1
Selain itu, saya menemukan bahwa applymetode ini bekerja lebih andal untuk memperluas array besar (1000+ item) pada kumpulan data besar. The tolist()Metode membunuh proses saya ketika kumpulan data melebihi 500k baris.
moritz
2
Ini adalah solusi hebat karena berfungsi baik dengan daftar ukuran berbeda.
dasilvadaniel
@KevinMarkham mereka paling peduli tentang menulis kode yang paling sederhana dan paling mudah dibaca Apakah pd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])benar-benar jauh lebih rumit?
AMC
15

Tampaknya ada cara yang lebih sederhana secara sintaksis, dan oleh karena itu lebih mudah diingat, dibandingkan dengan solusi yang diusulkan. Saya berasumsi bahwa kolom tersebut disebut 'meta' dalam dataframe df:

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())
mikkokotila
sumber
1
Saya mendapat kesalahan tetapi saya mengatasinya dengan menghapus str.split(). Ini jauh lebih sederhana dan memiliki keuntungan jika Anda tidak mengetahui jumlah item dalam daftar Anda.
otteheng
Tampaknya ada cara yang lebih sederhana secara sintaksis, dan oleh karena itu lebih mudah diingat, dibandingkan dengan solusi yang diusulkan. Betulkah? Karena ini praktis identik dengan jawaban teratas yang diposting tahun sebelumnya. Satu-satunya perbedaan adalah bagian yang tidak terkait dengan pertanyaan khusus ini.
AMC
Ini bekerja untuk saya !!
EduardoUstarez
3

Berdasarkan jawaban sebelumnya, berikut adalah solusi lain yang mengembalikan hasil yang sama seperti df2.teams.apply (pd.Series) dengan waktu proses yang jauh lebih cepat:

pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

Waktu:

In [1]:
import pandas as pd
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [2]: %timeit df2['teams'].apply(pd.Series)

8.27 s ± 2.73 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [3]: %timeit pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

35.4 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
ailurid.dll
sumber
3

Solusi di atas tidak berhasil untuk saya karena saya memiliki nanpengamatan di saya dataframe. Dalam kasus saya df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)menghasilkan:

object of type 'float' has no len()

Saya menyelesaikan ini menggunakan pemahaman daftar. Berikut contoh yang dapat direplikasi:

import pandas as pd
import numpy as np
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
            ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2.loc[2,'teams'] = np.nan
df2.loc[4,'teams'] = np.nan
df2

keluaran:

        teams
0   [SF, NYG]
1   [SF, NYG]
2   NaN
3   [SF, NYG]
4   NaN
5   [SF, NYG]
6   [SF, NYG]

df2['team1']=np.nan
df2['team2']=np.nan

menyelesaikan dengan pemahaman daftar:

for i in [0,1]:
    df2['team{}'.format(str(i+1))]=[k[i] if isinstance(k,list) else k for k in df2['teams']]

df2

hasil:

    teams   team1   team2
0   [SF, NYG]   SF  NYG
1   [SF, NYG]   SF  NYG
2   NaN        NaN  NaN
3   [SF, NYG]   SF  NYG
4   NaN        NaN  NaN
5   [SF, NYG]   SF  NYG
6   [SF, NYG]   SF  NYG
Lucas
sumber
1

pemahaman daftar

implementasi sederhana dengan pemahaman daftar (favorit saya)

df = pd.DataFrame([pd.Series(x) for x in df.teams])
df.columns = ['team_{}'.format(x+1) for x in df.columns]

waktu pada keluaran:

CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 2.71 ms

keluaran:

team_1  team_2
0   SF  NYG
1   SF  NYG
2   SF  NYG
3   SF  NYG
4   SF  NYG
5   SF  NYG
6   SF  NYG
Talis
sumber
Jenis daftar pegangan ini dengan panjang yang berbeda - yang merupakan peningkatan dari banyak jawaban lain, tetapi menghasilkan item tidak berada di kolomnya sendiri.
Ishak
0

Berikut solusi lain menggunakan df.transformdan df.set_index:

>>> (df['teams']
       .transform([lambda x:x[0], lambda x:x[1]])
       .set_axis(['team1','team2'],
                  axis=1,
                  inplace=False)
    )

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Sayandip Dutta
sumber