10.4 Miscelânea
Por fim, veremos algumas funções do purrr
que não têm exatamente a ver com
laços, mas que acabam sendo bastante úteis quando usando as funções que vimos
até agora. Elas não serão apresentadas em nenhuma ordem específica, este é apenas
um apanhado de funções sortidas que achamos úteis enquanto programando com o
purrr
.
10.4.1 Manter e descartar
Se quisermos filtrar elementos de um vetor ou lista, podemos usar as funções
keep()
e discard()
. Elas funcionam com fórmulas e podem ser extremamente úteis
em situações que dplyr::select()
e magrittr::extract()
não conseguem cobrir:
<- list(10:15, 20:25, c(30:34, NA))
obj keep(obj, ~any(is.na(.x)))
## [[1]]
## [1] 30 31 32 33 34 NA
discard(obj, ~!any(is.na(.x)))
## [[1]]
## [1] 30 31 32 33 34 NA
No exemplo acima descartamos todos os vetores da lista que não têm pelo menos um
elemento omisso (NA
).
10.4.2 A família is
Uma outra família do pacote purrr
é a is()
. Com essa série de funções podemos
fazer verificações extremamente estritas em objetos dos mais variados tipos. Seguem
alguns poucos exemplos:
is_scalar_integer(10:15)
## [1] FALSE
is_bare_integer(10:15)
## [1] TRUE
is_atomic(10:15)
## [1] TRUE
is_vector(10:15)
## [1] TRUE
10.4.3 Andar e modificar
walk()
e modify()
são pequenas alterações da família map()
que vêm a calhar
em diversas situações. A primeira destas funciona exatamente igual à map()
mas
não devolve resultado, apenas efeitos colaterais; a segunda, não muda a
estrutura do objeto sendo iterado, ela substitui os próprios elementos da entrada.
A maior utilidade de
walk
é quando precisamos salvar múltiplas tabelas. Para fazer isso, podemos usar algo comowalk(tabelas, readr::write_csv)
.
Um caso de uso interessante da modify()
é ao lado do sufixo _if()
,
combinação que nos permite iterar nas colunas de uma tabela e aplicar
transformações de tipo apenas quando um predicado for verdade (geralmente de
queremos transformar as colunas de fator para caractere).
10.4.4 Transposição e indexação profunda
Quando precisarmos lidar com listas complexas e profundas, o purrr
nos fornece
duas funções extremamente úteis: transpose()
e pluck()
. A primeira transpõe
uma lista, enquanto a segunda é capaz de acessar elementos profundos de uma lista
sem a necessidade de colchetes.
<- list(list(a = 1, b = 2, c = 3), list(a = 4, b = 5, c = 6))
obj str(obj)
## List of 2
## $ :List of 3
## ..$ a: num 1
## ..$ b: num 2
## ..$ c: num 3
## $ :List of 3
## ..$ a: num 4
## ..$ b: num 5
## ..$ c: num 6
pluck(obj, 2, "b")
## [1] 5
str(transpose(obj))
## List of 3
## $ a:List of 2
## ..$ : num 1
## ..$ : num 4
## $ b:List of 2
## ..$ : num 2
## ..$ : num 5
## $ c:List of 2
## ..$ : num 3
## ..$ : num 6
Obs.: Se você estiver com muitos problemas com listas profundas, dê uma olhada
nas funções relacionadas a depth()
pois elas podem ser muito úteis.
10.4.5 Aplicação parcial
Se quisermos pré-preencher os argumentos de uma função (seja para usá-la em uma
pipeline ou com alguma função do próprio purrr
), temos partial()
. Ela funciona
nos moldes da família invoke()
e pode ser bastante útil para tornar suas
pipelines mais enxutas:
<- function(x, y, z) { x + y + z }
soma_varios <- partial(soma_varios, x = 1, y = 2)
nova_soma nova_soma(3)
## [1] 6
10.4.6 Execução segura
Não é incomum executarmos uma função e recebermos um erro de volta. Isso pode ser
lidado com facilidade em um laço com um condicional, mas essa tarefa já é mais
complexa quando se trata de programação funcional. Para isso, no purrr
temos
algumas funções que embrulham uma função e, quando esta retornar um erro, o
silenciam e retornam um valor padrão em seu lugar.
quietly()
retorna uma lista com resultado, saída, mensagem e alertas, safely()
retorna uma lista com resultado e erro (um destes sempre é NULL
), e possibly()
silencia o erro e retorna um valor dado pelo usuário.
<- function(x) { x + 1 }
soma_um <- safely(soma_um, 0)
s_soma_um <- c(10, 11, "a", 13, 14, 15)
obj s_soma_um(obj)
## $result
## [1] 0
##
## $error
## <simpleError in x + 1: non-numeric argument to binary operator>