Pandas chia cột danh sách thành nhiều cột


136

Tôi có một DataFrame gấu trúc với một cột:

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)

Đầu ra:

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

Làm thế nào có thể chia cột danh sách này thành 2 cột?

Câu trả lời:


243

Bạn có thể sử dụng DataFrameconstructor với listsđược tạo bởi 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

Và cho mới 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

Giải pháp với apply(pd.Series)rất chậm:

#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)

4
Hãy cẩn thận, nếu bạn đang sử dụng nó trên khung dữ liệu hiện có, hãy đảm bảo đặt lại chỉ mục, nếu không nó sẽ không được gán chính xác.
dùng1700890

1
@ user1700890 - có, hoặc chỉ định chỉ mục trong hàm tạo df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
DataFrame

1
@Catbuilts - có, nếu tồn tại giải pháp vector hóa tốt nhất nên tránh nó.
jezrael

1
@Catbuilts - vâng, rõ ràng. Vectorized có nghĩa là nói chung không có vòng lặp, vì vậy không áp dụng, không cho, không hiểu danh sách. Nhưng nó phụ thuộc chính xác những gì cần. Cũng có thể giúp điều này
jezrael

2
@Catbuilts Thật vậy apply()có thể chậm hơn nhưng là phương thức đi đến khi chuỗi đầu vào và giá trị không bằng nhau trên các hàng của Sê-ri gốc!
CheTesta

52

Giải pháp đơn giản hơn nhiều:

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

Sản lượng,

  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

Nếu bạn muốn tách một cột của các chuỗi được phân tách thay vì danh sách, bạn có thể thực hiện tương tự:

pd.DataFrame(df["teams"].str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])

5
Nếu mỗi danh sách có số phần tử không đồng đều thì sao?
ikel

Nếu bạn muốn tách một cột của các chuỗi được phân tách thay vì các danh sách, bạn có thể làm tương tự: df["teams"].str.split('<delim>', expand=True) đã trả về DataFrame, vì vậy có lẽ sẽ đơn giản hơn khi chỉ đổi tên các cột.
AMC

26

Giải pháp này bảo tồn chỉ mục của df2DataFrame, không giống như bất kỳ giải pháp nào sử dụng tolist():

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

Đây là kết quả:

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

2
Cũng là một trong những điều chậm nhất applybạn có thể làm trong gấu trúc. Bạn nên tránh phương pháp này và sử dụng câu trả lời được chấp nhận. Trong thời gian của câu trả lời hàng đầu, phương pháp này 1400 xchậm hơn khoảng @rajan
Erfan

2
@Erfan Có, nhưng đôi khi người dùng không quan tâm liệu một thao tác mất 1 giây hay 1ms, và thay vào đó họ quan tâm nhất đến việc viết mã đơn giản nhất, dễ đọc nhất! Tôi thừa nhận rằng khả năng đọc / đơn giản là chủ quan, nhưng quan điểm của tôi đơn giản là tốc độ không phải là ưu tiên của tất cả người dùng mọi lúc.
Kevin Markham

1
Hơn nữa, tôi phát hiện ra rằng applyphương thức này hoạt động đáng tin cậy hơn để mở rộng các mảng lớn (hơn 1000 mục) trên các tập dữ liệu lớn. Các tolist()phương pháp giết chết quá trình của tôi khi tập dữ liệu vượt quá 500k hàng.
moritz

2
Đây là một giải pháp tuyệt vời vì nó hoạt động tốt với danh sách các kích cỡ khác nhau.
dasilvadaniel

@KevinMarkham họ quan tâm nhất đến việc viết mã đơn giản nhất, dễ đọc nhấtpd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])thực sự phức tạp hơn nhiều không?
AMC

15

Dường như có một cách đơn giản hơn về mặt cú pháp, và do đó dễ nhớ hơn, trái ngược với các giải pháp được đề xuất. Tôi giả sử rằng cột được gọi là 'meta' trong khung dữ liệu df:

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())

1
Tôi đã có một lỗi nhưng tôi đã giải quyết nó bằng cách loại bỏ str.split(). Điều này đơn giản hơn nhiều và có lợi thế nếu bạn không biết số lượng mặt hàng trong danh sách của mình.
otteheng

Dường như có một cách đơn giản hơn về mặt cú pháp, và do đó dễ nhớ hơn, trái ngược với các giải pháp được đề xuất. Có thật không? Bởi vì điều này thực tế giống hệt với câu trả lời hàng đầu đã được đăng từ nhiều năm trước. Sự khác biệt duy nhất là phần không liên quan đến câu hỏi cụ thể này.
AMC

Nó làm việc với tôi !!
EduardoUstarez

3

Dựa trên các câu trả lời trước, đây là một giải pháp khác trả về kết quả tương tự như df2.teams.apply (pd.Series) với thời gian chạy nhanh hơn nhiều:

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

Thời gian:

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)

3

Các giải pháp trên không hiệu quả với tôi vì tôi có những nanquan sát trong tôi dataframe. Trong trường hợp của tôi df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)mang lại:

object of type 'float' has no len()

Tôi giải quyết điều này bằng cách sử dụng danh sách hiểu. Dưới đây là ví dụ có thể nhân rộng:

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

đầu ra:

        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

giải quyết với việc hiểu danh sách:

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

sản lượng:

    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

1

danh sách hiểu

thực hiện đơn giản với việc hiểu danh sách (yêu thích của tôi)

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

thời gian trên đầu ra:

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

đầu ra:

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

Kiểu xử lý này liệt kê các độ dài khác nhau - đó là một cải tiến so với nhiều câu trả lời khác, nhưng kết quả là các mục không nằm trong các cột riêng của chúng.
Isaac
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.