Saya memiliki kerangka data panda (ini hanya sebagian kecil)
>>> d1
y norm test y norm train len(y_train) len(y_test) \
0 64.904368 116.151232 1645 549
1 70.852681 112.639876 1645 549
SVR RBF \
0 (35.652207342877873, 22.95533537448393)
1 (39.563683797747622, 27.382483096332511)
LCV \
0 (19.365430594452338, 13.880062435173587)
1 (19.099614489458364, 14.018867136617146)
RIDGE CV \
0 (4.2907610988480362, 12.416745648065584)
1 (4.18864306788194, 12.980833914392477)
RF \
0 (9.9484841581029428, 16.46902345373697)
1 (10.139848213735391, 16.282141345406522)
GB \
0 (0.012816232716538605, 15.950164822266007)
1 (0.012814519804493328, 15.305745202851712)
ET DATA
0 (0.00034337162272515505, 16.284800366214057) j2m
1 (0.00024811554516431878, 15.556506191784194) j2m
>>>
Saya ingin membagi semua kolom yang berisi tupel. Misalnya saya ingin mengganti kolom LCV
dengan kolom LCV-a
dan LCV-b
.
Bagaimana saya bisa melakukan itu?
Pada kumpulan data yang jauh lebih besar, saya menemukan bahwa
.apply()
beberapa pesanan lebih lambat daripd.DataFrame(df['b'].values.tolist(), index=df.index)
Masalah kinerja ini ditutup di GitHub, meskipun saya tidak setuju dengan keputusan ini:
https://github.com/pandas-dev/pandas/issues/11615
EDIT: berdasarkan jawaban ini: https://stackoverflow.com/a/44196843/2230844
sumber
pd.DataFrame(df['b'].tolist())
tanpa.values
tampaknya bekerja dengan baik juga. (Dan terima kasih, solusi Anda jauh lebih cepat daripada.apply()
)The
str
accessor yang tersedia untukpandas.Series
objekdtype == object
sebenarnya merupakan iterable.Asumsikan
pandas.DataFrame
df
:df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))])) df col 0 (a, 10) 1 (b, 20) 2 (c, 30) 3 (d, 40) 4 (e, 50) 5 (f, 60) 6 (g, 70) 7 (h, 80) 8 (i, 90) 9 (j, 100)
Kami dapat menguji apakah itu dapat diulang
from collections import Iterable isinstance(df.col.str, Iterable) True
Kami kemudian dapat menetapkan darinya seperti kami melakukan iterable lainnya:
var0, var1 = 'xy' print(var0, var1) x y
Solusi paling sederhana
Jadi dalam satu baris kita dapat menetapkan kedua kolom tersebut
df['a'], df['b'] = df.col.str df col a b 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Solusi lebih cepat
Hanya sedikit lebih rumit, kita dapat menggunakan
zip
untuk membuat iterable serupadf['c'], df['d'] = zip(*df.col) df col a b c d 0 (a, 10) a 10 a 10 1 (b, 20) b 20 b 20 2 (c, 30) c 30 c 30 3 (d, 40) d 40 d 40 4 (e, 50) e 50 e 50 5 (f, 60) f 60 f 60 6 (g, 70) g 70 g 70 7 (h, 80) h 80 h 80 8 (i, 90) i 90 i 90 9 (j, 100) j 100 j 100
Di barisan
Artinya, jangan mutasi yang sudah ada
df
Ini berfungsi karena
assign
mengambil argumen kata kunci di mana kata kuncinya adalah nama kolom baru (atau yang sudah ada) dan nilainya akan menjadi nilai kolom baru. Anda dapat menggunakan kamus dan mengekstraknya**
serta bertindak sebagai argumen kata kunci. Jadi ini adalah cara cerdas untuk menetapkan kolom baru bernama'g'
item pertama didf.col.str
iterable dan'h'
itu adalah item kedua didf.col.str
iterable.df.assign(**dict(zip('gh', df.col.str))) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
list
Pendekatan versi sayaDengan pemahaman daftar modern dan pembongkaran variabel.
Catatan: juga menggunakan inline
join
df.join(pd.DataFrame([*df.col], df.index, [*'ef'])) col g h 0 (a, 10) a 10 1 (b, 20) b 20 2 (c, 30) c 30 3 (d, 40) d 40 4 (e, 50) e 50 5 (f, 60) f 60 6 (g, 70) g 70 7 (h, 80) h 80 8 (i, 90) i 90 9 (j, 100) j 100
Versi mutasi akan menjadi
df[['e', 'f']] = pd.DataFrame([*df.col], df.index)
Tes Waktu yang Naif
DataFrame pendekGunakan salah satu yang ditentukan di atas
DataFrame Panjang%timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
10 ^ 3 kali lebih besar
df = pd.concat([df] * 1000, ignore_index=True) %timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sumber
df['a'], df['b'] = df.col.str
:)Menurut saya cara yang lebih sederhana adalah:
>>> import pandas as pd >>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]}) >>> df a b 0 1 (1, 2) 1 2 (3, 4) >>> df['b_a']=df['b'].str[0] >>> df['b_b']=df['b'].str[1] >>> df a b b_a b_b 0 1 (1, 2) 1 2 1 2 (3, 4) 3 4
sumber
str
representasi dari sebuahpd.Series
objek. Bisakah Anda menjelaskan bagaimana ini bekerja ?!Saya tahu ini dari beberapa waktu yang lalu, tetapi peringatan dari solusi kedua:
pd.DataFrame(df['b'].values.tolist())
adalah bahwa ia akan secara eksplisit membuang indeks, dan menambahkan indeks sekuensial default, sedangkan jawaban yang diterima
tidak akan, karena hasil penerapan akan mempertahankan indeks baris. Sementara urutan awalnya dipertahankan dari larik asli, panda akan mencoba mencocokkan indikasi dari dua kerangka data.
Ini bisa menjadi sangat penting jika Anda mencoba menyetel baris ke dalam larik yang diindeks secara numerik, dan panda akan secara otomatis mencoba mencocokkan indeks larik baru dengan yang lama, dan menyebabkan distorsi dalam urutan.
Solusi hibrid yang lebih baik adalah menyetel indeks kerangka data asli ke yang baru, yaitu
pd.DataFrame(df['b'].values.tolist(), index=df.index)
Yang akan mempertahankan kecepatan menggunakan metode kedua sambil memastikan urutan dan pengindeksan dipertahankan pada hasil.
sumber