Làm thế nào để tách một cột thành hai cột?


196

Tôi có một khung dữ liệu với một cột và tôi muốn chia nó thành hai cột, với một tiêu đề cột là ' fips'và cột kia'row'

Khung dữ liệu của tôi dftrông như thế này:

          row
0    00000 UNITED STATES
1    01000 ALABAMA
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL

Tôi không biết cách sử dụng df.row.str[:]để đạt được mục tiêu chia ô hàng. Tôi có thể sử dụng df['fips'] = hellođể thêm một cột mới và điền vào đó hello. Có ý kiến ​​gì không?

         fips       row
0    00000 UNITED STATES
1    01000 ALABAMA 
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL

3
Làm thế nào bạn tải dữ liệu của bạn vào gấu trúc? Bạn có thể sắp xếp dữ liệu theo định dạng mong muốn bằng cách sử dụng read_table()hoặc read_fwf()
zach

Câu trả lời:


136

Có thể có một cách tốt hơn, nhưng đây là một cách tiếp cận:

                            row
    0       00000 UNITED STATES
    1             01000 ALABAMA
    2  01001 Autauga County, AL
    3  01003 Baldwin County, AL
    4  01005 Barbour County, AL
df = pd.DataFrame(df.row.str.split(' ',1).tolist(),
                                 columns = ['flips','row'])
   flips                 row
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

6
Xin lưu ý rằng .tolist () sẽ xóa bất kỳ chỉ mục nào bạn có, vì vậy Dataframe mới của bạn sẽ được giới thiệu lại từ 0 (Không thành vấn đề trong trường hợp cụ thể của bạn).
Crashthatch

10
@Crashthatch - sau đó một lần nữa bạn có thể chỉ cần thêm index = df.indexvà bạn tốt.
root

Điều gì nếu một tế bào không thể được phân chia?
Nisba

@Nisba: Nếu bất kỳ ô nào không thể tách (ví dụ: chuỗi không chứa bất kỳ khoảng trống nào trong trường hợp này) thì nó vẫn hoạt động nhưng một phần của phần tách sẽ trống. Các tình huống khác sẽ xảy ra trong trường hợp bạn có các loại hỗn hợp trong cột có ít nhất một ô chứa bất kỳ loại số nào. Sau đó, splitphương thức trả về NaN và tolistphương thức sẽ trả về giá trị này như (NaN) sẽ dẫn đến ValueError(để khắc phục vấn đề này, bạn có thể chuyển nó thành loại chuỗi trước khi tách). Tôi khuyên bạn nên tự mình thử nó, đó là cách học tốt nhất :-)
Nerxis

@techkuz: Bạn có chắc là bạn dfrowtiêu đề cột không? Bạn có thể nghĩ đó là một loại thuộc tính DataFrame nhưng rõ ràng đây là tên của cột. Tùy thuộc vào cách bạn tạo và xác định các tiêu đề cột của mình, vì vậy nếu bạn sử dụng một tiêu đề khác nhau, hãy sử dụng nó (ví dụ df.my_column_name.split(...)).
Nerxis

388

Phiên bản TL; DR:

Đối với trường hợp đơn giản là:

  • Tôi có một cột văn bản với một dấu phân cách và tôi muốn hai cột

Giải pháp đơn giản nhất là:

df['A'], df['B'] = df['AB'].str.split(' ', 1).str

Hoặc bạn có thể tạo một DataFrame với một cột cho mỗi mục phân chia tự động với:

df['AB'].str.split(' ', 1, expand=True)

Bạn phải sử dụng expand=Truenếu chuỗi của bạn có số lần phân tách không đồng nhất và bạn muốn Nonethay thế các giá trị bị thiếu.

Lưu ý làm thế nào, trong cả hai trường hợp, .tolist()phương pháp là không cần thiết. Cũng không zip().

Chi tiết:

Giải pháp của Andy Hayden là tuyệt vời nhất trong việc chứng minh sức mạnh của str.extract()phương pháp.

Nhưng đối với một phân tách đơn giản trên một dấu phân cách đã biết (như, phân tách bằng dấu gạch ngang hoặc phân tách bằng khoảng trắng), .str.split()phương thức này là đủ 1 . Nó hoạt động trên một cột (Sê-ri) chuỗi và trả về một cột (Sê-ri) danh sách:

>>> import pandas as pd
>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2']})
>>> df

      AB
0  A1-B1
1  A2-B2
>>> df['AB_split'] = df['AB'].str.split('-')
>>> df

      AB  AB_split
0  A1-B1  [A1, B1]
1  A2-B2  [A2, B2]

1: Nếu bạn không chắc chắn hai tham số đầu tiên .str.split()phải làm gì, tôi khuyên bạn nên sử dụng tài liệu cho phiên bản Python đơn giản của phương thức .

Nhưng làm thế nào để bạn đi từ:

  • một cột chứa danh sách hai yếu tố

đến:

  • Hai cột, mỗi cột chứa phần tử tương ứng của danh sách?

Chà, chúng ta cần xem xét kỹ hơn .strthuộc tính của một cột.

Đó là một đối tượng ma thuật được sử dụng để thu thập các phương thức coi mỗi phần tử trong một cột thành một chuỗi, sau đó áp dụng phương thức tương ứng trong mỗi phần tử một cách hiệu quả nhất có thể:

>>> upper_lower_df = pd.DataFrame({"U": ["A", "B", "C"]})
>>> upper_lower_df

   U
0  A
1  B
2  C
>>> upper_lower_df["L"] = upper_lower_df["U"].str.lower()
>>> upper_lower_df

   U  L
0  A  a
1  B  b
2  C  c

Nhưng nó cũng có giao diện "lập chỉ mục" để lấy từng phần tử của chuỗi theo chỉ mục của nó:

>>> df['AB'].str[0]

0    A
1    A
Name: AB, dtype: object

>>> df['AB'].str[1]

0    1
1    2
Name: AB, dtype: object

Tất nhiên, giao diện lập chỉ mục .strnày không thực sự quan tâm nếu mỗi phần tử mà nó lập chỉ mục thực sự là một chuỗi, miễn là nó có thể được lập chỉ mục, vì vậy:

>>> df['AB'].str.split('-', 1).str[0]

0    A1
1    A2
Name: AB, dtype: object

>>> df['AB'].str.split('-', 1).str[1]

0    B1
1    B2
Name: AB, dtype: object

Sau đó, đó là một vấn đề đơn giản để tận dụng lợi thế của bộ giải nén Python để giải nén iterables để làm

>>> df['A'], df['B'] = df['AB'].str.split('-', 1).str
>>> df

      AB  AB_split   A   B
0  A1-B1  [A1, B1]  A1  B1
1  A2-B2  [A2, B2]  A2  B2

Tất nhiên, việc lấy DataFrame ra khỏi việc tách một cột chuỗi rất hữu ích để .str.split()phương thức có thể làm điều đó cho bạn với expand=Truetham số:

>>> df['AB'].str.split('-', 1, expand=True)

    0   1
0  A1  B1
1  A2  B2

Vì vậy, một cách khác để thực hiện những gì chúng ta muốn là làm:

>>> df = df[['AB']]
>>> df

      AB
0  A1-B1
1  A2-B2

>>> df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))

      AB   A   B
0  A1-B1  A1  B1
1  A2-B2  A2  B2

Các expand=Truephiên bản, mặc dù lâu hơn, có lợi thế rõ rệt so với tuple giải nén phương pháp. Tuple giải nén không giải quyết tốt với các phần tách có độ dài khác nhau:

>>> df = pd.DataFrame({'AB': ['A1-B1', 'A2-B2', 'A3-B3-C3']})
>>> df
         AB
0     A1-B1
1     A2-B2
2  A3-B3-C3
>>> df['A'], df['B'], df['C'] = df['AB'].str.split('-')
Traceback (most recent call last):
  [...]    
ValueError: Length of values does not match length of index
>>> 

Nhưng expand=Truexử lý nó một cách độc đáo bằng cách đặt Nonevào các cột không có đủ "phần tách":

>>> df.join(
...     df['AB'].str.split('-', expand=True).rename(
...         columns={0:'A', 1:'B', 2:'C'}
...     )
... )
         AB   A   B     C
0     A1-B1  A1  B1  None
1     A2-B2  A2  B2  None
2  A3-B3-C3  A3  B3    C3

df ['A'], df ['B'] = df ['AB']. str.split ('', 1) .str Ý nghĩa của '1' trong tách ('', 1) là gì?
Hariprasad

@Hariprasad, đó là số lần chia tối đa. Tôi đã thêm một liên kết đến các tài liệu cho Phiên bản Python của .split()phương thức giải thích hai tham số đầu tiên tốt hơn các tài liệu Pandas.
LeoRochael

5
pandas 1.0.0 báo cáo "FutureWarning: Lặp đi lặp lại các ký tự sẽ không được dùng nữa trong các bản phát hành trong tương lai."
Frank

1
Điều này hoạt động theo Python 1.0.1. df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
Martien Lubberink

59

Bạn có thể trích xuất các phần khác nhau khá gọn gàng bằng cách sử dụng mẫu regex:

In [11]: df.row.str.extract('(?P<fips>\d{5})((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))')
Out[11]: 
    fips                    1           state           county state_code
0  00000        UNITED STATES   UNITED STATES              NaN        NaN
1  01000              ALABAMA         ALABAMA              NaN        NaN
2  01001   Autauga County, AL             NaN   Autauga County         AL
3  01003   Baldwin County, AL             NaN   Baldwin County         AL
4  01005   Barbour County, AL             NaN   Barbour County         AL

[5 rows x 5 columns]

Để giải thích regex hơi dài:

(?P<fips>\d{5})
  • Ghép năm chữ số ( \d) và đặt tên cho chúng "fips".

Phần tiếp theo:

((?P<state>[A-Z ]*$)|(?P<county>.*?), (?P<state_code>[A-Z]{2}$))

Có ( |) một trong hai điều sau:

(?P<state>[A-Z ]*$)
  • Khớp với bất kỳ số ( *) nào của chữ in hoa hoặc dấu cách ( [A-Z ]) và đặt tên này "state"trước khi kết thúc chuỗi ( $),

hoặc là

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • phù hợp với bất cứ điều gì khác ( .*) sau đó
  • dấu phẩy và dấu cách
  • khớp với hai chữ số state_codetrước khi kết thúc chuỗi ( $).

Trong ví dụ:
Lưu ý rằng hai hàng đầu tiên đạt "trạng thái" (để lại NaN trong các cột của quận và state_code), trong khi ba hàng cuối cùng chạm vào hạt, state_code (để NaN ở cột trạng thái).


Đây chắc chắn là giải pháp tốt nhất nhưng nó có thể hơi quá sức với một số người với regex rất rộng. Tại sao không làm điều đó như một phần 2 và có phần 1 chỉ với các cột fips và hàng?
Bàn Bobby nhỏ

2
@josh đó là một điểm tốt, trong khi các phần riêng lẻ của regex "dễ hiểu", regex dài có thể trở nên phức tạp một cách nhanh chóng. Tôi đã thêm một số lời giải thích cho độc giả trong tương lai! (Tôi cũng phải cập nhật liên kết đến các tài liệu giải thích (?P<label>...)cú pháp! Tôi không biết tại sao tôi lại sử dụng regex phức tạp hơn, rõ ràng là đơn giản có thể hoạt động hmmmm
Andy Hayden

1
Trông thân thiện hơn nhiều. Tôi rất vui vì bạn đã làm vì tôi đã xem các tài liệu để hiểu <group_name>. Bây giờ tôi biết nó làm cho mã của tôi rất ngắn gọn.
Bàn Bobby nhỏ


22

Nếu bạn không muốn tạo một khung dữ liệu mới hoặc nếu khung dữ liệu của bạn có nhiều cột hơn chỉ những cột bạn muốn tách, bạn có thể:

df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]  

1
Tôi gặp zip argument #1 must support iterationlỗi, python 2.7
Allan Ruin

20

Bạn có thể sử dụng str.splittheo khoảng trắng (dấu phân cách mặc định) và tham số expand=Trueđể DataFramegán cho các cột mới:

df = pd.DataFrame({'row': ['00000 UNITED STATES', '01000 ALABAMA', 
                           '01001 Autauga County, AL', '01003 Baldwin County, AL', 
                           '01005 Barbour County, AL']})
print (df)
                        row
0       00000 UNITED STATES
1             01000 ALABAMA
2  01001 Autauga County, AL
3  01003 Baldwin County, AL
4  01005 Barbour County, AL



df[['a','b']] = df['row'].str.split(n=1, expand=True)
print (df)
                        row      a                   b
0       00000 UNITED STATES  00000       UNITED STATES
1             01000 ALABAMA  01000             ALABAMA
2  01001 Autauga County, AL  01001  Autauga County, AL
3  01003 Baldwin County, AL  01003  Baldwin County, AL
4  01005 Barbour County, AL  01005  Barbour County, AL

Sửa đổi nếu cần loại bỏ cột ban đầu với DataFrame.pop

df[['a','b']] = df.pop('row').str.split(n=1, expand=True)
print (df)
       a                   b
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Giống như thế nào:

df[['a','b']] = df['row'].str.split(n=1, expand=True)
df = df.drop('row', axis=1)
print (df)

       a                   b
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Nếu gặp lỗi:

#remove n=1 for split by all whitespaces
df[['a','b']] = df['row'].str.split(expand=True)

ValueError: Các cột phải có cùng độ dài với khóa

Bạn có thể kiểm tra và nó trả về 4 cột DataFrame, không chỉ 2:

print (df['row'].str.split(expand=True))
       0        1        2     3
0  00000   UNITED   STATES  None
1  01000  ALABAMA     None  None
2  01001  Autauga  County,    AL
3  01003  Baldwin  County,    AL
4  01005  Barbour  County,    AL

Sau đó, giải pháp được nối mới DataFramebằng cách join:

df = pd.DataFrame({'row': ['00000 UNITED STATES', '01000 ALABAMA', 
                           '01001 Autauga County, AL', '01003 Baldwin County, AL', 
                           '01005 Barbour County, AL'],
                    'a':range(5)})
print (df)
   a                       row
0  0       00000 UNITED STATES
1  1             01000 ALABAMA
2  2  01001 Autauga County, AL
3  3  01003 Baldwin County, AL
4  4  01005 Barbour County, AL

df = df.join(df['row'].str.split(expand=True))
print (df)

   a                       row      0        1        2     3
0  0       00000 UNITED STATES  00000   UNITED   STATES  None
1  1             01000 ALABAMA  01000  ALABAMA     None  None
2  2  01001 Autauga County, AL  01001  Autauga  County,    AL
3  3  01003 Baldwin County, AL  01003  Baldwin  County,    AL
4  4  01005 Barbour County, AL  01005  Barbour  County,    AL

Với loại bỏ cột ban đầu (nếu còn có các cột khác):

df = df.join(df.pop('row').str.split(expand=True))
print (df)
   a      0        1        2     3
0  0  00000   UNITED   STATES  None
1  1  01000  ALABAMA     None  None
2  2  01001  Autauga  County,    AL
3  3  01003  Baldwin  County,    AL
4  4  01005  Barbour  County,    AL   

8

Nếu bạn muốn chia một chuỗi thành nhiều hơn hai cột dựa trên một dấu phân cách, bạn có thể bỏ qua tham số 'tách tối đa'.
Bạn có thể dùng:

df['column_name'].str.split('/', expand=True)

Điều này sẽ tự động tạo nhiều cột như số lượng trường tối đa có trong bất kỳ chuỗi ban đầu nào của bạn.


6

Ngạc nhiên là tôi chưa thấy cái này bao giờ. Nếu bạn chỉ cần hai lần chia tách, tôi rất khuyến khích. . .

Series.str.partition

partition thực hiện một lần phân tách trên dải phân cách và thường khá hiệu quả.

df['row'].str.partition(' ')[[0, 2]]

       0                   2
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Nếu bạn cần đổi tên các hàng,

df['row'].str.partition(' ')[[0, 2]].rename({0: 'fips', 2: 'row'}, axis=1)

    fips                 row
0  00000       UNITED STATES
1  01000             ALABAMA
2  01001  Autauga County, AL
3  01003  Baldwin County, AL
4  01005  Barbour County, AL

Nếu bạn cần tham gia lại bản gốc này, hãy sử dụng joinhoặc concat:

df.join(df['row'].str.partition(' ')[[0, 2]])

pd.concat([df, df['row'].str.partition(' ')[[0, 2]]], axis=1)

                        row      0                   2
0       00000 UNITED STATES  00000       UNITED STATES
1             01000 ALABAMA  01000             ALABAMA
2  01001 Autauga County, AL  01001  Autauga County, AL
3  01003 Baldwin County, AL  01003  Baldwin County, AL
4  01005 Barbour County, AL  01005  Barbour County, AL

0

Tôi thích xuất chuỗi gấu trúc tương ứng (tức là các cột tôi cần), sử dụng hàm áp dụng để phân chia nội dung cột thành nhiều chuỗi và sau đó nối các cột được tạo vào DataFrame hiện có. Tất nhiên, cột nguồn nên được loại bỏ.

ví dụ

 col1 = df["<col_name>"].apply(<function>)
 col2 = ...
 df = df.join(col1.to_frame(name="<name1>"))
 df = df.join(col2.toframe(name="<name2>"))
 df = df.drop(["<col_name>"], axis=1)

Để phân chia hai từ chuỗi chức năng nên là một cái gì đó như thế:

lambda x: x.split(" ")[0] # for the first element
lambda x: x.split(" ")[-1] # for the last element

0

Tôi thấy rằng không ai đã sử dụng phương pháp lát, vì vậy ở đây tôi đặt 2 xu của tôi ở đây.

df["<col_name>"].str.slice(stop=5)
df["<col_name>"].str.slice(start=6)

Phương pháp này sẽ tạo ra hai cột mới.


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.