Saya bekerja dengan file csv besar dan di sebelah kolom terakhir memiliki string teks yang ingin saya pisahkan dengan pembatas tertentu. Saya bertanya-tanya apakah ada cara sederhana untuk melakukan ini menggunakan panda atau python?
CustNum CustomerName ItemQty Item Seatblocks ItemExt
32363 McCartney, Paul 3 F04 2:218:10:4,6 60
31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300
Saya ingin membagi spasi (' ')
dan kemudian titik dua (':')
di Seatblocks
kolom, tetapi setiap sel akan menghasilkan jumlah kolom yang berbeda. Saya memiliki fungsi untuk mengatur ulang kolom sehingga Seatblocks
kolom berada di akhir lembar, tetapi saya tidak yakin apa yang harus dilakukan dari sana. Saya dapat melakukannya di excel dengan text-to-columns
fungsi bawaan dan makro cepat, tetapi kumpulan data saya memiliki terlalu banyak catatan untuk ditangani oleh excel.
Pada akhirnya, saya ingin membuat catatan seperti John Lennon dan membuat beberapa baris, dengan info dari setiap set kursi di baris terpisah.
Jawaban:
Ini membagi Seatblock dengan ruang dan memberikan barisnya masing-masing.
In [43]: df Out[43]: CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300 In [44]: s = df['Seatblocks'].str.split(' ').apply(Series, 1).stack() In [45]: s.index = s.index.droplevel(-1) # to line up with df's index In [46]: s.name = 'Seatblocks' # needs a name to join In [47]: s Out[47]: 0 2:218:10:4,6 1 1:13:36:1,12 1 1:13:37:1,13 Name: Seatblocks, dtype: object In [48]: del df['Seatblocks'] In [49]: df.join(s) Out[49]: CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 32363 McCartney, Paul 3 F04 60 2:218:10:4,6 1 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13
Atau, untuk memberikan setiap string yang dipisahkan oleh titik dua di kolomnya sendiri:
In [50]: df.join(s.apply(lambda x: Series(x.split(':')))) Out[50]: CustNum CustomerName ItemQty Item ItemExt 0 1 2 3 0 32363 McCartney, Paul 3 F04 60 2 218 10 4,6 1 31316 Lennon, John 25 F01 300 1 13 36 1,12 1 31316 Lennon, John 25 F01 300 1 13 37 1,13
Ini sedikit jelek, tapi mungkin seseorang akan setuju dengan solusi yang lebih cantik.
sumber
DataFrame
sangat cepat. Dalam kasus saya, menjalankan kode pada tabel ~ 200M menghasilkan penggunaan ~ 10G memori (+ swap ...).split()
, karena hanyareduce()
melalui kolom berfungsi seperti pesona. Masalahnya mungkin terletak padastack()
...NameError: name 'Series' is not defined
untuk ini. darimanaSeries
asalnya? EDIT: tidak apa-apa, seharusnyapandas.Series
karena ini mengacu pada item daripandas
from pandas import Series
untuk kenyamanan / singkatnya.Beda dari Dan, saya anggap jawabannya cukup elegan ... tapi sayangnya itu juga sangat tidak efisien. Jadi, karena pertanyaan menyebutkan "file csv besar" , izinkan saya menyarankan untuk mencoba solusi shell Dan:
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print df['col'].apply(lambda x : pd.Series(x.split(' '))).head()"
... dibandingkan dengan alternatif ini:
time python -c "import pandas as pd; from scipy import array, concatenate; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(concatenate(df['col'].apply( lambda x : [x.split(' ')]))).head()"
... dan ini:
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)]))).head()"
Yang kedua hanya menahan diri dari mengalokasikan 100.000 Seri, dan ini cukup untuk membuatnya sekitar 10 kali lebih cepat. Tetapi solusi ketiga, yang ironisnya membuang banyak panggilan ke str.split () (ini disebut sekali per kolom per baris, jadi tiga kali lebih banyak daripada dua solusi lainnya), adalah sekitar 40 kali lebih cepat dari yang pertama, karena bahkan menghindari untuk memasukkan 100 000 daftar. Dan ya, ini pasti sedikit jelek ...
EDIT: jawaban ini menyarankan cara menggunakan "to_list ()" dan untuk menghindari kebutuhan lambda. Hasilnya seperti ini
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(df.col.str.split().tolist()).head()"
yang bahkan lebih efisien daripada solusi ketiga, dan tentunya jauh lebih elegan.
EDIT: bahkan lebih sederhana
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print pd.DataFrame(list(df.col.str.split())).head()"
bekerja juga, dan hampir seefisien.
EDIT: bahkan lebih sederhana ! Dan menangani NaN (tetapi kurang efisien):
time python -c "import pandas as pd; df = pd.DataFrame(['a b c']*100000, columns=['col']); print df.col.str.split(expand=True).head()"
sumber
tolist()
sempurna. Dalam kasus saya, saya hanya menginginkan salah satu bagian data dalam daftar dan dapat langsung menambahkan satu kolom ke df saya yang ada dengan menggunakan .ix:df['newCol'] = pd.DataFrame(df.col.str.split().tolist()).ix[:,2]
obect of type 'float' has no len()
yang membingungkan, sampai saya menyadari beberapa baris saya adaNaN
di dalamnya, bukanstr
.import pandas as pd import numpy as np df = pd.DataFrame({'ItemQty': {0: 3, 1: 25}, 'Seatblocks': {0: '2:218:10:4,6', 1: '1:13:36:1,12 1:13:37:1,13'}, 'ItemExt': {0: 60, 1: 300}, 'CustomerName': {0: 'McCartney, Paul', 1: 'Lennon, John'}, 'CustNum': {0: 32363, 1: 31316}, 'Item': {0: 'F04', 1: 'F01'}}, columns=['CustNum','CustomerName','ItemQty','Item','Seatblocks','ItemExt']) print (df) CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300
Solusi serupa lainnya dengan rantai adalah penggunaan
reset_index
danrename
:print (df.drop('Seatblocks', axis=1) .join ( df.Seatblocks .str .split(expand=True) .stack() .reset_index(drop=True, level=1) .rename('Seatblocks') )) CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 32363 McCartney, Paul 3 F04 60 2:218:10:4,6 1 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13
Jika dalam kolom TIDAK
NaN
nilai, solusi tercepat adalah menggunakanlist
pemahaman denganDataFrame
konstruktor:df = pd.DataFrame(['a b c']*100000, columns=['col']) In [141]: %timeit (pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)])))) 1 loop, best of 3: 211 ms per loop In [142]: %timeit (pd.DataFrame(df.col.str.split().tolist())) 10 loops, best of 3: 87.8 ms per loop In [143]: %timeit (pd.DataFrame(list(df.col.str.split()))) 10 loops, best of 3: 86.1 ms per loop In [144]: %timeit (df.col.str.split(expand=True)) 10 loops, best of 3: 156 ms per loop In [145]: %timeit (pd.DataFrame([ x.split() for x in df['col'].tolist()])) 10 loops, best of 3: 54.1 ms per loop
Tetapi jika kolom berisi
NaN
hanya berfungsistr.split
dengan parameterexpand=True
yang mengembalikanDataFrame
( dokumentasi ), dan itu menjelaskan mengapa lebih lambat:df = pd.DataFrame(['a b c']*10, columns=['col']) df.loc[0] = np.nan print (df.head()) col 0 NaN 1 a b c 2 a b c 3 a b c 4 a b c print (df.col.str.split(expand=True)) 0 1 2 0 NaN None None 1 a b c 2 a b c 3 a b c 4 a b c 5 a b c 6 a b c 7 a b c 8 a b c 9 a b c
sumber
expand=True
opsi untuk bekerjapandas.DataFrames
saat menggunakan.str.split()
misalnya.Pendekatan lain akan seperti ini:
temp = df['Seatblocks'].str.split(' ') data = data.reindex(data.index.repeat(temp.apply(len))) data['new_Seatblocks'] = np.hstack(temp)
sumber
Bisa juga menggunakan groupby () tanpa perlu join dan stack ().
Gunakan contoh data di atas:
import pandas as pd import numpy as np df = pd.DataFrame({'ItemQty': {0: 3, 1: 25}, 'Seatblocks': {0: '2:218:10:4,6', 1: '1:13:36:1,12 1:13:37:1,13'}, 'ItemExt': {0: 60, 1: 300}, 'CustomerName': {0: 'McCartney, Paul', 1: 'Lennon, John'}, 'CustNum': {0: 32363, 1: 31316}, 'Item': {0: 'F04', 1: 'F01'}}, columns=['CustNum','CustomerName','ItemQty','Item','Seatblocks','ItemExt']) print(df) CustNum CustomerName ItemQty Item Seatblocks ItemExt 0 32363 McCartney, Paul 3 F04 2:218:10:4,6 60 1 31316 Lennon, John 25 F01 1:13:36:1,12 1:13:37:1,13 300 #first define a function: given a Series of string, split each element into a new series def split_series(ser,sep): return pd.Series(ser.str.cat(sep=sep).split(sep=sep)) #test the function, split_series(pd.Series(['a b','c']),sep=' ') 0 a 1 b 2 c dtype: object df2=(df.groupby(df.columns.drop('Seatblocks').tolist()) #group by all but one column ['Seatblocks'] #select the column to be split .apply(split_series,sep=' ') # split 'Seatblocks' in each group .reset_index(drop=True,level=-1).reset_index()) #remove extra index created print(df2) CustNum CustomerName ItemQty Item ItemExt Seatblocks 0 31316 Lennon, John 25 F01 300 1:13:36:1,12 1 31316 Lennon, John 25 F01 300 1:13:37:1,13 2 32363 McCartney, Paul 3 F04 60 2:218:10:4,6
sumber
0 31316 Lennon, John 25 F01 300 1:13:36:1,12 A
dan baris berikutnya0 31316 Lennon, John 25 F01 300 1:13:37:1,13 B
Ini tampaknya metode yang jauh lebih mudah daripada yang disarankan di tempat lain di utas ini.
pisahkan baris dalam bingkai data panda
sumber