Pandas 데이터 프레임에서 튜플 열을 분할하는 방법은 무엇입니까?


88

나는 판다 데이터 프레임을 가지고 있습니다 (이것은 단지 작은 조각입니다)

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

튜플을 포함하는 모든 열을 분할하고 싶습니다. 예를 들어 LCV열을 LCV-a및 열로 바꾸고 싶습니다 LCV-b.

어떻게 할 수 있습니까?

답변:


159

pd.DataFrame(col.tolist())해당 열에서 이를 수행 할 수 있습니다 .

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

참고 : 이전 버전에서이 답변 df['b'].apply(pd.Series)pd.DataFrame(df['b'].tolist(), index=df.index). 그것도 작동하지만 (각 튜플을 Series로 만들고 데이터 프레임의 행 tolist으로 표시되기 때문에) 여기에 다른 답변에서 언급했듯이 버전 보다 느리거나 더 많은 메모리를 사용 합니다 (@denfromufa 덕분에) .
가장 눈에 띄는 답변이 최상의 솔루션인지 확인하기 위해이 답변을 업데이트했습니다.


2
많은 수의 열로 인해 자동화하는 방법이 있습니까?
Donbeo 2015

직접적으로 생각하지 않습니다. 하지만 위의 코드를 사용하여 쉽게 함수를 작성할 수 있습니다 (+ 원래 코드 제거)
joris

열이 많은 경우 데이터를 '정리'하는 것이 좋습니다 . vita.had.co.nz/papers/tidy-data.html 용융 기능을 사용하여이 작업을 수행 할 수 있습니다.
Axel

.apply (pd.Series)의 벌금을 작동하지만, 대규모 데이터 세트에 대한 메모리를 많이 소비하고 메모리 오류가 발생할 수 있습니다
Yury 지갑

26

훨씬 더 큰 데이터 세트에, 그 발견 .apply()보다 느린 몇 주문입니다pd.DataFrame(df['b'].values.tolist(), index=df.index)

이 결정에 동의하지 않지만이 성능 문제는 GitHub에서 종료되었습니다.

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

편집 :이 답변을 기반으로 : https://stackoverflow.com/a/44196843/2230844


5
pd.DataFrame(df['b'].tolist())없이도 .values잘 작동 하는 것 같습니다. (그리고 감사 솔루션은 훨씬 보다 빠른 .apply())
Swier

인덱스 캡처에 대해 걱정했기 때문에 .values를 명시 적으로 사용했습니다.
denfromufa

1
@denfromufa의 솔루션은 매우 빠르게 작동합니다 df [[ 'b1', 'b2']] = pd.DataFrame (df [ 'b']. values.tolist (), index = df.index). .apply (pd.Series)와 비교)
Yury Wallet

17

의 개체에 str사용할 수 있는 접근 자는 실제로 반복 가능합니다.pandas.Seriesdtype == object

가정 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)

반복 가능한지 테스트 할 수 있습니다.

from collections import Iterable

isinstance(df.col.str, Iterable)

True

그런 다음 다른 이터 러블처럼 할당 할 수 있습니다.

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

x y

가장 간단한 솔루션

따라서 한 줄에 두 열을 할당 할 수 있습니다.

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

더 빠른 솔루션

약간 더 복잡 zip하지만 비슷한 반복 가능한

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

인라인

의미, 기존을 변경하지 마십시오 . 키워드가 새 (또는 기존) 열 이름이고 값이 새 열의 값인 키워드 인수를 사용 df
하기 때문에 작동합니다 assign. 사전을 사용하고 압축을 풀고 **키워드 인수로 작동하도록 할 수 있습니다 . 따라서 이것은 이터 러블 'g'의 첫 번째 항목이고 df.col.str이터 러블 'h'의 두 번째 항목 인 새 열을 할당하는 영리한 방법입니다 df.col.str.

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접근 방식

현대적인 목록 이해 및 변수 풀기.
참고 : 또한 인라인 사용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

돌연변이 버전은

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

순진한 시간 테스트

짧은 DataFrame

위에 정의 된 것을 사용하십시오.

%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

10 ^ 3 배 더 큼

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
TL; DR : df['a'], df['b'] = df.col.str:) 추가 고려
mirekphd

11

더 간단한 방법은 다음과 같습니다.

>>> 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
이 솔루션은 훨씬 더 간단 참
ApplePie

@jinhuawang 이것은 객체 의 str표현 위에 해킹 된 것처럼 보입니다 pd.Series. 이것이 어떻게 작동하는지 설명해 주시겠습니까?!
denfromufa 19.04.14

나는 그것이 str 객체가 작동하는 방식이라고 생각합니까? 당신은 STR과 배열 개체에 액세스 할 수
진화 왕에게

일부 행에 값 수가 다른 튜플이 있으면 어떻게됩니까?
mammykins

나는 이것이 받아 들여 져야한다고 생각한다. 더 '판다-오닉'... 그게 문제라면.
Natacha

8

나는 이것이 얼마 전의 것임을 알고 있지만 두 번째 해결책에 대한 경고입니다.

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

명시 적으로 색인을 버리고 기본 순차 색인을 추가하는 반면 허용되는 대답은

apply(pd.Series)

적용 결과가 행 인덱스를 유지하므로 그렇지 않습니다. 순서는 처음에 원래 배열에서 유지되지만 pandas는 두 데이터 프레임의 인덱스를 일치 시키려고합니다.

이것은 행을 숫자 인덱스 배열로 설정하려는 경우 매우 중요 할 수 있으며 pandas는 자동으로 새 배열의 인덱스를 이전 배열과 일치 시키려고 시도하여 순서가 약간 왜곡됩니다.

더 나은 하이브리드 솔루션은 원래 데이터 프레임의 인덱스를 새 데이터 프레임에 설정하는 것입니다.

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

두 번째 방법을 사용하는 속도를 유지하면서 순서와 인덱싱이 결과에 유지되도록합니다.


인덱싱 관찰을 기반으로 내 대답을 편집했습니다. 감사합니다!
denfromufa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.