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)
답변
이런 식으로 어떻습니까 :
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
그런 다음 열의 이름을 바꾸면됩니다.
답변
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
답변
허용 된 답변보다 빠른 것을 찾기 위해 고통스러운 실험을 한 후에 나는 이것을 작동 시켰습니다. 내가 시도한 데이터 세트에서 약 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
답변
이 일반적인 작업을 위해 작성한 함수는 다음과 같습니다 . 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=',')
답변
팬더> = 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
이것은ravel
repeat
빈 목록을 완전히 무시하고 NaN을 질식시키는 + 기반 솔루션 보다 심각한 이점 입니다.
답변
비슷한 질문 : 팬더 : 열의 텍스트를 여러 행으로 나누려면 어떻게합니까?
당신은 할 수 있습니다 :
>> a=pd.DataFrame({"var1":"a,b,c d,e,f".split(),"var2":[1,2]})
>> s = a.var1.str.split(",").apply(pd.Series, 1).stack()
>> s.index = s.index.droplevel(-1)
>> del a['var1']
>> a.join(s)
var2 var1
0 1 a
0 1 b
0 1 c
1 2 d
1 2 e
1 2 f
답변
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
분할 할 경우 결과 목록의 길이가 구분 기호 수보다 하나 이상이라고 가정 하여 인수 발생 횟수를 계산합니다 .
나는 그를 사용 sep
에 join
다음 문자열 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)})
