Bagaimana cara membagi kolom menjadi dua kolom?

196

Saya memiliki bingkai data dengan satu kolom dan saya ingin membaginya menjadi dua kolom, dengan satu tajuk kolom sebagai ' fips'dan yang lainnya'row'

Kerangka data saya dfterlihat seperti ini:

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

Saya tidak tahu cara menggunakan df.row.str[:]untuk mencapai tujuan saya untuk memisahkan sel baris. Saya dapat menggunakan df['fips'] = hellountuk menambahkan kolom baru dan mengisinya hello. Ada ide?

         fips       row
0    00000 UNITED STATES
1    01000 ALABAMA 
2    01001 Autauga County, AL
3    01003 Baldwin County, AL
4    01005 Barbour County, AL
ak
sumber
3
bagaimana Anda memuat data Anda ke dalam panda? Anda mungkin dapat Laod data dalam format yang Anda inginkan menggunakan read_table()atau read_fwf()
zach

Jawaban:

137

Mungkin ada cara yang lebih baik, tapi ini satu pendekatan:

                            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
akar
sumber
6
Ketahuilah bahwa .tolist () akan menghapus indeks apa pun yang Anda miliki, sehingga Dataframe baru Anda akan diindeks ulang dari 0 (Tidak masalah dalam kasus spesifik Anda).
Crashthatch
10
@ Crashthatch - sekali lagi Anda bisa menambahkan index = df.indexdan Anda baik.
root
bagaimana jika satu sel tidak dapat dibagi?
Nisba
@Nisba: Jika ada sel yang tidak dapat dipecah (mis. String tidak mengandung ruang untuk kasus ini) itu masih akan bekerja tetapi satu bagian dari perpecahan akan kosong. Situasi lain akan terjadi jika Anda memiliki tipe campuran di kolom dengan setidaknya satu sel yang berisi jenis nomor apa pun. Kemudian splitmetode mengembalikan NaN dan tolistmetode akan mengembalikan nilai ini sebagaimana adanya (NaN) yang akan menghasilkan ValueError(untuk mengatasi masalah ini Anda dapat melemparkannya ke tipe string sebelum membelah). Saya sarankan Anda untuk mencobanya sendiri. Ini cara belajar terbaik :-)
Nerxis
@ techkuz: Apakah Anda yakin dfmemiliki rowtajuk kolom? Anda mungkin berpikir itu semacam atribut DataFrame tetapi cukup jelas ini adalah nama kolom. Terserah Anda bagaimana Anda membuat dan menentukan header kolom Anda jadi jika Anda menggunakan yang berbeda gunakan itu (misalnya df.my_column_name.split(...))
Nerxis
388

TL; versi DR:

Untuk kasus sederhana:

  • Saya memiliki kolom teks dengan pembatas dan saya ingin dua kolom

Solusi paling sederhana adalah:

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

Atau Anda dapat membuat membuat DataFrame dengan satu kolom untuk setiap entri pemisahan secara otomatis dengan:

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

Anda harus menggunakan expand=Truejika string Anda memiliki jumlah split yang tidak seragam dan Anda ingin Nonemengganti nilai yang hilang.

Perhatikan bagaimana, dalam kedua kasus itu, .tolist()metode ini tidak perlu. Tidak juga zip().

Secara terperinci:

Solusi Andy Hayden paling baik dalam menunjukkan kekuatan str.extract()metode ini.

Tetapi untuk pemisahan sederhana di atas pemisah yang diketahui (seperti, membelah dengan garis, atau membelah dengan spasi), .str.split()metode ini cukup 1 . Ini beroperasi pada kolom (Seri) string, dan mengembalikan kolom (Seri) daftar:

>>> 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: Jika Anda tidak yakin apa yang dilakukan dua parameter pertama .str.split(), saya merekomendasikan dokumen untuk versi metode Python sederhana .

Tapi bagaimana cara Anda:

  • kolom yang berisi daftar dua elemen

untuk:

  • dua kolom, masing-masing berisi elemen daftar masing-masing?

Nah, kita perlu melihat lebih dekat .stratribut kolom.

Ini adalah objek ajaib yang digunakan untuk mengumpulkan metode yang memperlakukan setiap elemen dalam kolom sebagai string, dan kemudian menerapkan metode masing-masing di setiap elemen seefisien mungkin:

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

Tetapi ia juga memiliki antarmuka "pengindeksan" untuk mendapatkan setiap elemen string dengan indeksnya:

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

0    A
1    A
Name: AB, dtype: object

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

0    1
1    2
Name: AB, dtype: object

Tentu saja, antarmuka pengindeksan .strini tidak terlalu peduli jika setiap elemen yang diindeksinya sebenarnya adalah string, asalkan dapat diindeks, jadi:

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

Kemudian, ini adalah masalah sederhana mengambil keuntungan dari tuple Python membongkar iterables untuk dilakukan

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

Tentu saja, mengeluarkan DataFrame dari pemisahan kolom string sangat berguna sehingga .str.split()metode ini dapat melakukannya untuk Anda dengan expand=Trueparameter:

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

    0   1
0  A1  B1
1  A2  B2

Jadi, cara lain untuk mencapai apa yang kita inginkan adalah dengan melakukan:

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

The expand=Trueversi, meskipun lagi, memiliki keuntungan yang berbeda atas metode tupel membongkar. Tuple unpacking tidak cocok dengan pemisahan dengan panjang yang berbeda:

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

Tetapi expand=Trueatasi dengan baik dengan menempatkan Nonedi kolom yang tidak memiliki "pemisahan" yang cukup:

>>> 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
LeoRochael
sumber
df ['A'], df ['B'] = df ['AB']. str.split ('', 1) .str Apa arti dari '1' dalam pemisahan ('', 1)?
Hariprasad
@Hariprasad, ini adalah jumlah split maksimum. Saya telah menambahkan tautan ke dokumen untuk Versi Python dari .split()metode yang menjelaskan dua parameter pertama lebih baik daripada dokumen Pandas.
LeoRochael
5
panda 1.0.0 melaporkan "FutureWarning: Iterasi kolom atas karakter akan ditinggalkan dalam rilis mendatang."
Frank
1
Ini berfungsi di bawah Python 1.0.1. df.join(df['AB'].str.split('-', 1, expand=True).rename(columns={0:'A', 1:'B'}))
Martien Lubberink
59

Anda dapat mengekstrak bagian-bagian yang berbeda dengan cukup rapi menggunakan pola 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]

Untuk menjelaskan regex yang agak panjang:

(?P<fips>\d{5})
  • Cocok dengan lima digit ( \d) dan beri nama "fips".

Bagian selanjutnya:

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

Apakah ( |) salah satu dari dua hal:

(?P<state>[A-Z ]*$)
  • Cocok dengan angka ( *) dari huruf besar atau spasi ( [A-Z ]) dan beri nama ini "state"sebelum akhir string ( $),

atau

(?P<county>.*?), (?P<state_code>[A-Z]{2}$))
  • cocok dengan yang lain ( .*) lalu
  • koma dan spasi kemudian
  • cocok dengan dua digit state_codesebelum akhir string ( $).

Dalam contoh:
Perhatikan bahwa dua baris pertama menekan "negara" (meninggalkan NaN di kolom county dan state_code), sementara tiga baris terakhir menghantam county, state_code (meninggalkan NaN di kolom negara).

Andy Hayden
sumber
Ini jelas merupakan solusi terbaik tetapi mungkin agak berlebihan bagi sebagian orang dengan regex yang sangat luas. Mengapa tidak melakukan itu sebagai bagian 2 dan memiliki bagian 1 hanya dengan kolom fips dan baris?
Little Bobby Tables
2
@ ya ampun itu poin yang bagus, sementara bagian-bagian individu dari regex "mudah" untuk dipahami, regex lama bisa menjadi rumit dengan cepat. Saya menambahkan beberapa penjelasan untuk pembaca masa depan! (Saya juga harus memperbarui tautan ke dokumen yang menjelaskan (?P<label>...)sintaks! Saya tidak tahu mengapa saya menggunakan regex yang lebih kompleks, jelas yang sederhana bisa berfungsi hmmmm
Andy Hayden
1
Terlihat lebih ramah. Saya senang Anda melakukannya karena saya melihat dokumen untuk memahami <group_name>. Sekarang saya tahu itu membuat kode saya sangat ringkas.
Little Bobby Tables
44
df[['fips', 'row']] = df['row'].str.split(' ', n=1, expand=True)
Bhagabat Behera
sumber
22

Jika Anda tidak ingin membuat kerangka data baru, atau jika kerangka data Anda memiliki lebih banyak kolom daripada yang ingin Anda bagi, Anda dapat:

df["flips"], df["row_name"] = zip(*df["row"].str.split().tolist())
del df["row"]  
keberwein
sumber
1
Saya mendapatkan zip argument #1 must support iterationkesalahan, python 2.7
Allan Ruin
20

Anda dapat menggunakan str.splitdengan spasi putih (pemisah default) dan parameter expand=Trueuntuk DataFramedengan menetapkan ke kolom baru:

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

Modifikasi jika perlu menghapus kolom asli dengan 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

Seperti apa itu:

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

Jika mendapatkan kesalahan:

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

ValueError: Kolom harus sama panjang dengan kunci

Anda dapat memeriksa dan mengembalikan 4 kolom DataFrame, tidak hanya 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

Maka solusi ditambahkan baru DataFramedengan 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

Dengan menghapus kolom asli (jika ada juga kolom lain):

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   
jezrael
sumber
8

Jika Anda ingin membagi string menjadi lebih dari dua kolom berdasarkan pembatas, Anda dapat menghilangkan parameter 'pemisahan maksimum'.
Kamu bisa memakai:

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

Ini akan secara otomatis membuat kolom sebanyak jumlah bidang maksimum yang termasuk dalam salah satu string awal Anda.

Melati
sumber
6

Terkejut saya belum melihat yang satu ini. Jika Anda hanya perlu dua split, saya sangat merekomendasikan. . .

Series.str.partition

partition melakukan satu split pada separator, dan umumnya cukup performant.

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

Jika Anda perlu mengganti nama baris,

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

Jika Anda perlu menggabungkan ini kembali ke aslinya, gunakan joinatau 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
cs95
sumber
0

Saya lebih suka mengekspor seri panda yang sesuai (yaitu kolom yang saya butuhkan), menggunakan fungsi yang berlaku untuk membagi konten kolom menjadi beberapa seri dan kemudian bergabung dengan kolom yang dihasilkan ke DataFrame yang ada. Tentu saja, kolom sumber harus dihapus.

misalnya

 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)

Untuk membagi dua kata fungsi string harus seperti itu:

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

Saya melihat bahwa tidak ada yang menggunakan metode slice, jadi di sini saya meletakkan 2 sen saya di sini.

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

Metode ini akan membuat dua kolom baru.

Vingt Cent
sumber