Почему назначение с [:] по сравнению с iloc [:] дает разные результаты в pandas? - PullRequest
13 голосов
/ 21 февраля 2020

Я так запутался с различными методами индексации, использующими iloc в pandas.

Допустим, я пытаюсь преобразовать 1-й Dataframe в 2-й Dataframe. Сначала у меня есть следующий 1-й Dataframe

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

И я собираюсь преобразовать его в 2-й Dataframe размером 2x4. Я начинаю с установки 2-го кадра данных следующим образом:

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

Затем я использую for-l oop, чтобы помочь мне преобразовать a_df (1-й) в b_df (2-й ) со следующим кодом

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

Это дает только следующие результаты

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

Но когда я изменил b_df.iloc[i,:] на b_df.iloc[i][:]. Результат правильный, как показано ниже, вот что я хочу

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

Может ли кто-нибудь объяснить мне, в чем разница между .iloc[i,:] и .iloc[i][:], и почему .iloc[i][:] работал в моем примере выше но не .iloc[i,:]

Ответы [ 3 ]

3 голосов
/ 29 февраля 2020

Существует очень, очень большая разница между series.iloc[:] и series[:] при назначении назад. (i)loc всегда проверяет, соответствует ли то, что вы назначаете, индексу уполномоченного. Между тем синтаксис [:] присваивает базовому массиву NumPy, минуя выравнивание индекса.

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.]) 

Теперь, когда вы понимаете разницу, давайте посмотрим, что происходит в вашем коде. Просто распечатайте RHS ваших циклов, чтобы увидеть, что вы назначаете:

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   

При назначении b_df.iloc[i, :] во второй итерации индексы отличаются, поэтому ничего не назначается, и вы видите только NaN. Однако изменение b_df.iloc[i, :] на b_df.iloc[i][:] будет означать, что вы присваиваете базовому массиву NumPy, поэтому выравнивание индексации обходится. Эта операция лучше выражается как

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

Стоит также упомянуть это форма цепного присваивания, что нехорошо , а также усложняет чтение и понимание кода. ,

0 голосов
/ 28 февраля 2020

Может ли кто-нибудь объяснить мне, в чем разница между .iloc[i,:] и .iloc[i][:]

Разница между .iloc[i,:] и .iloc[i][:]

В случае .iloc[i,:] вы получаете прямой доступ к указанной c возможности DataFrame, выбирая все (:) столбцы i -ой строки. Насколько я знаю, это эквивалентно оставить 2-е измерение неуказанным (.iloc[i]).

В случае .iloc[i][:] вы выполняете 2 цепные операции. Таким образом, результат .iloc[i] будет зависеть от [:]. Использование этого параметра для установки значений не рекомендуется Pandas само по себе здесь с предупреждением, поэтому его не следует использовать:

Независимо от того, возвращена ли копия или ссылка для Операция установки может зависеть от контекста. Это иногда называется цепным присваиванием и его следует избегать


... и почему .iloc[i][:] работал в моем примере выше, но не .iloc[i,:]

Как отметил @Scott в комментариях OP, выравнивание данных является внутренним c, поэтому индексы в правой части = не будут включены, если их нет в левой части сторона. Вот почему во 2-й строке есть значения NaN.

Таким образом, чтобы все было ясно, вы можете сделать следующее:

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

Или вы можете преобразовать в list вместо использования 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)
0 голосов
/ 21 февраля 2020

Разница в том, что в первом случае интерпретатор Python выполнил код следующим образом:

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

, где значение будет в правой части уравнения. В то время как во втором случае интерпретатор Python выполнил код следующим образом:

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

, где снова значение будет в правой части уравнения.

В каждом из этих двух случаев внутри setitem будет вызван другой метод из-за разницы в ключах (i, slice (None)) и slice (None). Поэтому у нас другое поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...