팬더 : 열의 텍스트를 여러 행으로 나누려면 어떻게합니까?


135

큰 csv 파일로 작업하고 있으며 마지막 열 옆에는 특정 구분 기호로 분할하려는 텍스트 문자열이 있습니다. 팬더 또는 파이썬을 사용 하여이 작업을 수행하는 간단한 방법이 있는지 궁금합니다.

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

나는 공간으로 분할 할 (' ')다음 콜론 (':')에서 Seatblocks열하지만 각 셀은 열 수가 다른 초래할 것이다. Seatblocks열이 시트의 끝에 오도록 열을 다시 정렬하는 기능이 있지만 거기에서 무엇을 해야할지 잘 모르겠습니다. 내장 text-to-columns함수와 빠른 매크로를 사용하여 Excel에서 할 수 있지만 내 데이터 세트에 Excel에서 처리하기에는 너무 많은 레코드가 있습니다.

궁극적으로 John Lennon과 같은 기록을 작성하고 각 좌석 세트의 정보를 별도의 줄에 여러 줄로 작성하고 싶습니다.


이 중대한 문제는 현재 사용하지 않는 존재 팬더에 FlatMap 관련
cdarlint

답변:


203

이것은 좌석 블록을 공간별로 나누고 각각의 행을 제공합니다.

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

또는 각 콜론으로 구분 된 문자열을 자체 열에 제공하려면 다음을 수행하십시오.

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

이것은 조금 추악하지만 누군가가 더 예쁘게 해결책을 찾을 것입니다.


7
@DanAllan은 신청할 때 시리즈에 색인을 제공합니다. 그들은 열 이름이 될 것입니다
Jeff

4
이것이 질문에 대한 대답이지만, split ()은 각 행에 대한 목록을 작성하여 DataFrame매우 빠르게 크기를 늘린다는 것을 언급 할 가치가 있습니다. 필자의 경우 ~ 200M 테이블에서 코드를 실행하면 ~ 10G 메모리 (+ 스왑 ...) 사용이 발생했습니다.
David Nemeskey 2016 년

1
비록 그것이 열을 통한 일이 매력처럼 작용 split()하기 때문에 나는 그것이 때문인지 확실하지 않습니다 reduce(). 문제는 다음에있을 수 있습니다 stack()...
David Nemeskey

4
NameError: name 'Series' is not defined이 오류가 발생 합니다. 어디 Series에서 왔을 까? 편집 : 신경 끄시 고, 그것은해야 pandas.Series그것이에서 항목을 참조하기 때문에pandas
user5359531

2
예, @ user5359531. 나는 from pandas import Series편의 / 부활을 위해.
Dan Allan

52

Dan과는 달리, 그의 대답은 매우 우아하다고 생각하지만 불행히도 매우 비효율적입니다. 따라서 질문에 "큰 csv 파일"이 언급 되었으므로 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()"

...이 대안에 비해 :

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()"

... 이:

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()"

두 번째는 단순히 100 000 Series를 배분하는 것만으로도 10 배 정도 빠르기에 충분합니다. 그러나 str.split ()에 대한 많은 호출을 다소 역설적으로 낭비하는 세 번째 솔루션 (행당 열당 한 번 호출되므로 다른 두 솔루션보다 3 배 더 많이 호출 됨) 은 첫 번째보다 약 40 배 빠릅니다. 심지어 100 000 개의 목록을 인스턴스화하지 않기 때문입니다. 그리고 네, 확실히 조금 추악합니다 ...

편집 : 이 답변 은 "to_list ()"를 사용하고 람다가 필요하지 않도록하는 방법을 제안합니다. 결과는 다음과 같습니다

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()"

이는 세 번째 솔루션보다 훨씬 효율적이며 훨씬 더 우아합니다.

편집 : 더 간단한

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()"

작동하고 거의 효율적입니다.

편집 : 훨씬 간단합니다 ! 그리고 NaN을 처리하지만 효율성은 떨어집니다.

time python -c "import pandas as pd;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print df.col.str.split(expand=True).head()"

이 방법이 사용하는 메모리 양에 약간의 문제가 있으며 약간의 조언을 해줄 수 있는지 궁금합니다. 약 8000 개의 행을 포함하는 DataFrame이 있으며 각각 9216 공백으로 구분 된 8 비트 정수를 포함하는 문자열이 있습니다. 이것은 대략 75MB이지만 마지막 솔루션을 그대로 적용하면 Python은 2GB의 메모리를 사용합니다. 이것이 왜 그런지, 그리고 그것을 해결하기 위해 내가 할 수있는 일을 알려주는 출처의 방향을 알려주시겠습니까? 감사.
castle-bravo

1
많은 목록과 매우 작은 문자열이 있는데, 이는 파이썬에서 메모리 사용에 대한 최악의 경우입니다 (그리고 중간 단계 ".split (). tolist ()"는 순수한 파이썬 객체를 생성합니다). 내가 대신 할 일은 DataFrame을 파일로 덤프 한 다음 read_csv (..., sep = '')를 사용하여 csv로 여는 것입니다. 그러나 주제를 유지하려면 첫 번째 솔루션 (세 번째 솔루션과 함께 느리게 진행되어야 함)은 상대적으로 긴 행 수가 적기 때문에 4 중에서 메모리 사용량이 가장 적은 솔루션 일 수 있습니다.
Pietro Battiston

Pietro, 나는 파일로 저장하고 다시로드하라는 제안을 시도했지만 꽤 잘 작동했습니다. StringIO 객체 에서이 작업을 시도했을 때 문제가 발생하여 문제에 대한 훌륭한 해결책이 여기 에 게시 되었습니다 .
castle-bravo

3
마지막 제안 tolist()은 완벽합니다. 필자의 경우에는 목록에있는 데이터 중 하나만 원했고 .ix를 사용하여 기존 df에 단일 열을 직접 추가 할 수있었습니다.df['newCol'] = pd.DataFrame(df.col.str.split().tolist()).ix[:,2]
fantabolous

대해 뭔가 - 아, 내가 문제가 처음에 일이 점점 가졌다 obect of type 'float' has no len()내가 실현 될 때까지, 이해할 수없는 한을 일부 내 행이 있었다 NaN반대로, 그들 str.
dwanderson

14
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

체인을 사용하는 또 다른 유사한 솔루션은 다음 reset_indexrename같습니다.

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

in 열이 값 이 아닌 NaN 경우 가장 빠른 해결책은 생성자 list와 함께 이해를 사용하는 것 입니다 DataFrame.

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

그러나 열이 포함 된 경우 ( documentation ) 을 반환 하는 매개 변수 NaN로만 작동 str.split하고 왜 더 느린 지 설명합니다.expand=TrueDataFrame

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

어쩌면 예를 들어 사용 expand=True하는 pandas.DataFrames동안 옵션 작업 이 필요하다는 것을 언급 할 가치가 있습니다 .str.split().
holzkohlengrill

@holzkohlengrill-의견 주셔서 감사합니다, 나는 대답에 추가합니다.
jezrael

@ jezrael,이 코드를 실행하는 데 시간이 오래 걸립니다. 정확히 어떻게 더 빨라 집니까? 내가 df [Seablocks] [: 100]의 for x를 서브셋에서만 수행 한 다음 이러한 서브셋으로 연결하려면 for 루프에 넣으면 작동합니까?
bernando_vialli

2

다른 접근 방식은 다음과 같습니다.

temp = df['Seatblocks'].str.split(' ')
data = data.reindex(data.index.repeat(temp.apply(len)))
data['new_Seatblocks'] = np.hstack(temp)

1

join 및 stack () 할 필요없이 groupby ()를 사용할 수도 있습니다.

위의 예제 데이터를 사용하십시오.

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

미리 감사드립니다. 두 개의 열을 동시에 분할하여 위의 코드를 사용하는 방법. 예 : 0 31316 Lennon, John 25 F01300 1 : 13 : 36 : 1,12 1 : 13 : 37 : 1,13 A, B. 결과는 0 31316 Lennon, John 25 F01 300 1:13:36:1,12 A다음과 같아야합니다.0 31316 Lennon, John 25 F01 300 1:13:37:1,13 B
Krithi.S

@ Krithi.S, 나는 질문을 이해하려고 노력합니다. 분할 후 두 열의 멤버 수가 동일해야합니까? 0 31316 Lennon, John 25 F01300 1 : 13 : 36 : 1,12 1 : 13 : 37 : 1,13 A, B, C에 대한 예상 결과는 무엇입니까?
Ben2018

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.