Tại sao việc gán với [:] so với iloc [:] mang lại kết quả khác nhau trong gấu trúc?


13

Tôi rất bối rối với các phương pháp lập chỉ mục khác nhau bằng cách sử dụng iloctrong gấu trúc.

Giả sử tôi đang cố gắng chuyển đổi Dataframe 1-d thành Dataframe 2-d. Đầu tiên tôi có Dataframe 1-d sau đây

a_array = [1,2,3,4,5,6,7,8]
a_df = pd.DataFrame(a_array).T

Và tôi sẽ chuyển đổi nó thành Dataframe 2 chiều với kích thước 2x4. Tôi bắt đầu bằng cách đặt trước Dataframe 2-d như sau:

b_df = pd.DataFrame(columns=range(4),index=range(2))

Sau đó, tôi sử dụng for-loop để giúp tôi chuyển đổi a_df(1-d) thành b_df(2-d) với mã sau

for i in range(2):
    b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]

Nó chỉ cho tôi kết quả như sau

     0    1    2    3
0    1    2    3    4
1  NaN  NaN  NaN  NaN

Nhưng khi tôi đổi b_df.iloc[i,:]thành b_df.iloc[i][:]. Kết quả đúng như sau, đây là điều tôi muốn

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

Bất cứ ai có thể giải thích cho tôi sự khác biệt giữa .iloc[i,:].iloc[i][:]là gì, và tại sao .iloc[i][:]làm việc trong ví dụ của tôi ở trên nhưng không.iloc[i,:]


Đây là tò mò. b_df.iloc[1] = a_df.iloc[0, 4:8]gán một chuỗi có chỉ mục [4, 5, 6, 7]cho một chuỗi có chỉ mục [0, 1, 2, 3]. Không có sự trùng lặp để NaNs được gán cho tất cả các yếu tố. Cho đến thời điểm này nó có ý nghĩa với tôi. Nhưng giống như bạn, tôi không rõ tại sao lại b_df.iloc[1][:] = ...cư xử khác nhau khi kiểm tra các đối tượng b_df.iloc[1]b_df.iloc[1][:]không cho thấy sự khác biệt giữa các chỉ số. Dự đoán tốt nhất của tôi sẽ là việc gán trực tiếp vào một bản sao ( [:]) được coi là trường hợp đặc biệt của Pandas, điều này khiến nó bỏ qua chỉ mục của người được chuyển nhượng và tạo ra sự khác biệt này.
Seb

Tôi nghĩ rằng nó được coi là chỉ số và thành công ở hàng đầu tiên vì nó có cùng chỉ số
Phụng Duy Phong

1
Tôi nhớ điều quan trọng về gấu trúc là hầu hết tất cả các hoạt động trong gấu trúc đều sử dụng một khái niệm gọi là 'căn chỉnh dữ liệu'. Có nghĩa là hầu như bất kỳ hoạt động nào bạn làm với gấu trúc sẽ căn chỉnh các chỉ mục của cả hai mặt của tuyên bố. Ở đây bạn đang cố gắng đặt chỉ mục 1 bằng cách sử dụng chỉ mục 0, gấu trúc sẽ gán các số không vì không có chỉ số 0 ở bên phải của nhiệm vụ đó. Cũng nên nhớ rằng các tiêu đề cột cũng là một chỉ mục. Vì vậy, gấu trúc sẽ căn chỉnh tiêu đề cột với tiêu đề cột.
Scott Boston

3
Thứ hai, sử dụng .iloc [i] [:] được gọi là chuỗi chỉ số và nó thường là một "không-không" khá lớn trong gấu trúc. Có một số vấn đề với gấu trúc tạo ra các khung nhìn của một đối tượng hoặc tạo ra một đối tượng hoàn toàn mới trong bộ nhớ có thể mang lại một số kết quả không mong muốn.
Scott Boston

Xin đừng quên nâng cấp tất cả các câu trả lời đang hoạt động và chấp nhận câu trả lời bạn thích nhất. Có lẽ bạn biết điều này, nhưng điều này là để cho cộng đồng biết câu trả lời nào hữu ích và cũng thưởng cho mọi người vì thời gian và công sức của họ;) Xem meta.stackexchange.com/questions/5234/ và meta.stackexchange.com/ câu hỏi / 173399 /
alan.elkin

Câu trả lời:


3

Có một sự khác biệt rất lớn giữa series.iloc[:]series[:]khi gán lại. (i)locluôn kiểm tra để đảm bảo mọi thứ bạn đang gán từ khớp với chỉ mục của người được chuyển nhượng. Trong khi đó, [:]cú pháp gán cho mảng NumPy bên dưới, bỏ qua việc căn chỉnh chỉ mục.

s = pd.Series(index=[0, 1, 2, 3], dtype='float')  
s                                                                          

0   NaN
1   NaN
2   NaN
3   NaN
dtype: float64

# Let's get a reference to the underlying array with `copy=False`
arr = s.to_numpy(copy=False) 
arr 
# array([nan, nan, nan, nan])

# Reassign using slicing syntax
s[:] = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])                 
s                                                                          

0    1
1    2
2    3
3    4
dtype: int64

arr 
# array([1., 2., 3., 4.]) # underlying array has changed

# Now, reassign again with `iloc`
s.iloc[:] = pd.Series([5, 6, 7, 8], index=[3, 4, 5, 6]) 
s                                                                          

0    NaN
1    NaN
2    NaN
3    5.0
dtype: float64

arr 
# array([1., 2., 3., 4.])  # `iloc` created a new array for the series
                           # during reassignment leaving this unchanged

s.to_numpy(copy=False)     # the new underlying array, for reference                                                   
# array([nan, nan, nan,  5.]) 

Bây giờ bạn đã hiểu sự khác biệt, hãy xem những gì xảy ra trong mã của bạn. Chỉ cần in ra RHS của các vòng lặp của bạn để xem những gì bạn đang chỉ định:

for i in range(2): 
    print(a_df.iloc[0, i*4:(i+1)*4]) 

# output - first row                                                                   
0    1
1    2
2    3
3    4
Name: 0, dtype: int64
# second row. Notice the index is different
4    5
5    6
6    7
7    8
Name: 0, dtype: int64   

Khi gán cho b_df.iloc[i, :]trong lần lặp thứ hai, các chỉ mục khác nhau nên không có gì được chỉ định và bạn chỉ thấy NaN. Tuy nhiên, thay đổi b_df.iloc[i, :]thành b_df.iloc[i][:]sẽ có nghĩa là bạn gán cho mảng NumPy bên dưới, do đó việc căn chỉnh chỉ mục được bỏ qua. Hoạt động này được thể hiện tốt hơn như là

for i in range(2):
    b_df.iloc[i, :] = a_df.iloc[0, i*4:(i+1)*4].to_numpy()

b_df                                                                       

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

Điều đáng nói là đây là một hình thức gán chuỗi, đây không phải là một điều tốt , và cũng làm cho mã của bạn khó đọc và hiểu hơn.


1
Bây giờ tôi hiểu nó, cảm ơn bạn. Trước khi tôi trao tiền thưởng, bạn có thể thêm một tham chiếu cho việc này không: " [:]cú pháp gán cho mảng NumPy bên dưới"?
Seb

@Seb Bạn sẽ không thực sự tìm thấy tài liệu tham khảo về tài liệu này vì nó phần nào là chi tiết triển khai. Có thể dễ dàng hơn để tìm mã trên GitHub chịu trách nhiệm cho việc này, nhưng tôi nghĩ cách dễ nhất là chỉ cần chứng minh những gì xảy ra. Tôi đã chỉnh sửa ví dụ nhỏ ở đầu câu trả lời của mình để cho biết cách thức mảng bên dưới được thao tác trong các loại gán lại khác nhau. Hy vọng rằng làm cho mọi thứ rõ ràng hơn!
cs95

Cảm ơn bạn rất nhiều! Bây giờ thì rõ ràng hơn nhiều.
Tommy Yip

0

Sự khác biệt là trong trường hợp đầu tiên, trình thông dịch Python đã thực thi mã là:

b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__setitem__((i, slice(None)), value)

trong đó giá trị sẽ là phía bên phải của phương trình. Trong trường hợp thứ hai, trình thông dịch Python đã thực thi mã là:

b_df.iloc[i][:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__getitem__(i).__setitem__(slice(None), value)

trong đó một lần nữa giá trị sẽ là phía bên phải của phương trình.

Trong mỗi hai trường hợp, một phương thức khác nhau sẽ được gọi bên trong setitem do sự khác biệt trong các khóa (i, lát (Không)) và lát (Không) Do đó chúng tôi có hành vi khác nhau.


b_df.iloc[i]b_df.iloc[i][:]có cùng chỉ số mặc dù. Tại sao bạn có thể gán một chuỗi có chỉ mục không khớp với một mà không phải là chỉ số khác?
Seb

trong trường hợp đầu tiên, _set_item sẽ được gọi trong one_setitem_slice thứ hai sẽ được gọi. Vì vậy, nghi ngờ do sự khác biệt của các phương pháp mà chúng tôi có hành vi trên
MaPy

0

Bất cứ ai có thể giải thích cho tôi sự khác biệt giữa .iloc[i,:].iloc[i][:]

Sự khác biệt giữa .iloc[i,:].iloc[i][:]

Trong trường hợp .iloc[i,:]bạn đang truy cập trực tiếp vào một sở hữu cụ thể của DataFrame, bằng cách chọn tất cả ( :) cột của ihàng thứ. Theo tôi biết, nó tương đương với việc để lại chiều thứ 2 không xác định ( .iloc[i]).

Trong trường hợp .iloc[i][:]bạn đang thực hiện 2 thao tác xích. Vì vậy, kết quả của .iloc[i]sau đó sẽ bị ảnh hưởng bởi [:]. Sử dụng điều này để đặt giá trị không được khuyến khích bởi chính Pandas ở đây với cảnh báo, vì vậy bạn không nên sử dụng nó:

Việc một bản sao hoặc một tham chiếu được trả về cho một thao tác cài đặt, có thể phụ thuộc vào ngữ cảnh. Điều này đôi khi được gọi là chuyển nhượng chuỗi và nên tránh


... và tại sao .iloc[i][:]làm việc trong ví dụ của tôi ở trên nhưng không.iloc[i,:]

Như @Scott đã đề cập trên các bình luận của OP, căn chỉnh dữ liệu là nội tại , do đó, các chỉ mục ở phía bên phải =sẽ không được bao gồm nếu chúng không xuất hiện ở phía bên trái. Đây là lý do tại sao có NaNcác giá trị trên hàng thứ 2.

Vì vậy, để mọi thứ rõ ràng, bạn có thể làm như sau:

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Reset the indices
    a_slice.reset_index(drop=True, inplace=True)
    # Set the slice into b_df
    b_df.iloc[i,:] = a_slice

Hoặc bạn có thể chuyển đổi sang listthay vì sử dụng reset_index:

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Convert the slice into a list and set it into b_df
    b_df.iloc[i,:] = list(a_slice)
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.