จะแบ่งคอลัมน์ออกเป็นสองคอลัมน์ได้อย่างไร?


197

ฉันมีกรอบข้อมูลที่มีหนึ่งคอลัมน์และฉันต้องการแยกออกเป็นสองคอลัมน์โดยมีส่วนหัวคอลัมน์หนึ่งเป็น ' fips'และอีกคอลัมน์หนึ่ง'row'

dataframe ของฉัน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.row.str[:]เพื่อให้บรรลุเป้าหมายในการแบ่งเซลล์แถว ฉันสามารถใช้เพื่อเพิ่มคอลัมน์ใหม่และเติมมันมีdf['fips'] = hello helloความคิดใด ๆ

         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
คุณโหลดข้อมูลของคุณเป็นหมีแพนด้าได้อย่างไร? คุณอาจจะสามารถ laod ข้อมูลในรูปแบบที่คุณต้องการโดยใช้read_table()หรือ read_fwf()
zach

คำตอบ:


140

อาจมีวิธีที่ดีกว่า แต่นี่เป็นวิธีหนึ่ง:

                            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
โปรดทราบว่า. .tolist () จะลบดัชนีใด ๆ ที่คุณมีดังนั้น Dataframe ใหม่ของคุณจะถูกทำดัชนีใหม่จาก 0 (ไม่สำคัญในกรณีเฉพาะของคุณ)
Crashthatch

10
@ Crashthatch - จากนั้นคุณสามารถเพิ่มอีกครั้งindex = df.indexและคุณก็ทำได้ดี
รูต

เกิดอะไรขึ้นถ้าเซลล์หนึ่งไม่สามารถแยกได้
Nisba

@Nisba: หากเซลล์ใด ๆ ไม่สามารถแยกได้ (เช่นสตริงไม่มีที่ว่างสำหรับกรณีนี้) มันจะยังใช้งานได้ แต่ส่วนหนึ่งของการแยกจะว่างเปล่า สถานการณ์อื่น ๆ จะเกิดขึ้นในกรณีที่คุณมีประเภทผสมในคอลัมน์ที่มีเซลล์อย่างน้อยหนึ่งเซลล์ที่มีประเภทตัวเลขใด ๆ จากนั้นsplitเมธอดจะส่งคืน NaN และtolistเมธอดจะส่งคืนค่านี้ตามที่เป็นอยู่ (NaN) ซึ่งจะส่งผลให้ValueError(เพื่อเอาชนะปัญหานี้คุณสามารถแปลงเป็นประเภทสตริงก่อนที่จะแยก) ผมขอแนะนำให้คุณลองด้วยตัวคุณเองก็เป็นวิธีที่ดีที่สุดของการเรียนรู้ :-)
Nerxis

@techkuz: คุณแน่ใจว่าdfมีrowส่วนหัวคอลัมน์ของคุณหรือไม่ คุณอาจคิดว่าเป็นคุณลักษณะ DataFrame บางชนิด แต่ค่อนข้างชัดเจนว่านี่คือชื่อของคอลัมน์ มันขึ้นอยู่กับคุณว่าคุณสร้างและกำหนดส่วนหัวคอลัมน์ของคุณอย่างไรถ้าคุณใช้อันที่แตกต่างกันให้ใช้มัน (เช่นdf.my_column_name.split(...))
Nerxis

392

TL; DR เวอร์ชัน:

สำหรับกรณีง่าย ๆ ของ:

  • ฉันมีคอลัมน์ข้อความพร้อมตัวคั่นและฉันต้องการสองคอลัมน์

ทางออกที่ง่ายที่สุดคือ:

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

หรือคุณสามารถสร้างสร้าง DataFrame ด้วยหนึ่งคอลัมน์สำหรับแต่ละรายการของการแบ่งโดยอัตโนมัติด้วย:

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

คุณต้องใช้expand=Trueถ้าสตริงของคุณมีจำนวนของการแบ่งที่ไม่สม่ำเสมอและคุณต้องการNoneแทนที่ค่าที่หายไป

สังเกตว่าไม่ว่าในกรณีใด.tolist()วิธีการนี้ไม่จำเป็น zip()ไม่เป็น

ในรายละเอียด:

คำตอบของ Andy Haydenนั้นยอดเยี่ยมที่สุดในการแสดงพลังของstr.extract()วิธีการ

แต่สำหรับการแยกง่ายกว่าคั่นที่รู้จักกัน (เช่นแยกจากรอยขีดข่วนหรือแยกโดยช่องว่าง) ที่.str.split()เป็นวิธีการที่เพียงพอที่1 มันทำงานบนคอลัมน์ (Series) ของสตริงและส่งกลับคอลัมน์ (Series) ของรายการ:

>>> 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: หากคุณไม่แน่ใจว่าสิ่งที่กำลังสองพารามิเตอร์แรกของการ.str.split()ทำผมขอแนะนำเอกสารสำหรับรุ่นหลามธรรมดาของวิธีการ

แต่คุณจะไปจาก:

  • คอลัมน์ที่มีรายการองค์ประกอบสององค์ประกอบ

ถึง:

  • สองคอลัมน์แต่ละรายการมีองค์ประกอบที่เกี่ยวข้องของรายการหรือไม่

เราต้องพิจารณา.strคุณสมบัติของคอลัมน์ให้ละเอียดยิ่งขึ้น

มันเป็นวัตถุมหัศจรรย์ที่ใช้เพื่อรวบรวมวิธีการที่ปฏิบัติต่อแต่ละองค์ประกอบในคอลัมน์เป็นสตริงจากนั้นใช้วิธีการที่เกี่ยวข้องในแต่ละองค์ประกอบให้มีประสิทธิภาพมากที่สุด:

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

แต่มันยังมีอินเทอร์เฟซ "การจัดทำดัชนี" สำหรับการรับแต่ละองค์ประกอบของสตริงตามดัชนี:

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

0    A
1    A
Name: AB, dtype: object

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

0    1
1    2
Name: AB, dtype: object

แน่นอนอินเทอร์เฟซการจัดทำดัชนีของ.strนี้ไม่สนใจจริง ๆ ว่าองค์ประกอบแต่ละอย่างที่ทำดัชนีนั้นเป็นสตริงหรือไม่ตราบใดที่สามารถจัดทำดัชนีได้ดังนั้น:

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

จากนั้นเป็นเรื่องง่ายที่จะใช้ประโยชน์จาก Python tuple ที่แกะกล่องออกมาเพื่อทำ

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

แน่นอนว่าการรับ DataFrame ออกจากการแยกคอลัมน์ของสายอักขระนั้นมีประโยชน์มากซึ่ง.str.split()วิธีการดังกล่าวสามารถทำได้เพื่อคุณโดยใช้expand=Trueพารามิเตอร์:

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

    0   1
0  A1  B1
1  A2  B2

ดังนั้นอีกวิธีในการบรรลุสิ่งที่เราต้องการคือการทำ:

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

expand=Trueรุ่นแม้ว่าอีกต่อไปมีข้อได้เปรียบที่แตกต่างกว่าวิธี tuple เอาออก Tuple unpacking ไม่สามารถจัดการกับความยาวที่แตกต่างกันได้ดี:

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

แต่expand=Trueจัดการอย่างดีโดยการวางNoneในคอลัมน์ที่ "แยก" ไม่เพียงพอ:

>>> 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 ความหมายของ '1' แยกกันคืออะไร ('', 1)?
Hariprasad

@Hariprasad เป็นจำนวนสูงสุดของการแบ่ง ฉันได้เพิ่มลิงก์ไปยังเอกสารสำหรับ Python Version ของ.split()วิธีการซึ่งอธิบายพารามิเตอร์สองตัวแรกดีกว่า Pandas docs
LeoRochael

5
pandas 1.0.0 รายงาน "FutureWarning: การวนซ้ำของคอลัมน์มากกว่าอักขระจะถูกคัดค้านในการเผยแพร่ในอนาคต"
Frank

1
ใช้งานได้กับ Python 1.0.1 df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
Martien Lubberink

59

คุณสามารถแยกส่วนต่าง ๆ ออกมาค่อนข้างเรียบร้อยโดยใช้รูปแบบ 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]

เพื่ออธิบาย regex ค่อนข้างยาว:

(?P<fips>\d{5})
  • ตรงกับตัวเลขห้าหลัก ( \d) "fips"และชื่อพวกเขา

ส่วนต่อไป:

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

ทำอย่างใดอย่างหนึ่ง ( |) สองอย่าง:

(?P<state>[A-Z ]*$)
  • ตรงกับจำนวนใด ๆ ( *) ของตัวอักษรใหญ่หรือช่องว่าง ( [A-Z ]) และชื่อนี้"state"ก่อนที่จะสิ้นสุดของสตริง ( $)

หรือ

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • ตรงกับสิ่งอื่น ( .*) จากนั้น
  • เครื่องหมายจุลภาคและช่องว่างแล้ว
  • ตรงกับตัวเลขสองหลักstate_codeก่อนที่จะสิ้นสุดของสตริง ( $)

ในตัวอย่าง:
โปรดทราบว่าสองแถวแรกตี "รัฐ" (ออกจาก NaN ในคอลัมน์เคาน์ตีและ state_code) ในขณะที่สามคนสุดท้ายตีเคาน์ตี state_code (ออกจาก NaN ในคอลัมน์สถานะ)


นี่เป็นทางออกที่ดีที่สุด แต่อาจเป็นเรื่องเล็กน้อยสำหรับ regex ที่มีอยู่มากมาย ทำไมไม่ทำเช่นนั้นในฐานะส่วนที่ 2 และมีส่วนที่ 1 ด้วยคอลัมน์ fips และแถว
Bobby น้อยตาราง

2
@ Josh เป็นจุดที่ดีในขณะที่แต่ละส่วนของ regex นั้น "เข้าใจง่าย" เพื่อทำความเข้าใจ แต่ regex ที่ยาวสามารถซับซ้อนได้อย่างรวดเร็ว ฉันเพิ่มคำอธิบายสำหรับผู้อ่านในอนาคต! (ฉันต้องอัปเดตลิงก์ไปยังเอกสารที่อธิบายเกี่ยวกับ(?P<label>...)ไวยากรณ์ด้วย! ฉันไม่รู้ว่าทำไมฉันถึงใช้ regex ที่ซับซ้อนมากขึ้นชัดเจนว่าคนที่เรียบง่ายสามารถทำงานได้ hmmmm
Andy Hayden

1
ดูเป็นมิตรกว่ามาก <group_name>ฉันดีใจที่คุณได้เพราะมันทำให้ผมดูเอกสารที่จะเข้าใจ ตอนนี้ฉันรู้แล้วว่ามันทำให้รหัสของฉันสั้นกระชับ
Little Bobby Tables


23

หากคุณไม่ต้องการสร้างดาต้าเฟรมใหม่หรือถ้าดาต้าเฟรมของคุณมีคอลัมน์มากกว่าที่คุณต้องการแยกคุณสามารถทำได้:

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

1
ฉันได้รับzip argument #1 must support iterationข้อผิดพลาด python 2.7
Allan Ruin

20

คุณสามารถใช้str.splitช่องว่าง (ตัวคั่นเริ่มต้น) และพารามิเตอร์expand=TrueสำหรับDataFrameด้วยกำหนดให้กับคอลัมน์ใหม่:

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

การแก้ไขหากต้องการลบคอลัมน์เดิมด้วย 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

อะไรที่เหมือนกัน:

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

หากได้รับข้อผิดพลาด:

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

ValueError: คอลัมน์ต้องมีความยาวเท่ากับคีย์

คุณสามารถตรวจสอบและมันกลับคอลัมน์ 4 DataFrameไม่เพียง 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

จากนั้นโซลูชันจะผนวกใหม่DataFrameโดย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

ด้วยการลบคอลัมน์เดิม (ถ้ามีอีกคอลัมน์หนึ่ง):

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

หากคุณต้องการแยกสตริงออกเป็นสองคอลัมน์โดยใช้ตัวคั่นคุณสามารถละเว้นพารามิเตอร์ 'การแยกสูงสุด'
คุณสามารถใช้ได้:

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

สิ่งนี้จะสร้างคอลัมน์ให้มากที่สุดโดยอัตโนมัติตามจำนวนฟิลด์สูงสุดที่รวมอยู่ในสตริงเริ่มต้นของคุณ


6

แปลกใจฉันยังไม่เห็นอันนี้ หากคุณต้องการเพียงสองแยกฉันขอแนะนำ . .

Series.str.partition

partition ทำการแยกหนึ่งครั้งบนตัวแยก

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

หากคุณต้องการเปลี่ยนชื่อแถว

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

หากคุณต้องการเข้าร่วมกลับไปที่เดิมใช้joinหรือ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

ฉันชอบส่งออกชุดหมีแพนด้าที่เกี่ยวข้อง (เช่นคอลัมน์ที่ฉันต้องการ) โดยใช้ฟังก์ชั่นApplyเพื่อแบ่งเนื้อหาคอลัมน์ออกเป็นหลาย ๆ ชุดแล้วเข้าร่วมคอลัมน์ที่สร้างขึ้นไปยัง DataFrame ที่มีอยู่ แน่นอนคอลัมน์แหล่งที่มาควรจะถูกลบออก

เช่น

 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)

ในการแยกฟังก์ชั่นสตริงคำสองคำควรเป็นดังนี้:

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

0

ฉันเห็นว่าไม่มีใครใช้วิธีสไลซ์ดังนั้นฉันจึงใส่ 2 เซนต์ที่นี่

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

วิธีนี้จะสร้างสองคอลัมน์ใหม่


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.