Nối chuỗi của hai cột gấu trúc


84

Tôi có một số thứ sau DataFrame:

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

Nó trông như thế này:

    bar foo
0    1   a
1    2   b
2    3   c

Bây giờ tôi muốn có một cái gì đó như:

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

Làm thế nào tôi có thể đạt được điều này? Tôi đã thử những cách sau:

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

nhưng nó cho tôi một kết quả sai:

>>>print df.ix[0]

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

Xin lỗi vì một câu hỏi ngớ ngẩn, nhưng con gấu trúc này: kết hợp hai cột trong DataFrame không hữu ích cho tôi.

Câu trả lời:



65

Câu hỏi này đã được trả lời, nhưng tôi tin rằng sẽ rất tốt nếu đưa một số phương pháp hữu ích chưa được thảo luận trước đây vào hỗn hợp và so sánh tất cả các phương pháp được đề xuất cho đến nay về hiệu suất.

Dưới đây là một số giải pháp hữu ích cho vấn đề này, theo thứ tự hiệu suất ngày càng tăng.


DataFrame.agg

Đây là một str.formatcách tiếp cận dựa trên cơ sở đơn giản .

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

Bạn cũng có thể sử dụng định dạng chuỗi f tại đây:

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

Chuyển đổi các cột để nối thành chararrays, sau đó thêm chúng lại với nhau.

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

Liệt kê hiểu vớizip

Tôi không thể nói quá mức độ hiểu của danh sách được đánh giá thấp ở gấu trúc.

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

Ngoài ra, sử dụng str.joinđể nối (cũng sẽ chia tỷ lệ tốt hơn):

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

Khả năng hiểu danh sách vượt trội trong thao tác chuỗi, bởi vì các hoạt động chuỗi vốn đã khó được vectơ hóa và hầu hết các hàm "vectorised" của gấu trúc về cơ bản là trình bao bọc xung quanh các vòng lặp. Tôi đã viết nhiều về chủ đề này trong vòng lặp Đối với gấu trúc - Khi nào tôi nên quan tâm? . Nói chung, nếu bạn không phải lo lắng về việc căn chỉnh chỉ mục, hãy sử dụng khả năng hiểu danh sách khi xử lý các phép toán chuỗi và regex.

Danh sách bên trên theo mặc định không xử lý NaN. Tuy nhiên, bạn luôn có thể viết một hàm bao bọc một lần thử ngoại trừ nếu bạn cần xử lý nó.

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 Đo lường hiệu suất

nhập mô tả hình ảnh ở đây

Biểu đồ được tạo bằng perfplot . Đây là danh sách mã hoàn chỉnh .

Chức năng

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
Đó là tất cả những gì tôi luôn muốn biết về cách nối chuỗi ở gấu trúc, nhưng sợ quá nên hỏi!
IanS

Bạn có thể vui lòng cập nhật cốt truyện lên cấp độ tiếp theo 10 4 (hoặc thậm chí cao hơn), một câu trả lời trực quan nhanh chóng với cốt truyện hiện tại được giới hạn ở 10 3 (1000, rất nhỏ đối với điều kiện ngày nay) là cs3 là tốt nhất, cuối cùng khi bạn thấy brenbarn trông ít theo cấp số nhân hơn cs3, vì vậy hầu hết có lẽ đối với tập dữ liệu lớn, brenbarn là câu trả lời tốt nhất (nhanh hơn).
Velizar VESSELINOV

1
@VelizarVESSELINOV Đã cập nhật! Điều làm tôi ngạc nhiên là ghép nối numpy chậm hơn cả ghép nối danh sách và ghép gấu trúc.
cs95

1
Bạn đã xem xét sử dụng df['bar'].tolist()df['foo'].tolist()trong cs3()? Tôi đoán rằng nó sẽ làm tăng thời gian "cơ sở" một chút nhưng nó sẽ mở rộng quy mô tốt hơn.
shadowtalker

44

Vấn đề trong mã của bạn là bạn muốn áp dụng thao tác trên mọi hàng. Mặc dù vậy, cách bạn đã viết nó lấy toàn bộ cột 'bar' và 'foo', chuyển đổi chúng thành chuỗi và trả lại cho bạn một chuỗi lớn. Bạn có thể viết nó như sau:

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

Nó dài hơn câu trả lời khác nhưng chung chung hơn (có thể được sử dụng với các giá trị không phải là chuỗi).


13

Bạn cũng có thể sử dụng

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

1
Điều này không hoạt động vì df ['bar'] không phải là một cột chuỗi. Bài tập đúng là 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

Câu trả lời này cũng hoạt động với số cột không xác định (> 1) & tên cột không xác định, làm cho nó hữu ích hơn phần còn lại.
johnDanger

4

@DanielVelkov câu trả lời là câu trả lời thích hợp NHƯNG sử dụng chuỗi ký tự nhanh hơn:

# 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 là cách linh hoạt nhất để tiếp cận vấn đề này:

Đối với 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

HOẶC LÀ

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

Quan trọng nhất (và không giống như .join()), điều này cho phép bạn bỏ qua hoặc thay thế Nullcác giá trị bằng na_reptham số.


tại sao chức năng này không được gói gọn .join()khiến tôi bối rối
johnDanger
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.