La différence entre le crochet [] et le double crochet [[]] pour accéder aux éléments d'une liste ou d'une trame de données

521

R propose deux méthodes différentes pour accéder aux éléments d'une liste ou data.frame: []et [[]].

Quelle est la différence entre les deux et dans quelles situations dois-je utiliser l'une sur l'autre?

Sharpie
la source

Réponses:

327

La définition du langage R est pratique pour répondre à ces types de questions:

R a trois opérateurs d'indexation de base, avec la syntaxe affichée par les exemples suivants

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

Pour les vecteurs et les matrices, les [[formulaires sont rarement utilisés, bien qu'ils présentent de légères différences sémantiques par rapport au [formulaire (par exemple, il supprime tout attribut de noms ou de dimnames, et cette correspondance partielle est utilisée pour les indices de caractères). Lors de l'indexation de structures multidimensionnelles avec un seul index, x[[i]]ou x[i]retournera le ie élément séquentiel de x.

Pour les listes, on utilise généralement [[pour sélectionner n'importe quel élément, tandis que [renvoie une liste des éléments sélectionnés.

Le [[formulaire ne permet de sélectionner qu'un seul élément à l'aide d'indices entiers ou de caractères, tandis qu'il [permet l'indexation par des vecteurs. Notez cependant que pour une liste, l'index peut être un vecteur et que chaque élément du vecteur est appliqué tour à tour à la liste, au composant sélectionné, au composant sélectionné de ce composant, etc. Le résultat est toujours un élément unique.

ars
la source
6
Quel est le raisonnement derrière l'utilisation de [[vs [pour indexer avec un seul nombre vs vecteur? Pourquoi ne pas simplement utiliser [pour les deux? Je suppose que vous pouvez utiliser le [[pour récupérer une seule entrée, et [avec un index renvoie une liste de longueur 1 ... mais pourquoi ne pas simplement faire [renvoyer une seule entrée avec un index au lieu d'une liste? Pourquoi voudriez-vous jamais qu'une liste de longueur 1 soit renvoyée?
wordsforhewise
4
@wordsforthewise, lors de la programmation, vous pouvez avoir un vecteur de longueur non définie que vous souhaitez utiliser pour l'indexation. Le fait de [toujours renvoyer une liste signifie que vous obtenez la même classe de sortie pour, x[v]quelle que soit la longueur de v. Par exemple, on peut vouloir lapplyplus d' un sous - ensemble d'une liste: lapply(x[v], fun). Si [la liste des vecteurs de longueur un était supprimée, cela retournerait une erreur chaque fois qu'elle va une longueur un.
Axeman
1
Je pense que cela explique plus clairement, adv-r.had.co.nz/Subsetting.html
The Red Pea
171

Les différences significatives entre les deux méthodes sont la classe des objets qu'ils retournent lorsqu'ils sont utilisés pour l'extraction et s'ils peuvent accepter une plage de valeurs, ou juste une seule valeur pendant l'affectation.

Prenons le cas de l'extraction de données dans la liste suivante:

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

Disons que nous aimerions extraire la valeur stockée par bool de foo et l'utiliser dans une if()instruction. Cela illustrera les différences entre les valeurs de retour de []et [[]]quand elles sont utilisées pour l'extraction de données. La []méthode renvoie des objets de la liste de classes (ou data.frame si foo était un data.frame) tandis que la [[]]méthode retourne des objets dont la classe est déterminée par le type de leurs valeurs.

Ainsi, l'utilisation de la []méthode donne les résultats suivants:

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

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

En effet, la []méthode a renvoyé une liste et une liste n'est pas un objet valide à passer directement dans une if()instruction. Dans ce cas, nous devons utiliser [[]]car il retournera l'objet "nu" stocké dans 'bool' qui aura la classe appropriée:

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

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

La deuxième différence est que l' []opérateur peut être utilisé pour accéder à une plage de créneaux dans une liste ou des colonnes dans une trame de données tandis que l' [[]]opérateur est limité à l'accès à un seul créneau ou colonne. Considérons le cas de l'attribution de valeur à l'aide d'une deuxième liste bar():

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

Disons que nous voulons écraser les deux derniers emplacements de foo avec les données contenues dans la barre. Si nous essayons d'utiliser l' [[]]opérateur, voici ce qui se passe:

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

En effet, [[]]est limité à l'accès à un seul élément. Nous devons utiliser []:

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

$str
[1] "R"

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

$bool
[1] -0.6291121

Notez que bien que l'affectation ait réussi, les emplacements de foo ont conservé leur nom d'origine.

Sharpie
la source
111

Les doubles crochets accèdent à un élément de la liste , tandis qu'un simple crochet vous donne une liste avec un seul élément.

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

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

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

De Hadley Wickham:

De Hadley Wickham

Ma modification (merde) pour montrer en utilisant tidyverse / purrr:

entrez la description de l'image ici

jzadra
la source
2
Cool! Vous avez quelques picosecondes de Grace Hopper !
Steve Pitchers
@StevePitchers hein?
jzadra
Grace Hopper sur Letterman, démontrant nano secondes dailymotion.com/video/x35dsz7 .
Steve Pitchers
48

[]extrait une liste, [[]]extrait des éléments de la liste

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
la source
18

Il suffit d'ajouter ici qui [[est également équipé pour l' indexation récursive .

Cela a été laissé entendre dans la réponse de @JijoMatthew mais n'a pas été exploré.

Comme indiqué dans ?"[[", la syntaxe comme x[[y]], où length(y) > 1, est interprétée comme:

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

Notez que cela ne change pas ce qui devrait être votre principal point de vue sur la différence entre [et [[- à savoir, que le premier est utilisé pour le sous- ensemble , et le dernier est utilisé pour extraire des éléments de liste unique.

Par exemple,

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

Pour obtenir la valeur 3, nous pouvons faire:

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

Pour en revenir à la réponse de @ JijoMatthew ci-dessus, rappelez-vous r:

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

En particulier, cela explique les erreurs que nous avons tendance à obtenir lors d'une mauvaise utilisation [[, à savoir:

r[[1:3]]

Erreur dans r[[1:3]]: l'indexation récursive a échoué au niveau 2

Étant donné que ce code a réellement essayé d'évaluer r[[1]][[2]][[3]]et l'imbrication des rarrêts au niveau un, la tentative d'extraction par indexation récursive a échoué [[2]], c'est-à-dire au niveau 2.

Erreur dans r[[c("foo", "far")]]: indice hors limites

Ici, R recherchait r[["foo"]][["far"]], ce qui n'existe pas, nous obtenons donc l'erreur d'indice hors limites.

Il serait probablement un peu plus utile / cohérent si ces deux erreurs transmettaient le même message.

MichaelChirico
la source
Bonjour monsieur Micheal, pouvons-nous utiliser [[]] pour plusieurs indexations ??
Therii
14

Les deux sont des moyens de sous-ensemble. La parenthèse unique renverra un sous-ensemble de la liste, qui en soi sera une liste. c'est-à-dire: il peut ou non contenir plus d'un élément. D'un autre côté, un double crochet ne renverra qu'un seul élément de la liste.

-Le support unique nous donnera une liste. Nous pouvons également utiliser un seul crochet si nous souhaitons retourner plusieurs éléments de la liste. considérer la liste suivante: -

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

Veuillez maintenant noter la façon dont la liste est renvoyée lorsque j'essaie de l'afficher. Je tape r et appuie sur entrée

>r

#the result is:-

[[1]]

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

$foo

[1] 1

$far

[1] 2

Maintenant, nous allons voir la magie de la parenthèse simple: -

>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

ce qui est exactement le même que lorsque nous avons essayé d'afficher la valeur de r à l'écran, ce qui signifie que l'utilisation d'un seul crochet a renvoyé une liste, où à l'index 1, nous avons un vecteur de 10 éléments, puis nous avons deux autres éléments avec des noms foo et loin. Nous pouvons également choisir de donner un seul index ou nom d'élément en entrée à la parenthèse unique. par exemple:

> r[1]

[[1]]

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

Dans cet exemple, nous avons donné un index "1" et en retour obtenu une liste avec un élément (qui est un tableau de 10 nombres)

> r[2]

$foo

[1] 1

Dans l'exemple ci-dessus, nous avons donné un index "2" et en retour obtenu une liste avec un élément

> r["foo"];

$foo

[1] 1

Dans cet exemple, nous avons passé le nom d'un élément et en retour une liste a été retournée avec un élément.

Vous pouvez également passer un vecteur de noms d'éléments comme: -

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

> r[x];

$foo

[1] 1

$far
[1] 2

Dans cet exemple, nous avons passé un vecteur avec deux noms d'éléments "foo" et "far"

En retour, nous avons obtenu une liste avec deux éléments.

En bref, la parenthèse simple vous renverra toujours une autre liste avec un nombre d'éléments égal au nombre d'éléments ou au nombre d'indices que vous passez dans la parenthèse simple.

En revanche, un double crochet ne renverra toujours qu'un seul élément. Avant de passer au double crochet, une note à garder à l'esprit. 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.

Je vais citer quelques exemples. Veuillez noter les mots en gras et y revenir une fois que vous avez terminé avec les exemples ci-dessous:

Le double crochet vous renverra la valeur réelle à l'index (il ne retournera PAS une liste)

  > r[[1]]

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


  >r[["foo"]]

    [1] 1

pour les crochets doubles si nous essayons de visualiser plusieurs éléments en passant un vecteur, cela entraînera une erreur simplement parce qu'il n'a pas été construit pour répondre à ce besoin, mais simplement pour renvoyer un seul élément.

Considérer ce qui suit

> 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
la source
1
Voté parce que "passer un vecteur ... entraînera une erreur simplement parce qu'il n'a pas été conçu pour répondre à ce besoin" est incorrect; voir ma nouvelle réponse.
MichaelChirico
1
Voté parce qu'il fait de fortes affirmations comme "TANT QU'UN DOUBLE SUPPORT NE RETOURNERA JAMAIS UNE LISTE". Ce n'est pas vrai - si nous avons un objet qui est une liste de listes, le double crochet retournera une autre liste.
dabsingh
Le fait que []renvoie une classe de liste même s'il ne s'agit que d'un seul chiffre est très peu intuitif. Ils auraient dû créer une autre syntaxe comme ([])pour la liste et [[]]accéder à l'élément réel est très bien. Je préfère penser [[]]à la valeur brute comme dans d'autres langues.
TokyoToo
13

Pour aider les débutants à naviguer dans le brouillard manuel, il peut être utile de voir la [[ ... ]]notation comme une fonction de réduction - en d'autres termes, c'est quand vous voulez simplement `` obtenir les données '' d'un vecteur, d'une liste ou d'un bloc de données nommé. Il est conseillé de le faire si vous souhaitez utiliser les données de ces objets pour les calculs. Ces exemples simples vont illustrer.

(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]]

Donc, à partir du troisième exemple:

> 2 * x[1]
  x
1 2
> 2 * x[[1]]
[1] 2
Redfoot
la source
1
En tant que débutant, j'ai trouvé utile dans les 3 affectations à x (en utilisant "<-") de remplacer x = 1 par w = 1 pour éviter toute confusion avec le x qui est la cible de "<-"
user36800
Bien que très simple, j'aime vraiment cette explication. Une autre démonstration simple: iris[[1]]retourne un vecteur, alors iris[1]retourne un data.frame
stevec
11

Étant terminologique, l' [[opérateur extrait l'élément d'une liste tandis que l' [opérateur prend un sous - ensemble d'une liste.

submartingale
la source
7

Pour un autre cas d'utilisation concret, utilisez des crochets doubles lorsque vous souhaitez sélectionner un bloc de données créé par la split()fonction. Si vous ne le savez pas, split()regroupe une liste / trame de données en sous-ensembles en fonction d'un champ clé. Il est utile si vous souhaitez opérer sur plusieurs groupes, les tracer, 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"
Peter
la source
-1

Veuillez vous référer à l'explication détaillée ci-dessous.

J'ai utilisé la trame de données intégrée dans R, appelée 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 ... 
           ............

La ligne supérieure du tableau est appelée en-tête qui contient les noms des colonnes. Chaque ligne horizontale indique ensuite une ligne de données, qui commence par le nom de la ligne, puis est suivie par les données réelles. Chaque membre de données d'une ligne est appelé une cellule.

opérateur "[]" entre crochets simples

Pour récupérer des données dans une cellule, nous entrerions ses coordonnées de ligne et de colonne dans l'opérateur "[]" entre crochets. Les deux coordonnées sont séparées par une virgule. En d'autres termes, les coordonnées commencent par la position de la ligne, puis sont suivies d'une virgule et se terminent par la position de la colonne. L'ordre est important.

Par exemple 1: - Voici la valeur de la cellule de la première ligne, deuxième colonne de mtcars.

> mtcars[1, 2] 
[1] 6

Par exemple 2: - En outre, nous pouvons utiliser les noms de ligne et de colonne au lieu des coordonnées numériques.

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

Opérateur "[[]]" à double crochet

Nous référençons une colonne de trame de données avec l'opérateur "[[]]" entre crochets doubles.

Par exemple 1: - Pour récupérer le vecteur de la neuvième colonne de l'ensemble de données intégré mtcars, nous écrivons mtcars [[9]].

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

Par exemple 2: - Nous pouvons récupérer le même vecteur de colonne par son nom.

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

Prasan Karunarathna
la source
-1

En plus:

Suivez le LIEN de la RÉPONSE ici.

Voici un petit exemple abordant le point suivant:

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
la source