팬더 데이터 프레임 문자열 항목을 분할하여 행 분리


200

I가 가지고 pandas dataframe있는 텍스트 스트링의 하나의 열은 쉼표로 구분 된 값을 포함한다. 각 CSV 필드를 분할하고 항목 당 새 행을 작성하려고합니다 (CSV가 깨끗하고 ','에서만 분할해야한다고 가정). 예를 들어 다음과 a같아야합니다 b.

In [7]: a
Out[7]: 
    var1  var2
0  a,b,c     1
1  d,e,f     2

In [8]: b
Out[8]: 
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

지금까지 다양한 간단한 함수를 시도했지만 .apply메서드가 축에서 사용될 때 하나의 행만 반환 값으로 허용하는 것처럼 보이며 얻을 수 없습니다..transform 작동 . 어떤 제안이라도 대단히 감사하겠습니다!

데이터 예 :

from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
               {'var1': 'b', 'var2': 1},
               {'var1': 'c', 'var2': 1},
               {'var1': 'd', 'var2': 2},
               {'var1': 'e', 'var2': 2},
               {'var1': 'f', 'var2': 2}])

우리는 numpy를 거쳐 DataFrame 메타 데이터를 잃기 때문에 이것이 작동하지 않는다는 것을 알고 있지만, 내가 시도한 것에 대한 감각을 제공해야합니다.

def fun(row):
    letters = row['var1']
    letters = letters.split(',')
    out = np.array([row] * len(letters))
    out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)

2
이 페이지의 다른 솔루션이 작동하지만 짧고 효과적인 방법을 찾았습니다. stackoverflow.com/questions/27263805/…
desaiankitb

1
이 페이지에 도착하여 여러 열을 유지하는 솔루션을 찾는 다른 사람들을 위해 다음 질문을 살펴보십시오. stackoverflow.com/questions/17116814/…
Sos

답변:


81

이런 식으로 어떻습니까 :

In [55]: pd.concat([Series(row['var2'], row['var1'].split(','))              
                    for _, row in a.iterrows()]).reset_index()
Out[55]: 
  index  0
0     a  1
1     b  1
2     c  1
3     d  2
4     e  2
5     f  2

그런 다음 열의 이름을 바꾸면됩니다.


1
이것이 작동하는 것처럼 보입니다. 당신의 도움을 주셔서 감사합니다! 그러나 일반적으로 Apply가 임의 크기의 데이터 프레임을 반환하지만 모든 청크에 대해 일관된 Split-Apply-Combine에 대한 선호되는 접근 방법이 있습니까?
Vincent

GroupBy.apply가 작동해야합니다 (방금 마스터에 대해 시도했습니다). 그러나이 경우 행별로 데이터를 생성하기 때문에 추가 그룹화 단계를 거치지 않아도됩니까?
창 쉐

1
얘들 아. 너무 늦게 뛰어 들어 죄송하지만 이에 대한 더 나은 해결책이 없는지 궁금합니다. 나는 이것이 티켓처럼 보이기 때문에 처음으로 iterrows를 실험하려고합니다. 또한 제안 된 솔루션에 혼란스러워합니다. "_"는 무엇을 나타 냅니까? 솔루션의 작동 방식을 설명 할 수 있습니까?
horatio1701d

11
솔루션을 두 개 이상의 열로 확장 할 수 있습니까?
horatio1701d

1
이 벡터화 된 접근법을 확인하십시오 ...
MaxU

146

UPDATE2 : 더 일반적인 벡터화 함수로 normal여러 list열과 여러 열에서 작동합니다.

def explode(df, lst_cols, fill_value='', preserve_index=False):
    # make sure `lst_cols` is list-alike
    if (lst_cols is not None
        and len(lst_cols) > 0
        and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
        lst_cols = [lst_cols]
    # all columns except `lst_cols`
    idx_cols = df.columns.difference(lst_cols)
    # calculate lengths of lists
    lens = df[lst_cols[0]].str.len()
    # preserve original index values    
    idx = np.repeat(df.index.values, lens)
    # create "exploded" DF
    res = (pd.DataFrame({
                col:np.repeat(df[col].values, lens)
                for col in idx_cols},
                index=idx)
             .assign(**{col:np.concatenate(df.loc[lens>0, col].values)
                            for col in lst_cols}))
    # append those rows that have empty lists
    if (lens == 0).any():
        # at least one list in cells is empty
        res = (res.append(df.loc[lens==0, idx_cols], sort=False)
                  .fillna(fill_value))
    # revert the original index order
    res = res.sort_index()
    # reset index if requested
    if not preserve_index:        
        res = res.reset_index(drop=True)
    return res

데모:

배수 list 열-모든 list열은 각 행에서 동일한 요소 수를 가져야합니다.

In [134]: df
Out[134]:
   aaa  myid        num          text
0   10     1  [1, 2, 3]  [aa, bb, cc]
1   11     2         []            []
2   12     3     [1, 2]      [cc, dd]
3   13     4         []            []

In [135]: explode(df, ['num','text'], fill_value='')
Out[135]:
   aaa  myid num text
0   10     1   1   aa
1   10     1   2   bb
2   10     1   3   cc
3   11     2
4   12     3   1   cc
5   12     3   2   dd
6   13     4

원래 색인 값 유지 :

In [136]: explode(df, ['num','text'], fill_value='', preserve_index=True)
Out[136]:
   aaa  myid num text
0   10     1   1   aa
0   10     1   2   bb
0   10     1   3   cc
1   11     2
2   12     3   1   cc
2   12     3   2   dd
3   13     4

설정:

df = pd.DataFrame({
 'aaa': {0: 10, 1: 11, 2: 12, 3: 13},
 'myid': {0: 1, 1: 2, 2: 3, 3: 4},
 'num': {0: [1, 2, 3], 1: [], 2: [1, 2], 3: []},
 'text': {0: ['aa', 'bb', 'cc'], 1: [], 2: ['cc', 'dd'], 3: []}
})

CSV 열 :

In [46]: df
Out[46]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

In [47]: explode(df.assign(var1=df.var1.str.split(',')), 'var1')
Out[47]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

이 작은 트릭을 사용하여 CSV와 같은 열을 list열로 변환 할 수 있습니다 .

In [48]: df.assign(var1=df.var1.str.split(','))
Out[48]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

업데이트 : 일반 벡터화 접근법 (여러 열에도 적용됨) :

원본 DF :

In [177]: df
Out[177]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

해결책:

먼저 CSV 문자열을 목록으로 변환 해 보겠습니다.

In [178]: lst_col = 'var1' 

In [179]: x = df.assign(**{lst_col:df[lst_col].str.split(',')})

In [180]: x
Out[180]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

이제 우리는 이것을 할 수 있습니다 :

In [181]: pd.DataFrame({
     ...:     col:np.repeat(x[col].values, x[lst_col].str.len())
     ...:     for col in x.columns.difference([lst_col])
     ...: }).assign(**{lst_col:np.concatenate(x[lst_col].values)})[x.columns.tolist()]
     ...:
Out[181]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

이전 답변 :

@ AFinkelstein 솔루션 에서 영감을 얻어 두 개 이상의 열을 사용하여 DF에 적용 할 수 있고 AFinkelstein의 솔루션만큼 빠르고 거의 DF에 적용 할 수 있도록 좀 더 일반화하고 싶었습니다.

In [2]: df = pd.DataFrame(
   ...:    [{'var1': 'a,b,c', 'var2': 1, 'var3': 'XX'},
   ...:     {'var1': 'd,e,f,x,y', 'var2': 2, 'var3': 'ZZ'}]
   ...: )

In [3]: df
Out[3]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

In [4]: (df.set_index(df.columns.drop('var1',1).tolist())
   ...:    .var1.str.split(',', expand=True)
   ...:    .stack()
   ...:    .reset_index()
   ...:    .rename(columns={0:'var1'})
   ...:    .loc[:, df.columns]
   ...: )
Out[4]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

7
친구, Git pandas에서 토론을 열 수 있다면, 우리는 이와 같은 기능의 빌드가 필요하다고 생각합니다! 팬더를 위해 SO에서 unlistify와 unnesting에 대한 많은 질문을 보았습니다
YOBEN_S

여러 열에 이것을 사용하는 방법. 쉼표로 2 열의 데이터를 분리하고 순서대로 수행하려는 것처럼?
Jaskaran Singh Puri

@JaskaranSinghPuri, 모든 CSV 열을 목록으로 먼저 변환하려고합니다.
MaxU

1
불행히도 목록 요소가 튜플 인 경우 작동하지 않습니다. 그러나 전체 튜플을 문자열로 변환 한 후에는 매력처럼 작동합니다!
귀도

2
팬더 신이 WenBen의 탄원을 듣고 .explode()API에 메소드를 설치했습니다 ( 이 답변 도 참조하십시오 ).
cs95

117

허용 된 답변보다 빠른 것을 찾기 위해 고통스러운 실험을 한 후에 나는 이것을 작동 시켰습니다. 내가 시도한 데이터 세트에서 약 100 배 빠르게 실행되었습니다.

누군가가 이것을 더 우아하게 만드는 방법을 알고 있다면 반드시 내 코드를 수정하십시오. 인덱스로 유지하려는 다른 열을 설정 한 다음 인덱스를 재설정하고 열의 이름을 바꾸지 않고 작동하는 방법을 찾을 수 없었지만 작동하는 다른 것이 있다고 생각합니다.

b = DataFrame(a.var1.str.split(',').tolist(), index=a.var2).stack()
b = b.reset_index()[[0, 'var2']] # var1 variable is currently labeled 0
b.columns = ['var1', 'var2'] # renaming var1

2
이 솔루션은 상당히 빠르게 작동했으며 메모리를 적게 사용하는 것으로 보입니다.
cyril

1
이것은 좋은 벡터화 팬더 솔루션입니다. 감사!
Dennis Golomazov

나는 내 자신의 데이터 세트에이를 때, 나는 점점 계속 TypeError: object of type 'float' has no len()맨 처음 단계에서 ( DataFrame(df.var1.str.split(',').tolist()))
user5359531

@ user5359531 데이터 셋에 아마 NaN해당 열이있을 것이므로 교체는b = DataFrame(a.var1.str.split(',').values.tolist(), index=a.var2).stack()
Flair

그냥 참고하시기 바랍니다 여기에 예를 들어이 솔루션의 최대 좋은 쓰기는.
hhbilly

46

이 일반적인 작업을 위해 작성한 함수는 다음과 같습니다 . Series/ stack메소드 보다 효율적 입니다. 열 순서와 이름이 유지됩니다.

def tidy_split(df, column, sep='|', keep=False):
    """
    Split the values of a column and expand so the new DataFrame has one split
    value per row. Filters rows where the column is missing.

    Params
    ------
    df : pandas.DataFrame
        dataframe with the column to split and expand
    column : str
        the column to split and expand
    sep : str
        the string used to split the column's values
    keep : bool
        whether to retain the presplit value as it's own row

    Returns
    -------
    pandas.DataFrame
        Returns a dataframe with the same columns as `df`.
    """
    indexes = list()
    new_values = list()
    df = df.dropna(subset=[column])
    for i, presplit in enumerate(df[column].astype(str)):
        values = presplit.split(sep)
        if keep and len(values) > 1:
            indexes.append(i)
            new_values.append(presplit)
        for value in values:
            indexes.append(i)
            new_values.append(value)
    new_df = df.iloc[indexes, :].copy()
    new_df[column] = new_values
    return new_df

이 기능을 사용하면 원래 질문 은 다음과 같이 간단합니다.

tidy_split(a, 'var1', sep=',')

1
이것은 엄청나게 빠릅니다! 고마워.
Anurag N. Sharma

42

팬더> = 0.25

시리즈 및 DataFrame 방법은 정의 .explode()방법을 폭발의 그 목록을 별도의 행에. 목록과 같은 열 분해 에 대한 문서 섹션을 참조하십시오. .

쉼표로 구분 된 문자열 목록이 있으므로 문자열을 쉼표로 분할하여 요소 목록을 가져온 다음 explode해당 열 을 호출하십시오 .

df = pd.DataFrame({'var1': ['a,b,c', 'd,e,f'], 'var2': [1, 2]})
df
    var1  var2
0  a,b,c     1
1  d,e,f     2

df.assign(var1=df['var1'].str.split(',')).explode('var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

참고 explode단일 컬럼에서 작동 (지금은).


NaN과 빈 목록은 후프를 뛰어 넘지 않고도 당당한 치료를받을 수 있습니다.

df = pd.DataFrame({'var1': ['d,e,f', '', np.nan], 'var2': [1, 2, 3]})
df
    var1  var2
0  d,e,f     1
1            2
2    NaN     3

df['var1'].str.split(',')

0    [d, e, f]
1           []
2          NaN

df.assign(var1=df['var1'].str.split(',')).explode('var1')

  var1  var2
0    d     1
0    e     1
0    f     1
1          2  # empty list entry becomes empty string after exploding 
2  NaN     3  # NaN left un-touched

이것은ravelrepeat 빈 목록을 완전히 무시하고 NaN을 질식시키는 + 기반 솔루션 보다 심각한 이점 입니다.


4
이것은 가장 쉽고 내 경우에 가장 적합합니다! 감사!
아이작 심


14

TL; DR

import pandas as pd
import numpy as np

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

데모

explode_str(a, 'var1', ',')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

d리스트가 있는 새로운 데이터 프레임 을 만들어 봅시다

d = a.assign(var1=lambda d: d.var1.str.split(','))

explode_list(d, 'var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

일반적인 답변

np.arange와 함께 사용할 repeat수있는 데이터 프레임 인덱스 위치를 생성하는 데 사용할 것입니다 iloc.

자주하는 질문

왜 사용하지 loc않습니까?

인덱스가 고유하지 않을 수 있으므로 사용 loc하면 쿼리 된 인덱스와 일치하는 모든 행이 반환됩니다.

values속성 을 사용하지 않고 슬라이스하지 않습니까?

를 호출 할 때 values데이터 프레임 전체가 하나의 응집성 "블록"에있는 경우 Pandas는 "블록"인 배열의보기를 반환합니다. 그렇지 않으면 팬더는 새로운 배열을 함께 모아야합니다. 결합 할 때 해당 배열은 균일 한 dtype이어야합니다. 종종 dtype 인 배열을 반환하는 것을 의미 object합니다. iloc슬라이싱 대신 사용 하여values속성 하면 처리 할 필요가 없습니다.

왜 사용 assign합니까?

내가 사용하는 경우 assign나 폭발 해요 것과 같은 열 이름을 사용하여, 나는 기존의 열을 덮어하고 dataframe에서의 위치를 유지합니다.

인덱스 값이 반복되는 이유는 무엇입니까?

iloc반복 된 위치에서 사용함으로써 결과 인덱스는 동일한 반복 패턴을 보여줍니다. 각 요소에 대해 하나의 반복이 목록 또는 문자열입니다.
이것으로 재설정 할 수 있습니다reset_index(drop=True)


문자열

문자열을 너무 일찍 분할하고 싶지 않습니다. 대신 sep분할 할 경우 결과 목록의 길이가 구분 기호 수보다 하나 이상이라고 가정 하여 인수 발생 횟수를 계산합니다 .

나는 그를 사용 sepjoin다음 문자열 split.

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

목록

문자열 sep이 이미 분리 되어 있기 때문에 발생 횟수를 계산할 필요가 없다는 점을 제외하고는 문자열과 유사 합니다.

Numpy를 사용 concatenate하여 목록을 함께 잼합니다.

import pandas as pd
import numpy as np

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})


난이게 좋아. 정말 간결하고 성능도 정말 좋아야합니다. 한가지 질문은 : df.iloc [i]가 데이터 프레임의 행을 반복하는 것과 같거나 그보다 더 효율적인가? 감사!
Tim

7

데이터 프레임의 구조를 변경하지 않고 데이터 프레임을 분할하고 분해 할 수 있습니다.

특정 열의 데이터 분할 및 확장

입력:

    var1    var2
0   a,b,c   1
1   d,e,f   2



#Get the indexes which are repetative with the split 
temp = df['var1'].str.split(',')
df = df.reindex(df.index.repeat(temp.apply(len)))


df['var1'] = np.hstack(temp)

밖:

    var1    var2
0   a   1
0   b   1
0   c   1
1   d   2
1   e   2
1   f   2

편집 -1

여러 열에 대한 행 분할 및 확장

Filename    RGB                                             RGB_type
0   A   [[0, 1650, 6, 39], [0, 1691, 1, 59], [50, 1402...   [r, g, b]
1   B   [[0, 1423, 16, 38], [0, 1445, 16, 46], [0, 141...   [r, g, b]

참조 열을 기반으로 다시 인덱싱하고 열 값 정보를 스택과 정렬

df = df.reindex(df.index.repeat(df['RGB_type'].apply(len)))
df = df.groupby('Filename').apply(lambda x:x.apply(lambda y: pd.Series(y.iloc[0])))
df.reset_index(drop=True).ffill()

밖:

                Filename    RGB_type    Top 1 colour    Top 1 frequency Top 2 colour    Top 2 frequency
    Filename                            
 A  0       A   r   0   1650    6   39
    1       A   g   0   1691    1   59
    2       A   b   50  1402    49  187
 B  0       B   r   0   1423    16  38
    1       B   g   0   1445    16  46
    2       B   b   0   1419    16  39

5

임의의 수의 열이있는 데이터 프레임에 대한 솔루션을 생각해 냈습니다 (한 번에 한 열의 항목 만 분리).

def splitDataFrameList(df,target_column,separator):
    ''' df = dataframe to split,
    target_column = the column containing the values to split
    separator = the symbol used to perform the split

    returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
    The values in the other columns are duplicated across the newly divided rows.
    '''
    def splitListToRows(row,row_accumulator,target_column,separator):
        split_row = row[target_column].split(separator)
        for s in split_row:
            new_row = row.to_dict()
            new_row[target_column] = s
            row_accumulator.append(new_row)
    new_rows = []
    df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
    new_df = pandas.DataFrame(new_rows)
    return new_df

2
이 todict () 변환으로 인해 좋지만 슬프게 느리다 :(
MAQ

4

다음은 splitpandas str접근 자의 메소드 를 사용한 다음 NumPy를 사용하여 각 행을 단일 배열로 병합 하는 매우 간단한 메시지입니다 .

를 사용하여 비분 할 열을 올바른 횟수만큼 반복하여 해당 값을 검색합니다 np.repeat.

var1 = df.var1.str.split(',', expand=True).values.ravel()
var2 = np.repeat(df.var2.values, len(var1) / len(df))

pd.DataFrame({'var1': var1,
              'var2': var2})

  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

1
그것은 매우 아름다운 대답이 될 수 있습니다. 불행히도, 그것은 많은 열에 맞게 확장되지 않습니까?
Michael Dorner

3

나는 다양한 방법으로 내 목록을 분해하여 메모리 부족 경험으로 고심하고 있었으므로 어떤 투표에 대한 답을 결정할지에 대한 벤치 마크를 준비했습니다. 목록 길이와 목록 수의 비율이 다양한 다섯 가지 시나리오를 테스트했습니다. 아래 결과 공유 :

시간 : (낮을수록 좋습니다. 큰 버전을 보려면 클릭하십시오)

속도

최대 메모리 사용량 : (낮을수록 좋음)

최대 메모리 사용량

결론 :

  • @MaxU의 답변 (업데이트 2), 코드 이름 연결 는 거의 모든 경우에 최고 속도를 제공하면서도 픽 메모리 사용량을 낮게 유지합니다.
  • 상대적으로 작은 목록으로 많은 행을 처리해야하고 피크 메모리를 늘릴 수있는 경우 @ DMulligan 's answer (codename stack )를 참조하십시오 .
  • 허용되는 @Chang의 대답 은 몇 개의 행이 있지만 매우 큰 목록이있는 데이터 프레임에 적합합니다.

자세한 내용 (기능 및 벤치마킹 코드)은이 GitHub 요지에 있습니다. 벤치 마크 문제는 단순화되었으며 문자열을 목록으로 나누는 작업은 포함하지 않았습니다. 대부분의 솔루션은 비슷한 방식으로 수행되었습니다.


좋은 비교! 벤치 마크를 작성하는 데 사용한 코드를 게시 하시겠습니까?
MaxU

1
이 링크를 참조하십시오 : gist.github.com/krassowski/0259a2cd2ba774ccd9f69bbcc3187fbf (답변에 이미 포함되어 있음) -IMO 여기에 모두 붙여 넣기가 너무 길 것입니다.
krassowski

2

탁월한 @DMulligan의 솔루션을 기반으로 한 데이터 프레임의 열을 여러 행으로 나누고 원래 데이터 프레임으로 다시 병합하는 일반적인 벡터화 (루프 없음) 함수가 있습니다. 또한 change_column_order답변 에서 훌륭한 일반 함수를 사용합니다 .

def change_column_order(df, col_name, index):
    cols = df.columns.tolist()
    cols.remove(col_name)
    cols.insert(index, col_name)
    return df[cols]

def split_df(dataframe, col_name, sep):
    orig_col_index = dataframe.columns.tolist().index(col_name)
    orig_index_name = dataframe.index.name
    orig_columns = dataframe.columns
    dataframe = dataframe.reset_index()  # we need a natural 0-based index for proper merge
    index_col_name = (set(dataframe.columns) - set(orig_columns)).pop()
    df_split = pd.DataFrame(
        pd.DataFrame(dataframe[col_name].str.split(sep).tolist())
        .stack().reset_index(level=1, drop=1), columns=[col_name])
    df = dataframe.drop(col_name, axis=1)
    df = pd.merge(df, df_split, left_index=True, right_index=True, how='inner')
    df = df.set_index(index_col_name)
    df.index.name = orig_index_name
    # merge adds the column to the last place, so we need to move it back
    return change_column_order(df, col_name, orig_col_index)

예:

df = pd.DataFrame([['a:b', 1, 4], ['c:d', 2, 5], ['e:f:g:h', 3, 6]], 
                  columns=['Name', 'A', 'B'], index=[10, 12, 13])
df
        Name    A   B
    10   a:b     1   4
    12   c:d     2   5
    13   e:f:g:h 3   6

split_df(df, 'Name', ':')
    Name    A   B
10   a       1   4
10   b       1   4
12   c       2   5
12   d       2   5
13   e       3   6
13   f       3   6    
13   g       3   6    
13   h       3   6    

열의 원래 색인과 순서를 유지합니다. 또한 비 순차 인덱스가있는 데이터 프레임에서도 작동합니다.


2
이것은 나를 위해 이것을 깨뜨렸다, 좋은 일 : stackoverflow.com/a/48554655/6672746
Evan

2

문자열 함수 split은 옵션 boolean argument 'expand'를 사용할 수 있습니다.

이 인수를 사용하는 솔루션은 다음과 같습니다.

(a.var1
  .str.split(",",expand=True)
  .set_index(a.var2)
  .stack()
  .reset_index(level=1, drop=True)
  .reset_index()
  .rename(columns={0:"var1"}))

1

위에서 Jiln의 탁월한 대답을 사용했지만 여러 열을 나누려면 확장해야했습니다. 내가 나누겠다고 생각했다.

def splitDataFrameList(df,target_column,separator):
''' df = dataframe to split,
target_column = the column containing the values to split
separator = the symbol used to perform the split

returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
The values in the other columns are duplicated across the newly divided rows.
'''
def splitListToRows(row, row_accumulator, target_columns, separator):
    split_rows = []
    for target_column in target_columns:
        split_rows.append(row[target_column].split(separator))
    # Seperate for multiple columns
    for i in range(len(split_rows[0])):
        new_row = row.to_dict()
        for j in range(len(split_rows)):
            new_row[target_columns[j]] = split_rows[j][i]
        row_accumulator.append(new_row)
new_rows = []
df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
new_df = pd.DataFrame(new_rows)
return new_df

1

MultiIndex 지원으로 MaxU의 답변 업그레이드

def explode(df, lst_cols, fill_value='', preserve_index=False):
    """
    usage:
        In [134]: df
        Out[134]:
           aaa  myid        num          text
        0   10     1  [1, 2, 3]  [aa, bb, cc]
        1   11     2         []            []
        2   12     3     [1, 2]      [cc, dd]
        3   13     4         []            []

        In [135]: explode(df, ['num','text'], fill_value='')
        Out[135]:
           aaa  myid num text
        0   10     1   1   aa
        1   10     1   2   bb
        2   10     1   3   cc
        3   11     2
        4   12     3   1   cc
        5   12     3   2   dd
        6   13     4
    """
    # make sure `lst_cols` is list-alike
    if (lst_cols is not None
        and len(lst_cols) > 0
        and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
        lst_cols = [lst_cols]
    # all columns except `lst_cols`
    idx_cols = df.columns.difference(lst_cols)
    # calculate lengths of lists
    lens = df[lst_cols[0]].str.len()
    # preserve original index values    
    idx = np.repeat(df.index.values, lens)
    res = (pd.DataFrame({
                col:np.repeat(df[col].values, lens)
                for col in idx_cols},
                index=idx)
             .assign(**{col:np.concatenate(df.loc[lens>0, col].values)
                            for col in lst_cols}))
    # append those rows that have empty lists
    if (lens == 0).any():
        # at least one list in cells is empty
        res = (res.append(df.loc[lens==0, idx_cols], sort=False)
                  .fillna(fill_value))
    # revert the original index order
    res = res.sort_index()
    # reset index if requested
    if not preserve_index:        
        res = res.reset_index(drop=True)

    # if original index is MultiIndex build the dataframe from the multiindex
    # create "exploded" DF
    if isinstance(df.index, pd.MultiIndex):
        res = res.reindex(
            index=pd.MultiIndex.from_tuples(
                res.index,
                names=['number', 'color']
            )
    )
    return res

1

한 - 라이너를 사용 split(___, expand=True)하고 levelname인수에 reset_index():

>>> b = a.var1.str.split(',', expand=True).set_index(a.var2).stack().reset_index(level=0, name='var1')
>>> b
   var2 var1
0     1    a
1     1    b
2     1    c
0     2    d
1     2    e
2     2    f

당신이 필요로하는 경우 b정확하게 문제처럼 보이도록, 당신은 추가 할 수 있습니다 :

>>> b = b.reset_index(drop=True)[['var1', 'var2']]
>>> b
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

0

이 문제에 대한 다음 해결책을 생각해 냈습니다.

def iter_var1(d):
    for _, row in d.iterrows():
        for v in row["var1"].split(","):
            yield (v, row["var2"])

new_a = DataFrame.from_records([i for i in iter_var1(a)],
        columns=["var1", "var2"])

0

파이썬 복사 패키지를 사용하는 다른 솔루션

import copy
new_observations = list()
def pandas_explode(df, column_to_explode):
    new_observations = list()
    for row in df.to_dict(orient='records'):
        explode_values = row[column_to_explode]
        del row[column_to_explode]
        if type(explode_values) is list or type(explode_values) is tuple:
            for explode_value in explode_values:
                new_observation = copy.deepcopy(row)
                new_observation[column_to_explode] = explode_value
                new_observations.append(new_observation) 
        else:
            new_observation = copy.deepcopy(row)
            new_observation[column_to_explode] = explode_values
            new_observations.append(new_observation) 
    return_df = pd.DataFrame(new_observations)
    return return_df

df = pandas_explode(df, column_name)

0

여기에 많은 답변이 있지만 아무도 내장 팬더 분해 기능을 언급하지 않은 것에 놀랐습니다. 아래 링크를 확인하십시오. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html#pandas.DataFrame.explode

어떤 이유로 든 그 기능에 액세스 할 수 없으므로 아래 코드를 사용했습니다.

import pandas_explode
pandas_explode.patch()
df_zlp_people_cnt3 = df_zlp_people_cnt2.explode('people')

여기에 이미지 설명을 입력하십시오

위는 내 데이터 샘플입니다. 당신이 사람들을 볼 수 있듯이 칼럼에는 일련의 사람들이 있었고 나는 그것을 폭발하려고했습니다. 내가 준 코드는 목록 유형 데이터에 적용됩니다. 따라서 쉼표로 구분 된 텍스트 데이터를 목록 형식으로 가져 오십시오. 또한 내 코드는 내장 함수를 사용하기 때문에 사용자 정의 / 적용 함수보다 훨씬 빠릅니다.

참고 : pip와 함께 pandas_explode를 설치해야 할 수도 있습니다.


0

비슷한 문제가 발생했습니다. 솔루션이 먼저 데이터 프레임을 사전 목록으로 변환 한 다음 전환을 수행했습니다. 기능은 다음과 같습니다.

import copy
import re

def separate_row(df, column_name):
    ls = []
    for row_dict in df.to_dict('records'):
        for word in re.split(',', row_dict[column_name]):
            row = copy.deepcopy(row_dict)
            row[column_name]=word
            ls(row)
    return pd.DataFrame(ls)

예:

>>> from pandas import DataFrame
>>> import numpy as np
>>> a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
>>> a
    var1  var2
0  a,b,c     1
1  d,e,f     2
>>> separate_row(a, "var1")
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

목록 유형 행 분리를 지원하기 위해 함수를 약간 변경할 수도 있습니다.

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