Cập nhật một khung dữ liệu trong gấu trúc trong khi lặp lại theo từng hàng


213

Tôi có một khung dữ liệu gấu trúc trông như thế này (nó khá lớn)

           date      exer exp     ifor         mat  
1092  2014-03-17  American   M  528.205  2014-04-19 
1093  2014-03-17  American   M  528.205  2014-04-19 
1094  2014-03-17  American   M  528.205  2014-04-19 
1095  2014-03-17  American   M  528.205  2014-04-19    
1096  2014-03-17  American   M  528.205  2014-05-17 

bây giờ tôi muốn lặp lại từng hàng và khi tôi đi qua từng hàng, giá trị của ifor mỗi hàng có thể thay đổi tùy thuộc vào một số điều kiện và tôi cần tra cứu một khung dữ liệu khác.

Bây giờ, làm thế nào để tôi cập nhật điều này khi tôi lặp lại. Đã thử một vài điều không ai trong số họ làm việc.

for i, row in df.iterrows():
    if <something>:
        row['ifor'] = x
    else:
        row['ifor'] = y

    df.ix[i]['ifor'] = x

Không có cách tiếp cận nào có vẻ hiệu quả. Tôi không thấy các giá trị được cập nhật trong khung dữ liệu.


2
Tôi nghĩ bạn muốn df.ix[i,'ifor']. df.ix[i]['ifor']là vấn đề bởi vì nó được lập chỉ mục chuỗi (không đáng tin cậy trong gấu trúc).
Karl D.

1
Bạn có thể cung cấp các khung khác cũng như <something>. Liệu mã của bạn có thể được vector hóa hay không sẽ phụ thuộc vào những điều đó. Nói chung, tránh iterrows. Trong trường hợp của bạn, bạn chắc chắn nên tránh nó vì mỗi hàng sẽ là một objectdtype Series.
Đám mây Phillip

Bạn nên tạo mặt nạ boolean cho điều kiện của mình, cập nhật tất cả các hàng đó và sau đó đặt phần còn lại thành giá trị khác
EdChum

Vui lòng không sử dụng iterrows (). Nó là một kẻ gây ra trắng trợn về kiểu chống tệ hại nhất trong lịch sử gấu trúc.
cs95

Câu trả lời:


232

Bạn có thể gán giá trị trong vòng lặp bằng df.set_value:

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.set_value(i,'ifor',ifor_val)

Nếu bạn không cần các giá trị hàng, bạn có thể chỉ cần lặp lại các chỉ số của df, nhưng tôi vẫn giữ vòng lặp for ban đầu trong trường hợp bạn cần giá trị hàng cho thứ gì đó không được hiển thị ở đây.

cập nhật

df.set_value () không được dùng nữa kể từ phiên bản 0.21.0, bạn có thể sử dụng df.at () thay thế:

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.at[i,'ifor'] = ifor_val

6
Xem pandas.pydata.org/pandas-docs/urdy/generated/ , viên đạn thứ hai: "2.Bạn không bao giờ nên sửa đổi một cái gì đó bạn đang lặp đi lặp lại"
Davor Josipovic

32
Tôi không chắc chúng ta có đọc chính xác không. Nếu bạn nhìn vào mã giả của tôi, tôi thực hiện sửa đổi trên khung dữ liệu, không phải trên giá trị từ trình vòng lặp. Giá trị lặp chỉ được sử dụng cho chỉ mục của giá trị / đối tượng. Điều gì sẽ thất bại là hàng ['ifor'] = some_thing, vì những lý do được đề cập trong tài liệu.
rakke

3
Cảm ơn bạn đã làm rõ.
Davor Josipovic

8
bây giờ set_value cũng được mô tả và nên sử dụng .at (hoặc .iat), vì vậy vòng lặp của tôi trông như thế này: for i, row in df.iterrows (): ifor_val = Something if <condition>: ifor_val = Something_else df.at [ i, 'ifor'] = ifor_val
phứcM

2
set_value không được dùng nữa và sẽ bị xóa trong bản phát hành trong tương lai. Vui lòng sử dụng bộ truy cập .at [] hoặc .iat []
RoyaumeIX

75

Đối tượng Pandas DataFrame nên được coi là một Series của Series. Nói cách khác, bạn nên nghĩ về nó theo các cột. Lý do tại sao điều này quan trọng là vì khi bạn sử dụng, pd.DataFrame.iterrowsbạn đang lặp qua các hàng dưới dạng Sê-ri. Nhưng đây không phải là Sê-ri mà khung dữ liệu đang lưu trữ và vì vậy chúng là Sê-ri mới được tạo cho bạn trong khi bạn lặp lại. Điều đó ngụ ý rằng khi bạn cố gắng gán tho chúng, những chỉnh sửa đó sẽ không được phản ánh trong khung dữ liệu gốc.

Ok, bây giờ điều đó đã hết cách: Chúng ta phải làm gì?

Gợi ý trước bài đăng này bao gồm:

  1. pd.DataFrame.set_valueđang bị phản đối như của Pandas phiên bản 0.21
  2. pd.DataFrame.ixđang bị phản đối
  3. pd.DataFrame.loclà tốt nhưng có thể làm việc trên các bộ chỉ mục mảng và bạn có thể làm tốt hơn

Đề nghị của tôi
Sử dụngpd.DataFrame.at

for i in df.index:
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y

Bạn thậm chí có thể thay đổi điều này thành:

for i in df.index:
    df.at[i, 'ifor'] = x if <something> else y

Trả lời bình luận

và nếu tôi cần sử dụng giá trị của hàng trước cho điều kiện if thì sao?

for i in range(1, len(df) + 1):
    j = df.columns.get_loc('ifor')
    if <something>:
        df.iat[i - 1, j] = x
    else:
        df.iat[i - 1, j] = y

và nếu tôi cần sử dụng giá trị của hàng trước cho điều kiện if thì sao? thêm một cột bị trễ vào df OG?
Yuca

hiệu quả khôn ngoan, cách tiếp cận của bạn tốt hơn so với việc thêm một cột bị trễ hay là hiệu quả không đáng kể đối với các bộ dữ liệu nhỏ? (<10k hàng)
Yuca

Mà phụ thuộc. Tôi sẽ sử dụng một cột bị trễ. Câu trả lời này là hiển thị những gì cần làm nếu bạn phải lặp. Nhưng nếu bạn không phải lặp, thì đừng.
piRSquared

Có nó, nếu có thể có phản hồi của bạn cho stackoverflow.com/q/51753001/9754169 thì thật tuyệt vời: D
Yuca

Đẹp cho tương phản .at [] với các lựa chọn thay thế cũ
Justas

35

Một phương thức bạn có thể sử dụng là itertuples(), nó lặp đi lặp lại trên các hàng DataFrame dưới dạng các tên được đặt tên, với giá trị chỉ mục là phần tử đầu tiên của bộ dữ liệu. Và nó nhanh hơn nhiều so với iterrows(). Đối với itertuples(), mỗi cái rowchứa nó Indextrong DataFrame và bạn có thể sử dụng locđể đặt giá trị.

for row in df.itertuples():
    if <something>:
        df.at[row.Index, 'ifor'] = x
    else:
        df.at[row.Index, 'ifor'] = x

    df.loc[row.Index, 'ifor'] = x

Trong hầu hết các trường hợp, itertuples()là nhanh hơn iathoặc at.

Cảm ơn @SantiStSupery, sử dụng .atnhanh hơn nhiềuloc .


3
Vì bạn chỉ trỏ đến một chỉ mục chính xác, bạn có thể nghĩ đến việc sử dụng .at thay vì .loc để cải thiện hiệu suất của mình. Xem câu hỏi này để biết thêm thông tin về điều này
SantiStSupery

suy nghĩ kỳ lạ nhưng df.loc[row.Index, 3] = xkhông hoạt động. Mặt khác, df.loc[row.Index, 'ifor'] = xhoạt động!
seralouk

19

Bạn nên gán giá trị bằng df.ix[i, 'exp']=Xhoặc df.loc[i, 'exp']=Xthay vì df.ix[i]['ifor'] = x.

Nếu không, bạn đang làm việc trên một khung nhìn và sẽ có được sự nóng lên:

-c:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead

Nhưng chắc chắn, vòng lặp có lẽ nên được thay thế bằng một số thuật toán được vector hóa để sử dụng toàn bộ DataFramenhư @Phillip Cloud đề xuất.


10

Chà, dù sao bạn cũng sẽ lặp đi lặp lại, tại sao không sử dụng phương pháp đơn giản nhất trong tất cả, df['Column'].values[i]

df['Column'] = ''

for i in range(len(df)):
    df['Column'].values[i] = something/update/new_value

Hoặc nếu bạn muốn so sánh các giá trị mới với cũ hoặc bất cứ thứ gì tương tự, tại sao không lưu trữ nó trong một danh sách và sau đó nối vào cuối.

mylist, df['Column'] = [], ''

for <condition>:
    mylist.append(something/update/new_value)

df['Column'] = mylist

7
for i, row in df.iterrows():
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y

0

Tốt hơn là sử dụng các lambdachức năng bằng cách sử dụng df.apply()-

df["ifor"] = df.apply(lambda x: {value} if {condition} else x["ifor"], axis=1)

-3

Tăng số MAX từ một cột. Ví dụ :

df1 = [sort_ID, Column1,Column2]
print(df1)

Đầu ra của tôi:

Sort_ID Column1 Column2
12         a    e
45         b    f
65         c    g
78         d    h

MAX = df1['Sort_ID'].max() #This returns my Max Number 

Bây giờ, tôi cần tạo một cột trong df2 và điền vào các giá trị cột làm tăng MAX.

Sort_ID Column1 Column2
79      a1       e1
80      b1       f1
81      c1       g1
82      d1       h1

Lưu ý: ban đầu df2 sẽ chỉ chứa Cột1 và Cột2. chúng ta cần cột Sortid được tạo và tăng MAX từ df1.

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.