4 Subsetting
4.1 Introdução
Neste capítulo, estudaremos como extrair ou modificar um ou mais elementos de um objeto. Esse capítulo está escrito de forma mais enxuta que os capítulos anteriores.
Para os exemplos, considere:
4.2 Operador [
O operador [ é utilizado após um vetor x[...], para selecionar um subconjunto de seus elementos. O conteúdo em ... pode tomar seis formas.
4.2.1 [ Com Vetores Atômicos
Seis ‘insumos’ para selecionar com [:
A1. Inteiros positivos: retornam elementos nas posições:
-
x[1] #> 1.3. -
x[1:3] #> 1.3 7.4 6.0. -
x[c(1,3)] #> 1.3 6.0.
A2. Inteiros negativos: retornam todos os elementos menos os das posições.
x[-c(1,3)] #> 7.4 5.9
Obs: decimais são truncados x[1.7] == x[1], e factors ‘viram’ inteiros.
A3. Vetores lógicos: retornam os elementos nas posições onde tem-se TRUE’s.
-
x[c(TRUE, TRUE, FALSE, FALSE)] #> 1.3 7.4.
A4. Vetores de texto: retornam os elementos com os nomes escolhidos.
-
c(a = 1, b = 2, c = 3)[c("a", "c")] #> 1 3.
Reciclagem: para relacionar vetores de tamanhos diferentes, o R multiplica o tamanho do menor, se for múltiplo do maior: x[c(TRUE, FALSE)].
Reciclagem no R é outro exemplo da linguagem tentando facilitar sua vida, mas genrando resultados inesperados.
Quando necessário, explicitamente alinhe vetores de tamanhos diferentes. A única exceção intuitiva é quando o vetor menor tem tamanho 1.
A5. Nada: x[] retorna o vetor original.
- É útil para alterar o conteúdo de um objeto, mas sem sobrescrever a “caixa ao redor”, isto é, sem alterar os atributos.
A6. Zero: retorna um vetor de tamanho 0.
Note que 1. e 4. podem reordear um vetor x[c(1, 3, 2)], bem como retornar um único elemento.
4.2.2 [ Com Matrizes e Arrays
Com matrizes (vetores com dimensão), existem três ‘métodos’:
M1. Múltiplos vetores: um vetor para cada dimensão:
-
m[c(TRUE, FALSE, TRUE), c("B", "A")]. - Um vetor omisso retorna todos os ‘elementos da dimensão’:
a[, c(1, 3)]. - Para arrays, use um argumento para cada dimensão
a[1:2, 2, , 4].
M2. Vetor único: como matrizes são vetores, podemos tratá-las como tal:
-
m[5:7]do elemento 5 até o 7 (a ordem é “column-major”). - Podemos usar um vetor lógico aqui. E, como dimensão é um mero meta-dado, uma matriz lógica.
M3. Matrizes seletoras: onde cada coluna é uma dimensão, e cada linha um elemento quisto:
-
m[rbind(c(1,1), c(3,1), c(2,4))]. - Para arrays, precisamos de três ou mais colunas.
4.2.3 [ Com Listas e Dataframes
Com listas, não temos nenhuma diferença. [ sempre retorna uma lista, mesmo ao selecionar um único elemento.
E para data frames? O que df[1:2] deve retornar?
E df[1:2, ]? É “natural” que esses métodos coexistam?
Com dataframes, que são uma ‘lista com cara de matriz’, ambos os métodos existem.
4.2.4 Operador [ e Simplificação
O operador [ simplifica a dimensão do resultado.
Em matrizes/arrays, dimensões de length 1 são desfeitas: m[1,], e m[,1]. Ou também m[1:9].
Ou ao selecionar uma única coluna de um dataframe no ‘modo matriz’ df[..., 2]. Mas selecionar linhas, mais de uma coluna, ou pelo ‘modo elemento’, mantêm a dimensão.
Isso é controlado pelo argumento drop = TRUE do operador. Podemos alterá-lo: df[1, , drop = FALSE]. Tibbles tem drop = FALSE por padrão, sempre retornando outra tibble/nunca simplificando (alá a preguiça).
4.3 Operador [[
Lembre que, em listas, [ sempre retorna uma lista – retorna o(s) elemento(s) e a ‘estrutura em volta’.
Para retornar apenas um (único) elemento, usamos os operadores [[...]] ou $....
Qual deve ser o resultado de x[1] == x[[1]]? Porque?
Pode ser útil usar [[ mesmo assim?
4.3.1 [[ Com Listas
Dois ‘insumos’ para selecionar com [[:
L1. Um escalar inteiro: selecionando o elemento pelo índide.
L2. Um escalar string: selecionando pelo nome.
No segundo caso, podemos usar a abreviação l$a, no lugar de l[["a"]].
Obs:
- Selecionar com um vetor seleciona recursivamente na lista,
x[[c(1, 2)]]é similar àx[[1]][[2]]. -
$faz matching parcial nos nomes, useoptions(warnPartialMatchDollar = TRUE).
4.4 Seleções Erradas
E se selecionarmos um elemento que não existe? A resposta é suco de R:
| x | fun | -Int OOB | +Int OOB | Chr OOB | NA |
0-length |
|---|---|---|---|---|---|---|
| Atomic | [ |
x |
NA |
NA |
N-length NA
|
0-length |
| List | [ |
x |
NULL |
NULL |
N-length NULL
|
0-length |
| Atomic | [[ |
Error | Error | Error | Error | Error |
| List | [[ |
Error | Error | NULL |
NULL |
Error |
| NULL |
[[/[
|
NULL |
NULL |
NULL |
NULL |
NULL |
Obs: o comportamento é o mesmo com ’x’s de tamanho zero.
Não é importante decorar essa matriz, mas pense sobre:
-
[é mais permissivo, retorna “nada” em vez de um erro.- Com -Int, retorna
x, faz sentido. - O que pode representar “nada” em listas? E em atômicos?
- Com -Int, retorna
-
[[é menos permissivo, retorna erro em vez de nada.- Mas com listas, temos duas exceções.
- O que significa selecionar com
NA? Em qual dos tipos de conteúdo ele se encaixa?
Para lidar com essas inconsistências, existem para [: purrr::keep_at(), vctrs::vec_slice; e para [[: purrr::pluck(), purrr::chuck().
4.5 Assingment
Todos os operadores de subset podem ser combinados com <-: x[i] <- ....
x[[i]] <- NULL remove o item de uma lista, x[i] <- list(NULL) adiciona um elemento NULL.
Subsetar com nada seleciona o ‘conteúdo’ de um objeto, e pode ser combinado com assignment para manter a estrutura inalterada:
df[] <- lapply(df, as.integer) # df se mantem um dataframe
df <- lapply(df, as.integer) # df vira uma listaComplemento
Recapitulando
Seis ‘insumos’ para selecionar com [:
A1. Inteiros positivos: retornam elementos nas posições.
A2. Inteiros negativos: retornam todos os elementos menos os das posições.
A3. Vetores lógicos: retornam os elementos nas posições onde tem-se TRUE’s.
A4. Vetores de texto: retornam os elementos com os nomes escolhidos.
A5. Nada: x[] retorna o vetor original.
A6. Zero: retorna um vetor de tamanho 0.
Reciclagem: para relacionar vetores de tamanhos diferentes, o R multiplica o tamanho do menor, se for múltiplo do maior: x[c(TRUE, FALSE)].
Três ‘métodos’ para selecionar com dimensões:
M1. Múltiplos vetores: um vetor para cada dimensão. M2. Vetor único: como matrizes são vetores, podemos tratá-las como tal. M3. Matrizes seletoras: onde cada coluna é uma dimensão, e cada linha um elemento quisto.
Com listas e dataframes:
- Com listas, não temos nenhuma diferença.
[sempre retorna uma lista, mesmo ao selecionar um único elemento. - Com dataframes, que são uma ‘lista com cara de matriz’, temos o método normal e o de matriz.
O operador [ simplifica a dimensão do resultado. Veja o argumento drop = TRUE.
Para retornar apenas um (único) elemento, usamos os operadores [[...]] ou $....
Dois ‘insumos’ para selecionar com [[:
L1. Um escalar inteiro: selecionando o elemento pelo índide. L2. Um escalar string: selecionando pelo nome.
Selecionar elementos que não existem está relacionado a comportamentos inconsistentes, as vezes retornando erros, NULL, ou NA.
-
[é mais permissivo, retorna “nada” em vez de um erro. Considere usarpurrr::keep_at()ouvctrs::vec_slice. -
[[é menos permissivo, retorna erro em vez de nada. Considere usarpurrr::pluck()epurrr::chuck().
Todos os operadores de subset podem ser combinados com <-: x[i] <- ....
Referências
Referência básica: Advanced R (2e) cap. 4.