두 팬더 열의 문자열 연결


84

다음이 있습니다 DataFrame.

from pandas import *
df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})

다음과 같이 보입니다.

    bar foo
0    1   a
1    2   b
2    3   c

이제 다음과 같은 것을 갖고 싶습니다.

     bar
0    1 is a
1    2 is b
2    3 is c

이것을 어떻게 달성 할 수 있습니까? 다음을 시도했습니다.

df['foo'] = '%s is %s' % (df['bar'], df['foo'])

그러나 그것은 나에게 잘못된 결과를 제공합니다.

>>>print df.ix[0]

bar                                                    a
foo    0    a
1    b
2    c
Name: bar is 0    1
1    2
2
Name: 0

멍청한 질문에 대해 미안하지만이 팬더 : DataFrame에서 두 개의 열을 결합하는 것은 나에게 도움이되지 않았습니다.

답변:



65

이 질문은 이미 답변되었지만 이전에 논의되지 않은 유용한 방법을 혼합하여 지금까지 제안한 모든 방법을 성능 측면에서 비교하는 것이 좋을 것이라고 생각합니다.

성능 순서를 높이기 위해이 문제에 대한 몇 가지 유용한 솔루션이 있습니다.


DataFrame.agg

이것은 간단한 str.format기반 접근 방식입니다.

df['baz'] = df.agg('{0[bar]} is {0[foo]}'.format, axis=1)
df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

여기에서 f- 문자열 형식을 사용할 수도 있습니다.

df['baz'] = df.agg(lambda x: f"{x['bar']} is {x['foo']}", axis=1)
df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

char.array기반 연결

열을로 연결하도록 변환 chararrays한 다음 함께 추가합니다.

a = np.char.array(df['bar'].values)
b = np.char.array(df['foo'].values)

df['baz'] = (a + b' is ' + b).astype(str)
df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

목록 이해zip

팬더에서 목록 이해력이 얼마나 과소 평가되었는지 과소 평가할 수 없습니다.

df['baz'] = [str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])]

또는을 사용 str.join하여 연결합니다 (확장도 향상됨).

df['baz'] = [
    ' '.join([str(x), 'is', y]) for x, y in zip(df['bar'], df['foo'])]

df
  foo  bar     baz
0   a    1  1 is a
1   b    2  2 is b
2   c    3  3 is c

문자열 연산은 본질적으로 벡터화하기 어렵고 대부분의 pandas "벡터화 된"함수는 기본적으로 루프를 둘러싼 래퍼이기 때문에 목록 이해는 문자열 조작에 탁월합니다. 판다를 사용한 For 루프 에서이 주제에 대해 광범위하게 작성했습니다. 언제 관심을 가져야합니까? . 일반적으로 인덱스 정렬에 대해 걱정할 필요가 없으면 문자열 및 정규식 작업을 처리 할 때 목록 이해를 사용하십시오.

위의 목록 구성은 기본적으로 NaN을 처리하지 않습니다. 그러나 처리해야하는 경우 항상 try-except를 래핑하는 함수를 작성할 수 있습니다.

def try_concat(x, y):
    try:
        return str(x) + ' is ' + y
    except (ValueError, TypeError):
        return np.nan


df['baz'] = [try_concat(x, y) for x, y in zip(df['bar'], df['foo'])]

perfplot 성능 측정

여기에 이미지 설명 입력

perfplot을 사용하여 생성 된 그래프 . 다음은 전체 코드 목록 입니다.

기능

def brenbarn(df):
    return df.assign(baz=df.bar.map(str) + " is " + df.foo)

def danielvelkov(df):
    return df.assign(baz=df.apply(
        lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1))

def chrimuelle(df):
    return df.assign(
        baz=df['bar'].astype(str).str.cat(df['foo'].values, sep=' is '))

def vladimiryashin(df):
    return df.assign(baz=df.astype(str).apply(lambda x: ' is '.join(x), axis=1))

def erickfis(df):
    return df.assign(
        baz=df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1))

def cs1_format(df):
    return df.assign(baz=df.agg('{0[bar]} is {0[foo]}'.format, axis=1))

def cs1_fstrings(df):
    return df.assign(baz=df.agg(lambda x: f"{x['bar']} is {x['foo']}", axis=1))

def cs2(df):
    a = np.char.array(df['bar'].values)
    b = np.char.array(df['foo'].values)

    return df.assign(baz=(a + b' is ' + b).astype(str))

def cs3(df):
    return df.assign(
        baz=[str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])])

4
그게 내가 팬더의 문자열 연결에 대해 항상 알고 싶었던 전부 였지만 너무 두려워서 물어보십시오!
IanS

플롯을 다음 레벨 10 4 (또는 그 이상)로 업데이트 해 주시겠습니까? 현재 플롯이 10 3 (오늘의 조건에 대해 매우 작은 1000)으로 제한되는 빠른 시각적 대답 은 cs3이 최고라는 것입니다. brenbarn은 cs3보다 덜 기하 급수적으로 보이므로 큰 데이터 세트의 경우 brenbarn이 가장 좋은 (빠른) 대답 일 것입니다.
Velizar VESSELINOV

1
@VelizarVESSELINOV 업데이트되었습니다! 놀랍게도 numpy 연결이 목록 comp와 pandas 연결보다 느리다는 것입니다.
cs95 19

1
당신은 사용하여 생각 해 봤나 df['bar'].tolist()df['foo'].tolist()cs3()? 내 생각에는 "기본"시간이 약간 증가하지만 더 잘 확장 될 것입니다.
shadowtalker

44

코드의 문제는 모든 행에 작업을 적용하려는 것입니다. 하지만 작성한 방식은 전체 'bar'및 'foo'열을 가져 와서 문자열로 변환하고 하나의 큰 문자열을 반환합니다. 다음과 같이 작성할 수 있습니다.

df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)

다른 답변보다 길지만 더 일반적입니다 (문자열이 아닌 값과 함께 사용할 수 있음).


13

당신은 또한 사용할 수 있습니다

df['bar'] = df['bar'].str.cat(df['foo'].values.astype(str), sep=' is ')

1
df [ 'bar']는 문자열 열이 아니기 때문에 작동하지 않습니다. 올바른 할당은 df['bar'] = df['bar'].astype(str).str.cat(df['foo'], sep=' is ')입니다.
cbrnr

8
df.astype(str).apply(lambda x: ' is '.join(x), axis=1)

0    1 is a
1    2 is b
2    3 is c
dtype: object

이 답변은 결정되지 않은 열 수 (> 1) 및 결정되지 않은 열 이름에서도 작동하므로 나머지보다 더 유용합니다.
johnDanger

4

@DanielVelkov 대답은 적절한 대답이지만 문자열 리터럴을 사용하는 것이 더 빠릅니다.

# Daniel's
%timeit df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)
## 963 µs ± 157 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# String literals - python 3
%timeit df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1)
## 849 µs ± 4.28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

4

series.str.cat 이 문제에 접근하는 가장 유연한 방법입니다.

에 대한 df = pd.DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})

df.foo.str.cat(df.bar.astype(str), sep=' is ')

>>>  0    a is 1
     1    b is 2
     2    c is 3
     Name: foo, dtype: object

또는

df.bar.astype(str).str.cat(df.foo, sep=' is ')

>>>  0    1 is a
     1    2 is b
     2    3 is c
     Name: bar, dtype: object

가장 중요한 것은 (와 달리 .join()) Null값 을 무시하거나 na_rep매개 변수로 바꿀 수 있다는 것 입니다.


이 기능이 왜 포장되지 않았는지 .join()혼란
스럽
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.