Thay đổi một giá trị dựa trên một giá trị khác ở gấu trúc


107

Tôi đang cố gắng lập trình lại mã Stata của mình thành Python để cải thiện tốc độ và tôi đã được chỉ dẫn theo hướng của PANDAS. Tuy nhiên, tôi gặp khó khăn trong việc làm thế nào để xử lý dữ liệu.

Giả sử tôi muốn lặp lại tất cả các giá trị trong đầu cột 'ID'. Nếu ID đó khớp với một số cụ thể, thì tôi muốn thay đổi hai giá trị tương ứng FirstName và LastName.

Trong Stata, nó trông như thế này:

replace FirstName = "Matt" if ID==103
replace LastName =  "Jones" if ID==103

Vì vậy, điều này thay thế tất cả các giá trị trong FirstName tương ứng với các giá trị của ID == 103 đến Matt.

Trong PANDAS, tôi đang thử một thứ như thế này

df = read_csv("test.csv")
for i in df['ID']:
    if i ==103:
          ...

Không chắc chắn nơi để đi từ đây. Bất kỳ ý tưởng?

Câu trả lời:


180

Một tùy chọn là sử dụng các tính năng phân loại và lập chỉ mục của Python để đánh giá một cách hợp lý những nơi mà điều kiện của bạn lưu giữ và ghi đè dữ liệu ở đó.

Giả sử bạn có thể tải dữ liệu của bạn trực tiếp vào pandasvới pandas.read_csvsau đó mã sau đây có thể hữu ích cho bạn.

import pandas
df = pandas.read_csv("test.csv")
df.loc[df.ID == 103, 'FirstName'] = "Matt"
df.loc[df.ID == 103, 'LastName'] = "Jones"

Như đã đề cập trong các nhận xét, bạn cũng có thể thực hiện việc gán cho cả hai cột trong một lần chụp:

df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'

Lưu ý rằng bạn sẽ cần pandasphiên bản 0.11 hoặc mới hơn để sử dụng loccho các hoạt động gán ghi đè.


Một cách khác để làm điều đó là sử dụng cái được gọi là phân công theo chuỗi. Hoạt động của điều này kém ổn định hơn và do đó nó không được coi là giải pháp tốt nhất (nó rõ ràng không được khuyến khích trong tài liệu), nhưng sẽ hữu ích nếu biết về:

import pandas
df = pandas.read_csv("test.csv")
df['FirstName'][df.ID == 103] = "Matt"
df['LastName'][df.ID == 103] = "Jones"

16
làm thế nào về việc thêm cũng như hương vị này:df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'
Boud

2
-1 "Một cách khác để làm điều đó là sử dụng cái được gọi là phép gán chuỗi." Rõ ràng là không. Nó chỉ hữu ích khi biết rằng nhiệm vụ xích là không đáng tin cậy. Không phải đó là một giải pháp đáng tin cậy, không phải là tối ưu, tình hình còn tệ hơn nhiều . Bạn thậm chí đã thừa nhận điều này ở nơi khác trên Stack Overflow . Hãy cố gắng tránh tạo ra ảo tưởng rằng bài tập theo chuỗi là một lựa chọn khả thi. Hai phương pháp đầu tiên bạn đưa ra là đủ và là cách ưu tiên để làm điều này.
Phillip Cloud

9
Tôi không đồng ý. Tôi không hiểu tại sao bạn vẫn cố chấp cố gắng khẳng định rằng việc phân công theo chuỗi không phải là một cách khả thi. Tôi thừa nhận rằng nó không được coi là cách ưa thích. Nhiều hơn những gì bạn muốn. Thật phi lý khi hành động như vậy không phải là một cách để làm điều đó. Trên thực tế, trong hệ thống của tôi ngay bây giờ (phiên bản 0.8), đó là cách đúng đắn để làm điều đó. Tôi không quan tâm đến số phiếu ủng hộ của bạn nếu bạn định đảm nhận vị trí này. Hãy thoải mái thể hiện quan điểm của bạn bằng một phản đối, nhưng tôi đã phản ánh quan điểm của bạn và không đồng ý với nó.
ely

11
Internet là công việc nghiêm túc. Ở bất kỳ mức độ nào, EMS, tôi đánh giá cao khi biết tùy chọn tồn tại.
Parseltongue

Một vấn đề mà bạn có thể gặp phải là csv có dấu chấm / chấm trong tên cột và các bài tập bị lộn xộn. Bạn có thể sửa các cột bằng cách sử dụng một cái gì đó như sau: cols = df.columns cols = cols.map (lambda x: x.replace ('.', '_') If isinstance (x, str) else x) df.columns = cols
ski_squaw

37

Bạn có thể sử dụng map, nó có thể ánh xạ các giá trị từ một chính tả hoặc thậm chí một chức năng tùy chỉnh.

Giả sử đây là df của bạn:

    ID First_Name Last_Name
0  103          a         b
1  104          c         d

Tạo các phái:

fnames = {103: "Matt", 104: "Mr"}
lnames = {103: "Jones", 104: "X"}

Và bản đồ:

df['First_Name'] = df['ID'].map(fnames)
df['Last_Name'] = df['ID'].map(lnames)

Kết quả sẽ là:

    ID First_Name Last_Name
0  103       Matt     Jones
1  104         Mr         X

Hoặc sử dụng một chức năng tùy chỉnh:

names = {103: ("Matt", "Jones"), 104: ("Mr", "X")}
df['First_Name'] = df['ID'].map(lambda x: names[x][0])

2
Điều này sẽ không tạo ra KeyError nếu các giá trị không tồn tại trong chính tả của bạn?
EdChum

1
Chức năng tùy chỉnh sẽ hoạt động, những chức năng khác sẽ hoạt động. Nhưng tôi giả định rằng dictnó được tạo ra để ánh xạ. Nếu một số kiểm tra / giặt có thể được thực hiện dựa trên một cái gì đó như:df.ID.isin(names.keys())
Rutger Kassies

Chức năng tùy chỉnh có thể được mở rộng thành bất kỳ chức năng nào (không ẩn danh).
user989762

14

Câu hỏi ban đầu đề cập đến một trường hợp sử dụng hẹp cụ thể. Đối với những người cần câu trả lời chung chung, đây là một số ví dụ:

Tạo một cột mới bằng cách sử dụng dữ liệu từ các cột khác

Với khung dữ liệu bên dưới:

import pandas as pd
import numpy as np

df = pd.DataFrame([['dog', 'hound', 5],
                   ['cat', 'ragdoll', 1]],
                  columns=['animal', 'type', 'age'])

In[1]:
Out[1]:
  animal     type  age
----------------------
0    dog    hound    5
1    cat  ragdoll    1

Dưới đây, chúng tôi sẽ thêm một descriptioncột mới dưới dạng nối các cột khác bằng cách sử dụng +thao tác được ghi đè cho chuỗi. Định dạng chuỗi lạ mắt, f-string, v.v. sẽ không hoạt động ở đây vì +áp dụng cho các giá trị vô hướng và không phải giá trị 'nguyên thủy':

df['description'] = 'A ' + df.age.astype(str) + ' years old ' \
                    + df.type + ' ' + df.animal

In [2]: df
Out[2]:
  animal     type  age                description
-------------------------------------------------
0    dog    hound    5    A 5 years old hound dog
1    cat  ragdoll    1  A 1 years old ragdoll cat

Chúng tôi nhận được 1 yearscho con mèo (thay vì 1 year) mà chúng tôi sẽ sửa chữa bên dưới bằng cách sử dụng các điều kiện.

Sửa đổi một cột hiện có với các điều kiện

Ở đây chúng tôi sẽ thay thế animalcột gốc bằng các giá trị từ các cột khác và sử dụng np.wheređể đặt một chuỗi con có điều kiện dựa trên giá trị của age:

# append 's' to 'age' if it's greater than 1
df.animal = df.animal + ", " + df.type + ", " + \
    df.age.astype(str) + " year" + np.where(df.age > 1, 's', '')

In [3]: df
Out[3]:
                 animal     type  age
-------------------------------------
0   dog, hound, 5 years    hound    5
1  cat, ragdoll, 1 year  ragdoll    1

Sửa đổi nhiều cột với điều kiện

Một cách tiếp cận linh hoạt hơn là gọi .apply()trên toàn bộ khung dữ liệu thay vì trên một cột duy nhất:

def transform_row(r):
    r.animal = 'wild ' + r.type
    r.type = r.animal + ' creature'
    r.age = "{} year{}".format(r.age, r.age > 1 and 's' or '')
    return r

df.apply(transform_row, axis=1)

In[4]:
Out[4]:
         animal            type      age
----------------------------------------
0    wild hound    dog creature  5 years
1  wild ragdoll    cat creature   1 year

Trong đoạn mã trên, transform_row(r)hàm lấy một Seriesđối tượng đại diện cho một hàng nhất định (được biểu thị bằng axis=1, giá trị mặc định của axis=0sẽ cung cấp một Seriesđối tượng cho mỗi cột). Điều này đơn giản hóa quá trình xử lý vì chúng tôi có thể truy cập các giá trị 'nguyên thủy' thực tế trong hàng bằng cách sử dụng tên cột và hiển thị các ô khác trong hàng / cột nhất định.


1
Cảm ơn bạn đã dành thời gian để viết lên một câu trả lời toàn diện như vậy. Nhiều đánh giá cao.
Parseltongue

Cảm ơn vì câu trả lời cực kỳ hữu ích này. Một phần tiếp theo - điều gì sẽ xảy ra nếu chúng ta muốn sửa đổi một cột bằng cách làm toán trên cột, thay vì sửa đổi một chuỗi? Ví dụ, bằng cách sử dụng ví dụ trên, điều gì sẽ xảy ra nếu chúng ta muốn nhân cột df.age với 7 nếu df.animal == 'dog'? Cảm ơn bạn!
GbG

1
@GbG: np.wherecó thể là những gì bạn đang tìm kiếm, ví dụ: stackoverflow.com/a/42540310/191246 nhưng cũng có thể bạn sẽ không thể điều chỉnh logic thành một phép toán vô hướng, khi đó bạn cần phải chuyển đổi rõ ràng ô về số tương tự như cách nó được thực hiện trongtransform_row
ccpizza,

Xin cảm ơn @ccpizza! Chỉ cần những gì tôi đang tìm kiếm.
GbG

13

Câu hỏi này có thể vẫn được truy cập thường xuyên đến mức đáng để đưa ra một phụ lục cho câu trả lời của ông Kassies. Lớp tích dicthợp có thể được phân lớp để trả về mặc định cho các khóa 'bị thiếu'. Cơ chế này hoạt động tốt đối với gấu trúc. Nhưng hãy xem bên dưới.

Bằng cách này, bạn có thể tránh được các lỗi chính.

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> class SurnameMap(dict):
...     def __missing__(self, key):
...         return ''
...     
>>> surnamemap = SurnameMap()
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap[x])
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

Điều tương tự có thể được thực hiện đơn giản hơn theo cách sau. Việc sử dụng đối số 'mặc định' cho getphương thức của một đối tượng dict làm cho nó không cần thiết phải phân lớp một dict.

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> surnamemap = {}
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap.get(x, ''))
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

1
đây là câu trả lời hay nhất và dễ nhất mà tôi từng thấy, với khả năng xử lý mặc định tuyệt vời. Cảm ơn bạn.
Brendan

@Brendan: Ồ! Cảm ơn rất nhiều.
Bill Bell
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.