Perché l'assegnazione con [:] contro iloc [:] produce risultati diversi in panda?


13

Sono così confuso con diversi metodi di indicizzazione utilizzando ilocin Panda.

Supponiamo che sto cercando di convertire un Dataframe 1-d in un Dataframe 2-d. Innanzitutto ho il seguente Dataframe 1-d

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

E ho intenzione di convertirlo in un Dataframe 2-d delle dimensioni di 2x4. Comincio preselezionando il Dataframe 2-d come segue:

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

Quindi uso for-loop per aiutarmi a convertire a_df(1-d) in b_df(2-d) con il seguente codice

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

Mi dà solo i seguenti risultati

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

Ma quando sono passato b_df.iloc[i,:]a b_df.iloc[i][:]. Il risultato è corretto come il seguente, che è quello che voglio

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

Qualcuno potrebbe spiegarmi qual è la differenza tra .iloc[i,:]e .iloc[i][:]è, e perché ha .iloc[i][:]funzionato nel mio esempio sopra ma non.iloc[i,:]


Questo è curioso. b_df.iloc[1] = a_df.iloc[0, 4:8]assegna una serie con indice [4, 5, 6, 7]a una serie con indice [0, 1, 2, 3]. Non vi è alcuna sovrapposizione, quindi NaNsi assegnano a tutti gli elementi. Fino a questo punto ha senso per me. Ma come te non sono chiaro il motivo per cui b_df.iloc[1][:] = ...si comporta diversamente: ispezionare gli oggetti b_df.iloc[1]e b_df.iloc[1][:]non rivela alcuna differenza tra gli indici. La mia ipotesi migliore sarebbe che l'assegnazione diretta a una copia ( [:]) sia trattata come un caso speciale da Pandas che lo fa ignorare l'indice del cessionario e creare questa discrepanza.
Seb,

Penso che sia stato utilizzato per l'indice e il successo della prima riga perché ha lo stesso indice
Phung Duy Phong,

1
La cosa fondamentale da ricordare sui panda è che la maggior parte delle operazioni sui panda utilizzano un concetto chiamato "allineamento di dati istrinico". Ciò significa che quasi tutte le operazioni eseguite con i panda allineano gli indici di entrambi i lati dell'istruzione. Qui stai provando a impostare l'indice 1 usando l'indice 0, i panda assegneranno nans perché non c'è indice 0 sul lato destro di quell'assegnazione. Ricorda anche che anche le intestazioni di colonna sono un indice. Quindi, i panda allineano l'intestazione di colonna all'intestazione di colonna.
Scott Boston,

3
In secondo luogo, l'uso di .iloc [i] [:] è chiamato concatenamento di indici ed è generalmente un "no-no" piuttosto grande nei panda. Ci sono alcuni isuse con i panda che creano viste di un oggetto o creano un oggetto nuovo di zecca in memoria che può produrre alcuni risultati inaspettati.
Scott Boston,

Per favore, non dimenticare di votare tutte le risposte funzionanti e di accettare quella che ti piace di più. Probabilmente lo sai, ma questo è per far sapere alla community quali risposte sono state utili e anche per premiare le persone per il loro tempo e fatica;) Vedi questo meta.stackexchange.com/questions/5234/ e meta.stackexchange.com/ questions / 173399 /
alan.elkin

Risposte:


3

C'è una differenza molto, molto grande tra series.iloc[:]e series[:], quando si assegna indietro. (i)loccontrolla sempre che tutto ciò che stai assegnando corrisponda all'indice dell'assegnatario. Nel frattempo, la [:]sintassi si assegna all'array NumPy sottostante, ignorando l'allineamento dell'indice.

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

Ora che hai capito la differenza, diamo un'occhiata a cosa succede nel tuo codice. Stampa l'RHS dei tuoi loop per vedere cosa stai assegnando:

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   

Quando si assegna a b_df.iloc[i, :]nella seconda iterazione, gli indici sono diversi, quindi non viene assegnato nulla e vengono visualizzati solo NaN. Tuttavia, la modifica b_df.iloc[i, :]in b_df.iloc[i][:]significa che si assegna all'array NumPy sottostante, quindi l'allineamento dell'indicizzazione viene ignorato. Questa operazione è meglio espressa come

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

Vale anche la pena ricordare che questa è una forma di assegnazione concatenata, che non è una buona cosa , e rende anche il tuo codice più difficile da leggere e capire.


1
Ora lo capisco, grazie. Prima di assegnare il premio, potresti aggiungere un riferimento per questo: "la [:]sintassi si assegna all'array NumPy sottostante"?
Seb

@Seb Non troverai davvero riferimenti a questo nella documentazione perché è in qualche modo un dettaglio di implementazione. Potrebbe essere più facile trovare il codice su GitHub che è responsabile di questo, ma penso che il modo più semplice sia semplicemente dimostrare cosa succede. Ho modificato il piccolo esempio nella parte superiore della mia risposta per mostrare come l'array sottostante viene manipolato durante i diversi tipi di riassegnazione. Spero che questo renda le cose più chiare!
cs95,

Grazie mille! Adesso è molto più chiaro.
Tommy Yip,

0

La differenza è che nel primo caso l'interprete Python ha eseguito il codice come:

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

dove il valore sarebbe il lato destro dell'equazione. Considerando che nel secondo caso l'interprete Python ha eseguito il codice come:

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

dove di nuovo il valore sarebbe il lato destro dell'equazione.

In ognuno di questi due casi, all'interno di setitem verrebbe chiamato un metodo diverso a causa della differenza tra i tasti (i, slice (None)) e slice (None). Pertanto abbiamo un comportamento diverso.


b_df.iloc[i]e b_df.iloc[i][:]hanno gli stessi indici però. Perché è possibile assegnare una serie con indice non corrispondente all'una ma non all'altra?
Seb,

nel primo caso il _set_item sarebbe chiamato nel secondo one_setitem_slice sarebbe chiamato. Quindi, sospetto a causa della differenza di quei metodi abbiamo il comportamento sopra
MaPy

0

Qualcuno potrebbe spiegarmi cosa la differenza tra .iloc[i,:]e .iloc[i][:]è

La differenza tra .iloc[i,:]e.iloc[i][:]

Nel caso in .iloc[i,:]cui si acceda direttamente a un possesso specifico del DataFrame, selezionando tutte le ( :) colonne della iriga th. Per quanto ne so, equivale a lasciare la seconda dimensione non specificata (.iloc[i] ).

Nel caso .iloc[i][:]tu stia eseguendo 2 operazioni incatenate. Quindi, il risultato di .iloc[i]sarà quindi influenzato da [:]. L'uso di questo per impostare i valori è scoraggiato dallo stesso Panda qui con un avviso, quindi non dovresti usarlo:

Se una copia o un riferimento viene restituito per un'operazione di impostazione, può dipendere dal contesto. Questo a volte viene chiamato incarico incatenato e dovrebbe essere evitato


... e perché ha .iloc[i][:]funzionato nel mio esempio sopra ma non.iloc[i,:]

Come @Scott ha menzionato nei commenti OP, l'allineamento dei dati è intrinseco , quindi gli indici nella parte destra di =non saranno inclusi se non sono presenti nella parte sinistra. Questo è il motivo per cui ci sono NaNvalori sulla seconda riga.

Quindi, per lasciare le cose chiare, potresti fare come segue:

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

Oppure puoi convertire in listinvece di usare 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.