A diferença entre colchetes [] e colchetes duplos [[]] para acessar os elementos de uma lista ou quadro de dados

521

R fornece dois métodos diferentes para acessar os elementos de uma lista ou data.frame: []e [[]].

Qual é a diferença entre os dois e em que situações devo usar um sobre o outro?

Sharpie
fonte

Respostas:

327

OR Language Definition é útil para responder a esses tipos de perguntas:

R possui três operadores de indexação básicos, com sintaxe exibida pelos seguintes exemplos

    x[i]
    x[i, j]
    x[[i]]
    x[[i, j]]
    x$a
    x$"a"

Para vetores e matrizes, os [[formulários raramente são usados, embora apresentem pequenas diferenças semânticas do [formulário (por exemplo, elimina qualquer atributo de nomes ou nomes de nomes e essa correspondência parcial é usada para índices de caracteres). Ao indexar estruturas multidimensionais com um único índice, x[[i]]ou x[i]retornará o ith elemento sequencial de x.

Para listas, geralmente se usa [[para selecionar qualquer elemento único, enquanto [retorna uma lista dos elementos selecionados.

O [[formulário permite que apenas um único elemento seja selecionado usando índices inteiros ou de caracteres, enquanto [permite a indexação por vetores. Observe que, para uma lista, o índice pode ser um vetor e cada elemento do vetor é aplicado, por sua vez, à lista, ao componente selecionado, ao componente selecionado desse componente e assim por diante. O resultado ainda é um único elemento.

ars
fonte
6
Qual é o raciocínio por trás do uso de [[vs [para indexar com um único número vs vetor? Por que não usar apenas [para ambos? Eu acho que você pode usar o [[para retornar uma única entrada e [com um índice retorna uma lista de comprimento 1 ... mas por que não fazer apenas [retornar uma única entrada com um índice em vez de uma lista? Por que você gostaria de retornar uma lista de tamanho 1?
wordsforthewise
4
@words, portanto, ao programar, você pode ter um vetor de comprimento indefinido que deseja usar para indexação. Tendo [sempre retornar um meio de lista que você começa a mesma classe de saída para x[v], independentemente da duração da v. Por exemplo, pode-se querer lapplymais de um subconjunto de uma lista: lapply(x[v], fun). Se você [soltasse a lista de vetores de comprimento um, isso retornaria um erro sempre que vtivesse comprimento um.
Axeman
1
Eu acho que isso explica mais claramente, adv-r.had.co.nz/Subsetting.html
The Red Pea
171

As diferenças significativas entre os dois métodos são a classe dos objetos que eles retornam quando usados ​​para extração e se eles podem aceitar um intervalo de valores ou apenas um valor único durante a atribuição.

Considere o caso da extração de dados na seguinte lista:

foo <- list( str='R', vec=c(1,2,3), bool=TRUE )

Digamos que gostaríamos de extrair o valor armazenado por bool de foo e usá-lo dentro de uma if()declaração. Isso ilustrará as diferenças entre os valores de retorno []e [[]]quando eles são usados ​​para extração de dados. O []método retorna objetos da lista de classes (ou data.frame se foo era um data.frame) enquanto o [[]]método retorna objetos cuja classe é determinada pelo tipo de seus valores.

Portanto, o uso do []método resulta no seguinte:

if( foo[ 'bool' ] ){ print("Hi!") }
Error in if (foo["bool"]) { : argument is not interpretable as logical

class( foo[ 'bool' ] )
[1] "list"

Isso ocorre porque o []método retornou uma lista e uma lista não é um objeto válido para passar diretamente para uma if()instrução. Nesse caso, precisamos usar [[]]porque ele retornará o objeto "vazio" armazenado em 'bool', que terá a classe apropriada:

if( foo[[ 'bool' ]] ){ print("Hi!") }
[1] "Hi!"

class( foo[[ 'bool' ]] )
[1] "logical"

A segunda diferença é que o []operador pode ser usado para acessar um intervalo de slots em uma lista ou colunas em um quadro de dados, enquanto o [[]]operador está limitado a acessar um único slot ou coluna. Considere o caso da atribuição de valor usando uma segunda lista bar():

bar <- list( mat=matrix(0,nrow=2,ncol=2), rand=rnorm(1) )

Digamos que queremos substituir os dois últimos slots de foo pelos dados contidos em bar. Se tentarmos usar o [[]]operador, é isso que acontece:

foo[[ 2:3 ]] <- bar
Error in foo[[2:3]] <- bar : 
more elements supplied than there are to replace

Isso ocorre porque o [[]]acesso a um único elemento é limitado. Precisamos usar []:

foo[ 2:3 ] <- bar
print( foo )

$str
[1] "R"

$vec
     [,1] [,2]
[1,]    0    0
[2,]    0    0

$bool
[1] -0.6291121

Observe que, embora a tarefa tenha sido bem-sucedida, os slots foo mantiveram seus nomes originais.

Sharpie
fonte
111

Os colchetes duplos acessam um elemento da lista , enquanto um colchete fornece uma lista com um único elemento.

lst <- list('one','two','three')

a <- lst[1]
class(a)
## returns "list"

a <- lst[[1]]
class(a)
## returns "character"
medriscoll
fonte
66

Partida Hadley Wickham:

De Hadley Wickham

Minha modificação (de baixa qualidade) para mostrar usando tidyverse / purrr:

insira a descrição da imagem aqui

jzadra
fonte
2
Legal! Você tem alguns dos picossegundos de Grace Hopper !
Steve jarros
@StevePitchers hein?
jzadra
Grace Hopper em Letterman, demonstrando nano segundos dailymotion.com/video/x35dsz7 .
Steve Pitchers
48

[]extrai uma lista, [[]]extrai elementos dentro da lista

alist <- list(c("a", "b", "c"), c(1,2,3,4), c(8e6, 5.2e9, -9.3e7))

str(alist[[1]])
 chr [1:3] "a" "b" "c"

str(alist[1])
List of 1
 $ : chr [1:3] "a" "b" "c"

str(alist[[1]][1])
 chr "a"
Brad Gilbert
fonte
18

Apenas adicionando aqui que [[também está equipado para indexação recursiva .

Isso foi sugerido na resposta por @JijoMatthew, mas não explorado.

Conforme observado em ?"[[", sintaxe como x[[y]], onde length(y) > 1, é interpretada como:

x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]

Observe que isso não altera qual deve ser sua principal explicação sobre a diferença entre [e [[- ou seja, que o primeiro é usado para subconjunto e o último é usado para extrair elementos de lista única.

Por exemplo,

x <- list(list(list(1), 2), list(list(list(3), 4), 5), 6)
x
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [1] 1
#
# [[1]][[2]]
# [1] 2
#
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
# [[2]][[1]][[1]][[1]]
# [1] 3
#
# [[2]][[1]][[2]]
# [1] 4
#
# [[2]][[2]]
# [1] 5
#
# [[3]]
# [1] 6

Para obter o valor 3, podemos fazer:

x[[c(2, 1, 1, 1)]]
# [1] 3

Voltando à resposta de @ JijoMatthew acima, lembre-se r:

r <- list(1:10, foo=1, far=2)

Em particular, isso explica os erros que tendemos a obter ao usar incorretamente [[, a saber:

r[[1:3]]

Erro em r[[1:3]]: falha na indexação recursiva no nível 2

Como esse código realmente tentou avaliar r[[1]][[2]][[3]]e o aninhamento de rparadas no nível um, a tentativa de extrair por meio da indexação recursiva falhou [[2]]no nível 2.

Erro em r[[c("foo", "far")]]: subscrito fora dos limites

Aqui, R estava procurando r[["foo"]][["far"]], o que não existe, então obtemos o erro de subscrito fora dos limites.

Provavelmente seria um pouco mais útil / consistente se esses dois erros dessem a mesma mensagem.

MichaelChirico
fonte
Olá Micheal senhor, podemos usar [[]] para indexação múltipla?
Therii
14

Ambos são formas de subconjunto. O colchete único retornará um subconjunto da lista, que por si só será uma lista. ou seja: pode ou não conter mais de um elemento. Por outro lado, um colchete retornará apenas um elemento da lista.

-Suporte único nos dará uma lista. Também podemos usar colchetes se quisermos retornar vários elementos da lista. considere a seguinte lista: -

>r<-list(c(1:10),foo=1,far=2);

Agora observe a maneira como a lista é retornada quando tento exibi-la. Digite digito e pressione enter

>r

#the result is:-

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

$foo

[1] 1

$far

[1] 2

Agora veremos a magia do suporte simples: -

>r[c(1,2,3)]

#the above command will return a list with all three elements of the actual list r as below

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

$foo

[1] 1


$far

[1] 2

que é exatamente o mesmo que quando tentamos exibir o valor de r na tela, o que significa que o uso de colchetes retornou uma lista, onde no índice 1 temos um vetor de 10 elementos, temos mais dois elementos com nomes foo e longe. Também podemos optar por fornecer um único índice ou nome de elemento como entrada para o colchete único. por exemplo:

> r[1]

[[1]]

 [1]  1  2  3  4  5  6  7  8  9 10

Neste exemplo, fornecemos um índice "1" e, em troca, obtivemos uma lista com um elemento (que é uma matriz de 10 números)

> r[2]

$foo

[1] 1

No exemplo acima, demos um índice "2" e, em troca, obtivemos uma lista com um elemento

> r["foo"];

$foo

[1] 1

Neste exemplo, passamos o nome de um elemento e, em troca, uma lista foi retornada com um elemento.

Você também pode passar um vetor de nomes de elementos como: -

> x<-c("foo","far")

> r[x];

$foo

[1] 1

$far
[1] 2

Neste exemplo, passamos um vetor com dois nomes de elementos "foo" e "far"

Em troca, temos uma lista com dois elementos.

Em suma, o colchete único sempre retornará outra lista com número de elementos igual ao número de elementos ou número de índices que você passa para o colchete único.

Por outro lado, um colchete duplo sempre retornará apenas um elemento. Antes de passar para o colchete duplo, observe uma nota. NOTE:THE MAJOR DIFFERENCE BETWEEN THE TWO IS THAT SINGLE BRACKET RETURNS YOU A LIST WITH AS MANY ELEMENTS AS YOU WISH WHILE A DOUBLE BRACKET WILL NEVER RETURN A LIST. RATHER A DOUBLE BRACKET WILL RETURN ONLY A SINGLE ELEMENT FROM THE LIST.

Vou colocar alguns exemplos. Mantenha uma anotação das palavras em negrito e retorne a ela depois de concluir os exemplos abaixo:

O colchete duplo retornará o valor real no índice. ( NÃO retornará uma lista)

  > r[[1]]

     [1]  1  2  3  4  5  6  7  8  9 10


  >r[["foo"]]

    [1] 1

para colchetes duplos, se tentarmos exibir mais de um elemento passando um vetor, resultará em um erro apenas porque não foi criado para atender a essa necessidade, mas apenas para retornar um único elemento.

Considere o seguinte

> r[[c(1:3)]]
Error in r[[c(1:3)]] : recursive indexing failed at level 2
> r[[c(1,2,3)]]
Error in r[[c(1, 2, 3)]] : recursive indexing failed at level 2
> r[[c("foo","far")]]
Error in r[[c("foo", "far")]] : subscript out of bounds
Jijo Mathew
fonte
1
A votação foi reduzida porque "a transmissão de um vetor ... resultará em um erro apenas porque ele não foi criado para atender a essa necessidade" está incorreto; veja minha nova resposta.
MichaelChirico
1
Votado, porque faz reivindicações fortes como "QUANDO UM SUPORTE DUPLO NUNCA DEVOLVERÁ UMA LISTA". Não é verdade - se tivermos um objeto que seja lista de listas, o colchete duplo retornará outra lista.
precisa saber é
O fato de []retornar uma classe de lista, mesmo que seja um dígito único, é muito pouco intuitivo. Eles deveriam ter criado outra sintaxe, como ([])para a lista, e [[]]acessar o elemento real está correto. Prefiro pensar [[]]no valor bruto como em outros idiomas.
TokyoToo
13

Para ajudar os novatos a navegar pelo nevoeiro manual, pode ser útil ver a [[ ... ]]notação como uma função em colapso - em outras palavras, é quando você deseja apenas 'obter os dados' de um vetor, lista ou quadro de dados nomeado. É bom fazer isso se você quiser usar dados desses objetos para cálculos. Esses exemplos simples ilustrarão.

(x <- c(x=1, y=2)); x[1]; x[[1]]
(x <- list(x=1, y=2, z=3)); x[1]; x[[1]]
(x <- data.frame(x=1, y=2, z=3)); x[1]; x[[1]]

Então, a partir do terceiro exemplo:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2
Pé Vermelho
fonte
1
Como iniciante, achei útil nas 3 atribuições x (usando "<-") substituir x = 1 por w = 1 para evitar confusão com o x que é o alvo de "<-"
user36800
Embora muito simples, eu realmente gosto desta explicação. Outra demonstração simples: iris[[1]]retorna um vetor, enquanto iris[1]retorna um data.frame
stevec
11

Sendo terminológico, o [[operador extrai o elemento de uma lista, enquanto o [operador pega um subconjunto de uma lista.

submartingale
fonte
7

Para outro caso de uso concreto, use colchetes duplos quando desejar selecionar um quadro de dados criado pela split()função. Se você não souber, split()agrupa um quadro de lista / dados em subconjuntos com base em um campo-chave. É útil se, quando você quiser operar em vários grupos, plotá-los, etc.

> class(data)
[1] "data.frame"

> dsplit<-split(data, data$id)
> class(dsplit)
[1] "list"

> class(dsplit['ID-1'])
[1] "list"

> class(dsplit[['ID-1']])
[1] "data.frame"
Pedro
fonte
-1

Por favor, consulte a explicação detalhada abaixo.

Eu usei o quadro de dados interno no R, chamado mtcars.

> mtcars 
               mpg cyl disp  hp drat   wt ... 
Mazda RX4     21.0   6  160 110 3.90 2.62 ... 
Mazda RX4 Wag 21.0   6  160 110 3.90 2.88 ... 
Datsun 710    22.8   4  108  93 3.85 2.32 ... 
           ............

A linha superior da tabela é chamada de cabeçalho que contém os nomes das colunas. Cada linha horizontal posteriormente indica uma linha de dados, que começa com o nome da linha e depois é seguida pelos dados reais. Cada membro de dados de uma linha é chamado de célula.

operador de colchete único "[]"

Para recuperar dados em uma célula, inseriríamos suas coordenadas de linha e coluna no único operador de colchete "[]". As duas coordenadas são separadas por vírgula. Em outras palavras, as coordenadas começam com a posição da linha, seguidas por vírgula e terminam com a posição da coluna. A ordem é importante.

Por exemplo 1: - Aqui está o valor da célula da primeira linha, segunda coluna dos mtcars.

> mtcars[1, 2] 
[1] 6

Exemplo 2: - Além disso, podemos usar os nomes de linhas e colunas em vez das coordenadas numéricas.

> mtcars["Mazda RX4", "cyl"] 
[1] 6 

Operador de colchete duplo "[[]]"

Fazemos referência a uma coluna de quadro de dados com o operador de colchete duplo "[[]]".

Exemplo 1: - Para recuperar o vetor da nona coluna do conjunto de dados interno mtcars, escrevemos mtcars [[9]].

mtcars [[9]] [1] 1 1 1 0 0 0 0 0 0 0 0 0 ...

Ex. 2: - Podemos recuperar o mesmo vetor de coluna por seu nome.

mtcars [["am"]] [1] 1 1 1 0 0 0 0 0 0 0 0 0 ...

Prasan Karunarathna
fonte
-1

Além do que, além do mais:

Seguindo o LINK da RESPOSTA aqui.

Aqui está um pequeno exemplo abordando o seguinte ponto:

x[i, j] vs x[[i, j]]

df1   <- data.frame(a = 1:3)
df1$b <- list(4:5, 6:7, 8:9)

df1[[1,2]]
df1[1,2]

str(df1[[1,2]])
str(df1[1,2])
Andre Elrico
fonte