R Language
Röroperatörer (%>% och andra)
Sök…
Introduktion
Röroperatörer, tillgängliga i magrittr
, dplyr
och andra R-paket, bearbetar ett dataobjekt med hjälp av en sekvens av operationer genom att lämna resultatet av ett steg som ingång för nästa steg med infix-operatörer snarare än den mer typiska R-metoden för att kapsla funktion samtal.
Observera att det avsedda syftet med röroperatörer är att öka den mänskliga läsbarheten för skriftlig kod. Se avsnittet Kommentarer för resultathänsyn.
Syntax
lhs%>% rhs # rörsyntax för
rhs(lhs)
lhs%>% rhs (a = 1) # rörsyntax för
rhs(lhs, a = 1)
lhs%>% rhs (a = 1, b =.) # rörsyntax för
rhs(a = 1, b = lhs)
lhs% <>% rhs #
lhs <- rhs(lhs)
förlhs <- rhs(lhs)
lhs% $% rhs (a) # rörsyntax för
with(lhs, rhs(lhs$a))
lhs% T>% rhs # rörsyntax för
{ rhs(lhs); lhs }
parametrar
lhs | rhs |
---|---|
Ett värde eller den magrittr platshållaren. | Ett funktionssamtal med magrittr semantik |
Anmärkningar
Paket som använder %>%
magrittr
definieras i magrittr
paketet, men den fick stor synlighet och popularitet med dplyr
paketet (som importerar definitionen från magrittr
). Nu är det en del av tidyverse
, som är en samling paket som "fungerar i harmoni eftersom de delar gemensamma datarepresentationer och API-design" .
magrittr
paketet tillhandahåller också flera varianter av röroperatören för dem som vill ha mer flexibilitet i rörledningar, såsom sammansatt tilldelningsrör %<>%
, exponeringsröret %$%
och teeoperatören %T>%
. Den tillhandahåller också en serie aliasfunktioner för att ersätta vanliga funktioner som har speciell syntax ( +
, [
, [[
, etc.) så att de enkelt kan användas i en rörkedja.
Hitta dokumentation
Som med alla infixoperatörer (som +
, *
, ^
, &
, %in%
), kan du hitta den officiella dokumentationen om du lägger den i offert ?'%>%'
Eller help('%>%')
( förutsatt att du har laddat ett paket som bifogar pkg:magrittr
).
Snabbtangenter
Det finns en speciell snabbtangent i RStudio för röroperatören : Ctrl+Shift+M
( Windows & Linux ), Cmd+Shift+M
( Mac ).
Resultathänsyn
Medan röroperatören är användbar, ska du vara medveten om att det har en negativ inverkan på prestanda beroende främst på omkostnaderna för att använda den. Tänk på följande två saker noggrant när du använder röroperatören:
- Maskinprestanda (slingor)
- Utvärdering (
object %>% rm()
tar inte bortobject
)
Grundläggande användning och kedja
Röroperatören, %>%
, används för att infoga ett argument i en funktion. Det är inte en basfunktion i språket och kan bara användas efter att ha anslutit ett paket som tillhandahåller det, till exempel magrittr
. Röroperatören tar vänster sida (LHS) på röret och använder det som det första argumentet för funktionen på höger sida (RHS) för röret. Till exempel:
library(magrittr)
1:10 %>% mean
# [1] 5.5
# is equivalent to
mean(1:10)
# [1] 5.5
Röret kan användas för att ersätta en sekvens av funktionssamtal. Flera rör tillåter oss att läsa och skriva sekvensen från vänster till höger, snarare än inifrån och ut. Anta till exempel att vi har years
definierade som en faktor men vill konvertera den till en siffra. För att förhindra eventuell informationsförlust konverterar vi först till tecken och sedan till numeriskt:
years <- factor(2008:2012)
# nesting
as.numeric(as.character(years))
# piping
years %>% as.character %>% as.numeric
Om vi inte vill att LHS (vänster sida) används som det första argumentet på RHS (höger sida) finns det lösningar, till exempel att namnge argumenten eller använda .
för att indikera var den rörliga ingången går.
# 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"
Funktionella sekvenser
Med tanke på en sekvens av steg vi använder upprepade gånger är det ofta praktiskt att lagra det i en funktion. Rör gör det möjligt att spara sådana funktioner i ett läsbart format genom att starta en sekvens med en punkt som i:
. %>% RHS
Anta som exempel att vi har faktordatum och vill extrahera året:
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
Vi kan granska funktionens sammansättning genom att skriva dess namn eller använda functions
:
read_year
# Functional sequence with the following components:
#
# 1. as.character(.)
# 2. as.Date(.)
# 3. year(.)
#
# Use 'functions' to extract the individual functions.
Vi kan också komma åt varje funktion genom dess position i sekvensen:
read_year[[2]]
# function (.)
# as.Date(.)
I allmänhet kan denna metod vara användbar när tydlighet är viktigare än hastighet.
Uppdrag med% <>%
magrittr
paketet innehåller en sammansatt tilldelningsinfix-operatör, %<>%
, som uppdaterar ett värde genom att först leda det till ett eller flera rhs
uttryck och sedan tilldela resultatet. Detta eliminerar behovet av att skriva ett objektnamn två gånger (en gång på varje sida av tilldelningsoperatören <-
). %<>%
måste vara den första infix-operatören i en kedja:
library(magrittr)
library(dplyr)
df <- mtcars
Istället för att skriva
df <- df %>% select(1:3) %>% filter(mpg > 20, cyl == 6)
eller
df %>% select(1:3) %>% filter(mpg > 20, cyl == 6) -> df
Operatören av sammansatt tilldelning kommer både att röra och tilldela df
:
df %<>% select(1:3) %>% filter(mpg > 20, cyl == 6)
Exponering av innehåll med% $%
Exponeringsrörsoperatören, %$%
, exponerar kolumnnamnen som R-symboler i det vänstra objektet för högeruttrycket. Denna operatör är praktiskt när rörsystem i funktioner som inte har en data
argument (till skillnad från, säg, lm
) och som inte tar en data.frame och kolumnnamn som argument (de flesta av de viktigaste dplyr funktioner).
Exponeringsrörsoperatören %$%
tillåter en användare att undvika att bryta en pipeline när han behöver referera till kolumnnamn. Säg till exempel att du vill filtrera ett data.frame och sedan köra ett korrelationstest på två kolumner med 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
Här passerar standard %>%
-röret data.frame genom att filter()
, medan %$%
-röret exponerar cor.test()
för cor.test()
.
Exponeringsröret fungerar som en rörledningsbar version av basen R with()
-funktioner, och samma objekt på vänster sida accepteras som ingångar.
Använd röret med dplyr och ggplot2
Operatören %>%
kan också användas för att leda dplyr-utgången till ggplot. Detta skapar en enhetlig EDA-pipeline som är lätt att anpassa. Denna metod är snabbare än att göra aggregeringar internt i ggplot och har den extra fördelen att undvika onödiga mellanvariabler.
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")
Skapa biverkningar med% T>%
Vissa funktioner i R ger en bieffekt (dvs. spara, skriva ut, plotta, etc.) och ger inte alltid ett meningsfullt eller önskat värde.
%T>%
(tee-operatör) låter dig vidarebefordra ett värde till en sidoeffekt-producerande funktion och samtidigt hålla det ursprungliga lhs
värdet intakt. Med andra ord: tee-operatören fungerar som %>%
, förutom att lhs
är lhs
själv, och inte resultatet av rhs
funktionen / uttrycket.
Exempel: Skapa, rör, skriva och returnera ett objekt. Om %>%
användes i stället för %T>%
i detta exempel, skulle variabeln all_letters
innehålla NULL
snarare än värdet på det sorterade objektet.
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
Varning: Pipning av ett namn som inte heter för att save()
ger ett namn som heter .
när den laddas i arbetsområdet med load()
. En lösning med hjälp av en hjälpfunktion är dock möjlig (som också kan skrivas inline som en anonym funktion).
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")