làm thế nào để chia cột các bộ giá trị trong khung dữ liệu gấu trúc?


88

Tôi có một khung dữ liệu gấu trúc (đây chỉ là một phần nhỏ)

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

Tôi muốn tách tất cả các cột có chứa các bộ giá trị. Ví dụ, tôi muốn thay thế cột LCVbằng các cột LCV-aLCV-b.

Làm thế nào tôi có thể làm điều đó?

Câu trả lời:


160

Bạn có thể làm điều này bằng cách thực hiện pd.DataFrame(col.tolist())trên cột đó:

In [2]: df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]})                                                                                                                      

In [3]: df                                                                                                                                                                      
Out[3]: 
   a       b
0  1  (1, 2)
1  2  (3, 4)

In [4]: df['b'].tolist()                                                                                                                                                        
Out[4]: [(1, 2), (3, 4)]

In [5]: pd.DataFrame(df['b'].tolist(), index=df.index)                                                                                                                                          
Out[5]: 
   0  1
0  1  2
1  3  4

In [6]: df[['b1', 'b2']] = pd.DataFrame(df['b'].tolist(), index=df.index)                                                                                                                       

In [7]: df                                                                                                                                                                      
Out[7]: 
   a       b  b1  b2
0  1  (1, 2)   1   2
1  2  (3, 4)   3   4

Lưu ý: trong phiên bản cũ hơn, câu trả lời này được khuyến nghị sử dụng df['b'].apply(pd.Series)thay vì pd.DataFrame(df['b'].tolist(), index=df.index). Điều đó cũng hoạt động (vì nó tạo thành mỗi bộ một Series, sau đó được xem như một hàng của khung dữ liệu), nhưng chậm hơn / sử dụng nhiều bộ nhớ hơn so với tolistphiên bản, như được lưu ý bởi các câu trả lời khác ở đây (cảm ơn @denfromufa) .
Tôi đã cập nhật câu trả lời này để đảm bảo câu trả lời dễ thấy nhất có giải pháp tốt nhất.


2
có cách nào để tự động hóa nó do số lượng lớn các cột không?
Donbeo

Tôi nghĩ không trực tiếp. Nhưng bạn có thể dễ dàng viết một hàm cho nó bằng cách sử dụng mã trên (+ loại bỏ một bản gốc)
Joris

Nếu bạn có một số lượng lớn các cột, bạn có thể muốn xem xét để 'sắp xếp' dữ liệu của mình: vita.had.co.nz/papers/tidy-data.html Bạn có thể thực hiện việc này bằng cách sử dụng chức năng tan.
Axel

.apply (pd.Series) hoạt động tốt, nhưng đối với các tập dữ liệu lớn sẽ tiêu tốn nhiều bộ nhớ và có thể gây ra Lỗi bộ nhớ
Yury Wallet

26

Trên các tập dữ liệu lớn hơn nhiều, tôi thấy rằng .apply()có ít đơn đặt hàng chậm hơnpd.DataFrame(df['b'].values.tolist(), index=df.index)

Vấn đề hiệu suất này đã được đóng trong GitHub, mặc dù tôi không đồng ý với quyết định này:

https://github.com/pandas-dev/pandas/issues/11615

CHỈNH SỬA: dựa trên câu trả lời này: https://stackoverflow.com/a/44196843/2230844


5
pd.DataFrame(df['b'].tolist())mà không .valuescó vẻ như hoạt động tốt quá. (Và cảm ơn, giải pháp của bạn là nhiều nhanh hơn .apply())
Swier

Tôi đã lo lắng về việc nắm bắt chỉ mục, do đó sử dụng rõ ràng các giá trị.
denfromufa 20/09/2016

1
giải pháp của @denfromufa hoạt động siêu nhanh df [['b1', 'b2']] = pd.DataFrame (df ['b']. values.tolist (), index = df.index) và không gây ra Lỗi bộ nhớ (như so với .apply (pd.Series))
Yury Wallet

18

Trình truy strcập có sẵn cho pandas.Seriescác đối tượng dtype == objectthực sự là một có thể lặp lại.

Giả sử một 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)

Chúng tôi có thể kiểm tra xem nó có phải là một

from collections import Iterable

isinstance(df.col.str, Iterable)

True

Sau đó, chúng tôi có thể gán từ nó giống như chúng tôi thực hiện các bước lặp khác:

var0, var1 = 'xy'
print(var0, var1)

x y

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

Vì vậy, trong một dòng, chúng ta có thể chỉ định cả hai cột

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

Giải pháp nhanh hơn

Chỉ phức tạp hơn một chút, chúng ta có thể sử dụng zipđể tạo một tệp có thể lặp lại tương tự

df['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

Nội tuyến

Có nghĩa là, không thay đổi hiện tại df
Điều này hoạt động vì assignlấy các đối số từ khóa trong đó từ khóa là tên cột mới (hoặc hiện có) và giá trị sẽ là giá trị của cột mới. Bạn có thể sử dụng một từ điển và giải nén nó **và để nó hoạt động như các đối số từ khóa. Vì vậy, đây là một cách thông minh để gán một cột mới có tên 'g'là mục đầu tiên trong có thể df.col.strlặp và 'h'đó là mục thứ hai trong có thể df.col.strlặp.

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

Phiên bản của tôi về listcách tiếp cận

Với khả năng hiểu danh sách hiện đại và khả năng giải nén có thể thay đổi.
Lưu ý: cũng sử dụng nội tuyếnjoin

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

Phiên bản đột biến sẽ là

df[['e', 'f']] = pd.DataFrame([*df.col], df.index)

Kiểm tra thời gian ngây thơ

DataFrame ngắn

Sử dụng một định nghĩa ở trên

%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)
DataFrame dài

Lớn gấp 10 ^ 3 lần

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)

2
Cân nhắc thêm TL; DR: df['a'], df['b'] = df.col.str:)
mirekphd

11

Tôi nghĩ một cách đơn giản hơn là:

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

1
Giải pháp này thực sự đơn giản hơn nhiều
ApplePie

@jinhuawang, có vẻ như đây là hack trên phần strđại diện của một pd.Seriesđối tượng. Bạn có thể giải thích nó hoạt động như thế nào không ?!
denfromufa

Tôi nghĩ đó chỉ là cách đối tượng str hoạt động? bạn có thể truy cập vào đối tượng mảng với str
Kim Hoa Wang

Điều gì sẽ xảy ra nếu một số hàng có các bộ giá trị với một số giá trị khác nhau?
mammykins

Tôi nghĩ điều này nên được chấp nhận. Đó là 'pandas-onic' ... nếu đó là một điều.
Natacha

8

Tôi biết điều này là từ một thời gian trước, nhưng một cảnh báo về giải pháp thứ hai:

pd.DataFrame(df['b'].values.tolist())

là nó sẽ loại bỏ chỉ mục một cách rõ ràng và thêm vào một chỉ mục tuần tự mặc định, trong khi câu trả lời được chấp nhận

apply(pd.Series)

sẽ không, vì kết quả áp dụng sẽ giữ lại chỉ mục hàng. Trong khi thứ tự ban đầu được giữ lại từ mảng ban đầu, gấu trúc sẽ cố gắng khớp các chỉ dẫn từ hai khung dữ liệu.

Điều này có thể rất quan trọng nếu bạn đang cố gắng đặt các hàng thành một mảng được lập chỉ mục số và gấu trúc sẽ tự động cố gắng khớp chỉ mục của mảng mới với mảng cũ và gây ra một số sai lệch trong thứ tự.

Một giải pháp kết hợp tốt hơn sẽ là đặt chỉ mục của khung dữ liệu gốc vào khung mới, tức là

pd.DataFrame(df['b'].values.tolist(), index=df.index)

Điều này sẽ duy trì tốc độ sử dụng phương pháp thứ hai trong khi đảm bảo thứ tự và chỉ mục được giữ lại trên kết quả.


Tôi đã chỉnh sửa câu trả lời của mình dựa trên quan sát lập chỉ mục của bạn, cảm ơn!
denfromufa
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.