R Language
Operatori di condotte (%>% e altri)
Ricerca…
introduzione
Gli operatori di pipe, disponibili in magrittr
, dplyr
e altri pacchetti R, elaborano un oggetto dati utilizzando una sequenza di operazioni passando il risultato di un passaggio come input per il passaggio successivo utilizzando gli operatori infissi anziché il metodo R più tipico di nidificato chiamate di funzione.
Si noti che lo scopo previsto degli operatori di pipe è aumentare la leggibilità umana del codice scritto. Vedere la sezione Note per considerazioni sulle prestazioni.
Sintassi
lhs%>% rhs # sintassi del pipe per
rhs(lhs)
lhs%>% rhs (a = 1) # sintassi della pipe per
rhs(lhs, a = 1)
lhs%>% rhs (a = 1, b =.) # sintassi della pipe per
rhs(a = 1, b = lhs)
lhs% <>% rhs # sintassi del pipe per
lhs <- rhs(lhs)
lhs% $% rhs (a) # sintassi pipe
with(lhs, rhs(lhs$a))
lhs% T>% rhs # sintassi del pipe per
{ rhs(lhs); lhs }
Parametri
LHS | RHS |
---|---|
Un valore o il segnaposto magrittr. | Una chiamata di funzione usando la semantica magrittr |
Osservazioni
Pacchetti che usano %>%
L'operatore di pipe è definito nel pacchetto magrittr
, ma ha ottenuto enorme visibilità e popolarità con il pacchetto dplyr
(che importa la definizione da magrittr
). Ora fa parte di tidyverse
, che è una raccolta di pacchetti che "funzionano in armonia perché condividono rappresentazioni di dati comuni e design API" .
Il pacchetto magrittr
fornisce inoltre diverse varianti dell'operatore del tubo per coloro che desiderano una maggiore flessibilità nelle tubazioni, come il tubo di assegnazione composto %<>%
, il tubo di esposizione %$%
e l'operatore tee %T>%
. Fornisce anche una suite di funzioni alias per sostituire le funzioni comuni che hanno una sintassi speciale ( +
, [
, [[
, ecc.) In modo che possano essere facilmente utilizzate all'interno di una catena di pipe.
Trovare documentazione
Come con qualsiasi operatore infisso (come +
, *
, ^
, &
, %in%
), puoi trovare la documentazione ufficiale se la inserisci tra virgolette ?'%>%'
O help('%>%')
( assumendo che tu abbia caricato un pacchetto che allega pkg:magrittr
).
Tasti di scelta rapida
C'è un hotkey speciale in RStudio per l'operatore di pipe: Ctrl+Shift+M
( Windows e Linux ), Cmd+Shift+M
( Mac ).
Considerazioni sulle prestazioni
Mentre l'operatore di tubazioni è utile, occorre tenere presente che l'impatto negativo sulla prestazione è dovuto principalmente al sovraccarico dell'utilizzo. Considerare le seguenti due cose attentamente quando si utilizza l'operatore di tubi:
- Prestazioni della macchina (loop)
- Valutazione (
object %>% rm()
non rimuove l'object
)
Uso di base e concatenamento
L'operatore di pipe, %>%
, viene utilizzato per inserire un argomento in una funzione. Non è una funzione di base della lingua e può essere utilizzato solo dopo aver allegato un pacchetto che lo fornisce, come ad esempio magrittr
. L'operatore del tubo prende il lato sinistro (LHS) del tubo e lo usa come primo argomento della funzione sul lato destro (RHS) del tubo. Per esempio:
library(magrittr)
1:10 %>% mean
# [1] 5.5
# is equivalent to
mean(1:10)
# [1] 5.5
La pipe può essere utilizzata per sostituire una sequenza di chiamate di funzione. Tubi multipli ci permettono di leggere e scrivere la sequenza da sinistra a destra, piuttosto che dall'interno verso l'esterno. Ad esempio, supponiamo di avere years
definiti come fattore, ma vogliamo convertirlo in numerico. Per evitare possibili perdite di informazioni, prima convertiamo in carattere e poi in numerico:
years <- factor(2008:2012)
# nesting
as.numeric(as.character(years))
# piping
years %>% as.character %>% as.numeric
Se non vogliamo che LHS (Left Hand Side) sia usato come primo argomento sul RHS (lato destro), ci sono soluzioni alternative, come la denominazione degli argomenti o l'uso .
per indicare dove va l'input inviato.
# example with grepl
# its syntax:
# grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
# note that the `substring` result is the *2nd* argument of grepl
grepl("Wo", substring("Hello World", 7, 11))
# piping while naming other arguments
"Hello World" %>% substring(7, 11) %>% grepl(pattern = "Wo")
# piping with .
"Hello World" %>% substring(7, 11) %>% grepl("Wo", .)
# piping with . and curly braces
"Hello World" %>% substring(7, 11) %>% { c(paste('Hi', .)) }
#[1] "Hi World"
#using LHS multiple times in argument with curly braces and .
"Hello World" %>% substring(7, 11) %>% { c(paste(. ,'Hi', .)) }
#[1] "World Hi World"
Sequenze funzionali
Data una sequenza di passaggi che utilizziamo ripetutamente, è spesso utile memorizzarla in una funzione. I pipe consentono di salvare tali funzioni in un formato leggibile avviando una sequenza con un punto come in:
. %>% RHS
Ad esempio, supponiamo di avere date dei fattori e di voler estrarre l'anno:
library(magrittr) # needed to include the pipe operators
library(lubridate)
read_year <- . %>% as.character %>% as.Date %>% year
# Creating a dataset
df <- data.frame(now = "2015-11-11", before = "2012-01-01")
# now before
# 1 2015-11-11 2012-01-01
# Example 1: applying `read_year` to a single character-vector
df$now %>% read_year
# [1] 2015
# Example 2: applying `read_year` to all columns of `df`
df %>% lapply(read_year) %>% as.data.frame # implicit `lapply(df, read_year)
# now before
# 1 2015 2012
# Example 3: same as above using `mutate_all`
library(dplyr)
df %>% mutate_all(funs(read_year))
# if an older version of dplyr use `mutate_each`
# now before
# 1 2015 2012
Possiamo esaminare la composizione della funzione digitandone il nome o utilizzando le functions
:
read_year
# Functional sequence with the following components:
#
# 1. as.character(.)
# 2. as.Date(.)
# 3. year(.)
#
# Use 'functions' to extract the individual functions.
Possiamo anche accedere a ciascuna funzione in base alla sua posizione nella sequenza:
read_year[[2]]
# function (.)
# as.Date(.)
In generale, questo approccio può essere utile quando la chiarezza è più importante della velocità.
Assegnazione con% <>%
Il pacchetto magrittr
contiene un operatore infisso di assegnazione composta, %<>%
, che aggiorna un valore prima di collegarlo a una o più espressioni rhs
e quindi assegnare il risultato. Ciò elimina la necessità di digitare un nome oggetto due volte (una volta su ciascun lato dell'operatore di assegnazione <-
). %<>%
deve essere il primo operatore infisso in una catena:
library(magrittr)
library(dplyr)
df <- mtcars
Invece di scrivere
df <- df %>% select(1:3) %>% filter(mpg > 20, cyl == 6)
o
df %>% select(1:3) %>% filter(mpg > 20, cyl == 6) -> df
L'operatore di assegnazione composto eseguirà il pipe e riassegnerà df
:
df %<>% select(1:3) %>% filter(mpg > 20, cyl == 6)
Esposizione di contenuti con% $%
L'operatore del tubo di esposizione, %$%
, espone i nomi delle colonne come simboli R all'interno dell'oggetto a sinistra nell'espressione a destra. Questo operatore è utile quando si collegano le funzioni che non hanno un argomento di data
(diversamente da, ad esempio, lm
) e che non accettano nomi di dati e di colonne come argomenti (la maggior parte delle funzioni principali di dplyr).
L'operatore di pipe dell'esposizione %$%
consente a un utente di evitare la rottura di una pipeline quando è necessario fare riferimento ai nomi di colonna. Ad esempio, supponiamo che desideri filtrare un data.frame e quindi eseguire un test di correlazione su due colonne con cor.test
:
library(magrittr)
library(dplyr)
mtcars %>%
filter(wt > 2) %$%
cor.test(hp, mpg)
#>
#> Pearson's product-moment correlation
#>
#> data: hp and mpg
#> t = -5.9546, df = 26, p-value = 2.768e-06
#> alternative hypothesis: true correlation is not equal to 0
#> 95 percent confidence interval:
#> -0.8825498 -0.5393217
#> sample estimates:
#> cor
#> -0.7595673
Qui la pipe %>%
standard passa da data.frame a filter()
, mentre la pipe %$%
espone i nomi delle colonne a cor.test()
.
La pipe dell'esposizione funziona come una versione in pipe della base R with()
funzioni with()
, e gli stessi oggetti di sinistra sono accettati come input.
Usando la pipa con dplyr e ggplot2
L'operatore %>%
può anche essere usato per reindirizzare l'output di dplyr in ggplot. Ciò crea una pipeline di analisi dei dati esplorativi unificata (EDA) facilmente personalizzabile. Questo metodo è più veloce di fare le aggregazioni internamente in ggplot e ha il vantaggio aggiuntivo di evitare variabili intermedie non necessarie.
library(dplyr)
library(ggplot)
diamonds %>%
filter(depth > 60) %>%
group_by(cut) %>%
summarize(mean_price = mean(price)) %>%
ggplot(aes(x = cut, y = mean_price)) +
geom_bar(stat = "identity")
Creazione di effetti collaterali con% T>%
Alcune funzioni in R producono un effetto collaterale (ad es. Salvataggio, stampa, stampa, ecc.) E non sempre restituiscono un valore significativo o desiderato.
%T>%
(operatore tee) consente di inoltrare un valore in una funzione di produzione di effetti collaterali mantenendo intatto il valore originale di lhs
. In altre parole: l'operatore tee funziona come %>%
, tranne che i valori restituiti sono lhs
e non il risultato della funzione / espressione rhs
.
Esempio: crea, convoglia, scrive e restituisce un oggetto. Se in questo esempio sono stati utilizzati %>%
al posto di %T>%
, la variabile all_letters
conterrà NULL
anziché il valore dell'oggetto ordinato.
all_letters <- c(letters, LETTERS) %>%
sort %T>%
write.csv(file = "all_letters.csv")
read.csv("all_letters.csv") %>% head()
# x
# 1 a
# 2 A
# 3 b
# 4 B
# 5 c
# 6 C
Avvertenza: Piping di un oggetto senza nome per save()
produrrà un oggetto denominato .
quando caricato nell'area di lavoro con load()
. Tuttavia, è possibile una soluzione alternativa utilizzando una funzione di supporto (che può anche essere scritta in linea come funzione anonima).
all_letters <- c(letters, LETTERS) %>%
sort %T>%
save(file = "all_letters.RData")
load("all_letters.RData", e <- new.env())
get("all_letters", envir = e)
# Error in get("all_letters", envir = e) : object 'all_letters' not found
get(".", envir = e)
# [1] "a" "A" "b" "B" "c" "C" "d" "D" "e" "E" "f" "F" "g" "G" "h" "H" "i" "I" "j" "J"
# [21] "k" "K" "l" "L" "m" "M" "n" "N" "o" "O" "p" "P" "q" "Q" "r" "R" "s" "S" "t" "T"
# [41] "u" "U" "v" "V" "w" "W" "x" "X" "y" "Y" "z" "Z"
# Work-around
save2 <- function(. = ., name, file = stop("'file' must be specified")) {
assign(name, .)
call_save <- call("save", ... = name, file = file)
eval(call_save)
}
all_letters <- c(letters, LETTERS) %>%
sort %T>%
save2("all_letters", "all_letters.RData")