Qual a diferença entre iloc, ix e loc?

636

Alguém pode explicar como esses três métodos de fatiar são diferentes?
Eu já vi os documentos e essas respostas , mas ainda me encontro incapaz de explicar como os três são diferentes. Para mim, eles parecem intercambiáveis ​​em grande parte, porque estão nos níveis mais baixos de fatiamento.

Por exemplo, digamos que queremos obter as cinco primeiras linhas de a DataFrame. Como é que todos esses três funcionam?

df.loc[:5]
df.ix[:5]
df.iloc[:5]

Alguém pode apresentar três casos em que a distinção de usos é mais clara?

AZhao
fonte
7
muito importante mencionar os cenários SettingWithCopyWarning: stackoverflow.com/questions/20625582/… e stackoverflow.com/questions/23688307/…
Paul
9
Observe que o ix agora está planejado para descontinuação: github.com/pandas-dev/pandas/issues/14218
JohnE

Respostas:

970

Nota: no pandas versão 0.20.0 e superior, ixestá obsoleto e o uso de loce ilocé incentivado. Deixei as partes desta resposta que descrevem ixintactas como uma referência para usuários de versões anteriores do pandas. Exemplos foram adicionados abaixo, mostrando alternativas para ix .


Primeiro, aqui está uma recapitulação dos três métodos:

  • locobtém linhas (ou colunas) com rótulos específicos do índice.
  • ilocobtém linhas (ou colunas) em posições específicas no índice (portanto, são necessários números inteiros).
  • ixgeralmente tenta se comportar como, locmas volta a se comportar como ilocse um rótulo não estivesse presente no índice.

É importante observar algumas sutilezas que podem ser ixum pouco complicadas de usar:

  • se o índice for do tipo inteiro, ixusará apenas a indexação baseada em rótulo e não voltará à indexação baseada em posição. Se o rótulo não estiver no índice, um erro será gerado.

  • se o índice não contiver apenas números inteiros, um número inteiro ixusará imediatamente a indexação baseada em posição em vez da indexação baseada em rótulo. Se, no entanto, ixreceber outro tipo (por exemplo, uma string), ele poderá usar a indexação baseada em rótulo.


Para ilustrar as diferenças entre os três métodos, considere a seguinte série:

>>> s = pd.Series(np.nan, index=[49,48,47,46,45, 1, 2, 3, 4, 5])
>>> s
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN
4    NaN
5    NaN

Veremos fatiar com o valor inteiro 3.

Nesse caso, s.iloc[:3]retorna as 3 primeiras linhas (já que trata 3 como uma posição) e s.loc[:3]retorna as 8 primeiras linhas (já que trata 3 como um rótulo):

>>> s.iloc[:3] # slice the first three rows
49   NaN
48   NaN
47   NaN

>>> s.loc[:3] # slice up to and including label 3
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN

>>> s.ix[:3] # the integer is in the index so s.ix[:3] works like loc
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN

Aviso s.ix[:3]retorna a mesma série ques.loc[:3] uma vez que procura primeiro o rótulo em vez de trabalhar na posição (e o índice para sé do tipo inteiro).

E se tentarmos com um rótulo inteiro que não esteja no índice (por exemplo, 6 )?

Aqui s.iloc[:6]retorna as 6 primeiras linhas da série conforme o esperado. No entanto, s.loc[:6]gera um KeyError, pois 6não está no índice.

>>> s.iloc[:6]
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN

>>> s.loc[:6]
KeyError: 6

>>> s.ix[:6]
KeyError: 6

De acordo com as sutilezas mencionadas acima, s.ix[:6]agora gera um KeyError porque ele tenta funcionar como locmas não consegue encontrar um 6no índice. Como nosso índice é do tipo inteiroix ele não volta a se comportar comoiloc .

Se, no entanto, nosso índice fosse do tipo misto, dado que um número inteiro ixse comportaria como ilocimediatamente, em vez de gerar um KeyError:

>>> s2 = pd.Series(np.nan, index=['a','b','c','d','e', 1, 2, 3, 4, 5])
>>> s2.index.is_mixed() # index is mix of different types
True
>>> s2.ix[:6] # now behaves like iloc given integer
a   NaN
b   NaN
c   NaN
d   NaN
e   NaN
1   NaN

Lembre-se de que ixainda é possível aceitar números não inteiros e se comportar da seguinte maneira loc:

>>> s2.ix[:'c'] # behaves like loc given non-integer
a   NaN
b   NaN
c   NaN

Como orientação geral, se você estiver indexando apenas usando rótulos ou indexando apenas com posições inteiras, use locou ilocevite resultados inesperados - tente não usar ix.


Combinando indexação baseada em posição e baseada em rótulo

Às vezes, com um DataFrame, você desejará combinar métodos de indexação de rótulo e de posição para as linhas e colunas.

Por exemplo, considere o seguinte DataFrame. Qual a melhor forma de dividir as linhas até e incluindo 'c' e pegar as quatro primeiras colunas?

>>> df = pd.DataFrame(np.nan, 
                      index=list('abcde'),
                      columns=['x','y','z', 8, 9])
>>> df
    x   y   z   8   9
a NaN NaN NaN NaN NaN
b NaN NaN NaN NaN NaN
c NaN NaN NaN NaN NaN
d NaN NaN NaN NaN NaN
e NaN NaN NaN NaN NaN

Nas versões anteriores do pandas (antes da versão 0.20.0), ixvocê pode fazer isso de maneira bem organizada - podemos dividir as linhas por rótulo e as colunas por posição (observe que, para as colunas, ixo padrão será o faturamento baseado em posição, pois 4não é um nome de coluna ):

>>> df.ix[:'c', :4]
    x   y   z   8
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN

Nas versões posteriores dos pandas, podemos alcançar esse resultado usando iloce com a ajuda de outro método:

>>> df.iloc[:df.index.get_loc('c') + 1, :4]
    x   y   z   8
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN

get_loc()é um método de índice que significa "obter a posição do rótulo neste índice". Observe que, desde o corte comiloc é exclusivo de seu ponto de extremidade, devemos adicionar 1 a esse valor se quisermos a linha 'c' também.

Existem outros exemplos na documentação dos pandas aqui .

Alex Riley
fonte
12
Ótima explicação! Uma pergunta relacionada que eu sempre tive é que relação, se houver, loc, iloc e ix tem com os avisos SettingWithCopy? Há alguma documentação, mas para ser honesto eu ainda estou um pouco confuso pandas.pydata.org/pandas-docs/stable/...
measureallthethings
3
@measureallthethings: loc, iloce ixainda pode acionar o aviso se eles são encadeados. O uso do exemplo DataFrame nos documentos vinculados dfmi.loc[:, 'one'].loc[:, 'second']aciona o aviso da mesma maneira dfmi['one']['second']que uma cópia dos dados (em vez de uma exibição) pode ser retornada pela primeira operação de indexação.
Alex Riley
O que você usa se deseja pesquisar um DateIndex com uma Data ou algo parecido df.ix[date, 'Cash']?
Cjm2671
@ cjm2671: ambos locou ixdevem funcionar nesse caso. Por exemplo, df.loc['2016-04-29', 'Cash']retornará todos os índices de linha com essa data específica na coluna 'Caixa'. (Você pode ser o mais específico possível ao recuperar índices com seqüências de caracteres, por exemplo '2016-01', selecionará todas as datas em janeiro de 2016, `` 2016-01-02 11 'selecionará as datas em 2 de janeiro de 2016 com o horário 11: ??: ?? .)
Alex Riley
Caso você queira atualizar esta resposta em algum momento, há sugestões aqui de como usar o loc / iloc em vez de ix github.com/pandas-dev/pandas/issues/14218
JohnE
142

ilocfunciona com base no posicionamento inteiro. Portanto, não importa quais sejam seus rótulos de linha, você sempre pode, por exemplo, obter a primeira linha fazendo

df.iloc[0]

ou as últimas cinco linhas, fazendo

df.iloc[-5:]

Você também pode usá-lo nas colunas. Isso recupera a terceira coluna:

df.iloc[:, 2]    # the : in the first position indicates all rows

Você pode combiná-los para obter interseções de linhas e colunas:

df.iloc[:3, :3] # The upper-left 3 X 3 entries (assuming df has 3+ rows and columns)

Por outro lado, .locuse índices nomeados. Vamos configurar um quadro de dados com seqüências de caracteres como rótulos de linha e coluna:

df = pd.DataFrame(index=['a', 'b', 'c'], columns=['time', 'date', 'name'])

Então podemos obter a primeira linha por

df.loc['a']     # equivalent to df.iloc[0]

e as segundas duas linhas da 'date'coluna por

df.loc['b':, 'date']   # equivalent to df.iloc[1:, 1]

e assim por diante. Agora, vale a pena ressaltar que os índices padrão de linha e coluna para a DataFramesão números inteiros de 0 e, nesse caso, iloce locfuncionariam da mesma maneira. É por isso que seus três exemplos são equivalentes. Se você tivesse um índice não numérico, como seqüências de caracteres ou data / hora, isso df.loc[:5] geraria um erro.

Além disso, você pode fazer a recuperação da coluna apenas usando os quadros de dados __getitem__:

df['time']    # equivalent to df.loc[:, 'time']

Agora, suponha que você queira misturar a posição e a indexação nomeada, ou seja, indexar usando nomes em linhas e posições em colunas (para esclarecer, quero dizer, selecione em nosso quadro de dados, em vez de criar um quadro de dados com seqüências de caracteres no índice de linha e números inteiros em o índice da coluna). É aqui que .ixentra:

df.ix[:2, 'time']    # the first two rows of the 'time' column

Também acho que vale a pena mencionar que você também pode passar vetores booleanos para o locmétodo. Por exemplo:

 b = [True, False, True]
 df.loc[b] 

Retornará a primeira e a terceira linhas de df. Isso é equivalente a df[b]seleção, mas também pode ser usado para atribuir via vetores booleanos:

df.loc[b, 'name'] = 'Mary', 'John'
JoeCondron
fonte
O df.iloc [:,:] é equivalente a todas as linhas e colunas?
Alvis
É como seria df.loc[:, :]. Pode ser usado para reatribuir os valores do todo DataFrameou criar uma visualização dele.
21317 JoeCondron
oi, você sabe por que loc e iloc levam parâmetros entre parênteses quadrados [] e não como um método normal entre parênteses clássicos ()?
Marine Galantin
119

Na minha opinião, a resposta aceita é confusa, pois usa um DataFrame com apenas valores ausentes. Também não gosto do termo baseado em posição.iloc e prefiro local inteiro , pois é muito mais descritivo e exatamente o que .ilocsignifica. A palavra-chave é INTEGER - .ilocprecisa de INTEGERS.

Veja minha série de blogs extremamente detalhada sobre seleção de subconjuntos para mais


.ix está obsoleto e ambíguo e nunca deve ser usado

Como .ixestá obsoleto, focaremos apenas nas diferenças entre .loce .iloc.

Antes de falarmos sobre as diferenças, é importante entender que os DataFrames têm rótulos que ajudam a identificar cada coluna e cada índice. Vamos dar uma olhada em um exemplo de DataFrame:

df = pd.DataFrame({'age':[30, 2, 12, 4, 32, 33, 69],
                   'color':['blue', 'green', 'red', 'white', 'gray', 'black', 'red'],
                   'food':['Steak', 'Lamb', 'Mango', 'Apple', 'Cheese', 'Melon', 'Beans'],
                   'height':[165, 70, 120, 80, 180, 172, 150],
                   'score':[4.6, 8.3, 9.0, 3.3, 1.8, 9.5, 2.2],
                   'state':['NY', 'TX', 'FL', 'AL', 'AK', 'TX', 'TX']
                   },
                  index=['Jane', 'Nick', 'Aaron', 'Penelope', 'Dean', 'Christina', 'Cornelia'])

insira a descrição da imagem aqui

Todas as palavras em negrito são os rótulos. As etiquetas, age, color, food, height, scoree statesão usados para as colunas . Os outros rótulos, Jane, Nick, Aaron, Penelope, Dean, Christina, Corneliasão usados para o índice .


As principais maneiras de selecionar linhas específicas em um DataFrame são com os indexadores .loce .iloc. Cada um desses indexadores também pode ser usado para selecionar colunas simultaneamente, mas é mais fácil focar apenas nas linhas por enquanto. Além disso, cada um dos indexadores usa um conjunto de colchetes que seguem imediatamente seu nome para fazer suas seleções.

.loc seleciona dados apenas por rótulos

Primeiro falaremos sobre o .locindexador, que seleciona apenas os dados pelos rótulos do índice ou da coluna. Em nosso exemplo DataFrame, fornecemos nomes significativos como valores para o índice. Muitos DataFrames não terão nomes significativos e, em vez disso, usarão como padrão apenas os números inteiros de 0 a n-1, onde n é o comprimento do DataFrame.

Existem três entradas diferentes que você pode usar para .loc

  • Uma linha
  • Uma lista de strings
  • Notação de fatia usando cadeias como valores de início e parada

Selecionando uma única linha com .loc com uma sequência

Para selecionar uma única linha de dados, coloque o rótulo do índice dentro dos colchetes a seguir .loc.

df.loc['Penelope']

Isso retorna a linha de dados como uma série

age           4
color     white
food      Apple
height       80
score       3.3
state        AL
Name: Penelope, dtype: object

Selecionando várias linhas com .loc com uma lista de cadeias

df.loc[['Cornelia', 'Jane', 'Dean']]

Isso retorna um DataFrame com as linhas na ordem especificada na lista:

insira a descrição da imagem aqui

Selecionando várias linhas com .loc com notação de fatia

A notação de fatia é definida pelos valores de início, parada e etapa. Ao fatiar por etiqueta, os pandas incluem o valor de parada no retorno. As seguintes fatias de Aaron a Dean, inclusive. Seu tamanho da etapa não é definido explicitamente, mas o padrão é 1.

df.loc['Aaron':'Dean']

insira a descrição da imagem aqui

Fatias complexas podem ser obtidas da mesma maneira que as listas Python.

.iloc seleciona dados apenas por localização inteira

Vamos agora voltar para .iloc. Cada linha e coluna de dados em um DataFrame tem um local inteiro que o define. Isso é um acréscimo ao rótulo que é exibido visualmente na saída . O local inteiro é simplesmente o número de linhas / colunas da parte superior / esquerda começando em 0.

Existem três entradas diferentes que você pode usar para .iloc

  • Um inteiro
  • Uma lista de números inteiros
  • Notação de fatia usando números inteiros como valores inicial e final

Selecionando uma única linha com .iloc com um número inteiro

df.iloc[4]

Isso retorna a 5ª linha (local inteiro 4) como uma Série

age           32
color       gray
food      Cheese
height       180
score        1.8
state         AK
Name: Dean, dtype: object

Selecionando várias linhas com .iloc com uma lista de números inteiros

df.iloc[[2, -2]]

Isso retorna um DataFrame da terceira e da segunda à última linhas:

insira a descrição da imagem aqui

Selecionando várias linhas com .iloc com notação de fatia

df.iloc[:5:3]

insira a descrição da imagem aqui


Seleção simultânea de linhas e colunas com .loc e .iloc

Uma excelente capacidade de ambos .loc/.ilocé a capacidade de selecionar linhas e colunas simultaneamente. Nos exemplos acima, todas as colunas foram retornadas de cada seleção. Podemos escolher colunas com os mesmos tipos de entradas que fazemos para linhas. Simplesmente precisamos separar a seleção de linha e coluna com uma vírgula .

Por exemplo, podemos selecionar as linhas Jane e Dean com apenas as colunas altura, pontuação e estado da seguinte maneira:

df.loc[['Jane', 'Dean'], 'height':]

insira a descrição da imagem aqui

Isso usa uma lista de rótulos para as linhas e notação de fatia para as colunas

Naturalmente, podemos realizar operações semelhantes .ilocusando apenas números inteiros.

df.iloc[[1,4], 2]
Nick      Lamb
Dean    Cheese
Name: food, dtype: object

Seleção simultânea com rótulos e localização de número inteiro

.ixfoi usado para fazer seleções simultaneamente com rótulos e local inteiro, o que foi útil, mas às vezes confuso e ambíguo e, felizmente, foi preterido. No caso de você precisar fazer uma seleção com uma mistura de rótulos e locais inteiros, será necessário fazer os rótulos de seleção ou locais inteiros.

Por exemplo, se quisermos selecionar linhas Nicke, Corneliajuntamente com as colunas 2 e 4, poderíamos usar .locconvertendo os números inteiros em rótulos com o seguinte:

col_names = df.columns[[2, 4]]
df.loc[['Nick', 'Cornelia'], col_names] 

Ou, alternativamente, converta os rótulos de índice em números inteiros com o get_locmétodo index.

labels = ['Nick', 'Cornelia']
index_ints = [df.index.get_loc(label) for label in labels]
df.iloc[index_ints, [2, 4]]

Seleção booleana

O indexador .loc também pode fazer a seleção booleana. Por exemplo, se estivermos interessados ​​em encontrar todas as linhas com idade acima de 30 e retornar apenas as colunas foode score, podemos fazer o seguinte:

df.loc[df['age'] > 30, ['food', 'score']] 

Você pode replicar isso com, .ilocmas não pode transmitir uma série booleana. Você deve converter a série booleana em uma matriz numpy como esta:

df.iloc[(df['age'] > 30).values, [2, 4]] 

Selecionando todas as linhas

É possível usar .loc/.ilocapenas para seleção de coluna. Você pode selecionar todas as linhas usando dois pontos como este:

df.loc[:, 'color':'score':2]

insira a descrição da imagem aqui


O operador de indexação [], também pode selecionar linhas e colunas, mas não simultaneamente.

A maioria das pessoas conhece o objetivo principal do operador de indexação DataFrame, que é selecionar colunas. Uma cadeia de caracteres seleciona uma única coluna como uma série e uma lista de cadeias seleciona várias colunas como um DataFrame.

df['food']

Jane          Steak
Nick           Lamb
Aaron         Mango
Penelope      Apple
Dean         Cheese
Christina     Melon
Cornelia      Beans
Name: food, dtype: object

O uso de uma lista seleciona várias colunas

df[['food', 'score']]

insira a descrição da imagem aqui

O que as pessoas estão menos familiarizadas é que, quando a notação de fatia é usada, a seleção acontece por rótulos de linha ou por local inteiro. Isso é muito confuso e algo que quase nunca uso, mas funciona.

df['Penelope':'Christina'] # slice rows by label

insira a descrição da imagem aqui

df[2:6:2] # slice rows by integer location

insira a descrição da imagem aqui

A explicitação de .loc/.ilocpara selecionar linhas é altamente preferida. O operador de indexação sozinho não pode selecionar linhas e colunas simultaneamente.

df[3:5, 'color']
TypeError: unhashable type: 'slice'
Ted Petrou
fonte
6
Uau, essa foi uma das explicações muito bem articuladas e lúcidas que eu já vi sobre um tópico de programação. O que você explicou no último sobre a indexação normal que funciona em linhas ou colunas é uma das razões pelas quais temos loc e iloc método. Me deparei com essa advertência no curso do datacamp. a.) O que df.columns e df.index retornam? É uma lista de strings? Se for uma lista, é permitido acessar dois elementos como este df.columns [[2,4]] em uma lista? b.) Posso chamar get_loc () em df.columns? c.) Por que precisamos chamar df ['age']> 30.values ​​no caso de iloc.
Pragun 30/05/19
O melhor analista que eu já vi.
Max
Esta é uma resposta muito boa, gostei de não entrar muito no ix, que é preterido e inútil mergulhar fundo. Obrigado.
omabena 16/05