Menu

אופרטור ה-Pipe ב-R: %>% וה-|> המובנה

מה פירוש %>%, איך ה-pipe המובנה |> של R הבסיסי עובד, הכללים וממלאי המקום של כל אחד, ובאיזה מהם להשתמש בקוד חדש.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

הבעיה ש-pipes פותרים

pipe לוקח את הערך שלפניו ומעביר אותו לקריאת הפונקציה שאחריו: x |> f() פירושו f(x). זה נשמע כמו שכתוב טריוויאלי עד ששרשרים שלושה או ארבעה שלבים. בלי pipe, טרנספורמציות מרובות שלבים מתקננות, וקריאות מקוננות נקראות מהסוף להתחלה: הדבר הראשון שקורה יושב במיקום הפנימי ביותר.

כדי לקרוא את round(sqrt(x), 1) מתחילים באמצע (x), מתקדמים החוצה (sqrt, ואז round), ועוקבים איזה , 1) בסוף שייך לאיזו קריאה. בשלוש רמות עומק זה מעצבן; בחמש, עם פעלים של data frames וכמה ארגומנטים לכל אחד, באמת קשה לעקוב. החלופה שאנשים פונים אליה, לשמור כל שלב ביניים ב-tmp1, tmp2, tmp3, ממלאת את סביבת העבודה בשמות שאף אחד לא צריך.

pipe כותב מחדש את השרשרת בסדר שבו השלבים באמת קורים: קחו את x, ואז הוציאו לו שורש, ואז עגלו.

ה-pipe המובנה |>

מאז R 4.1, ה-pipe בנוי לתוך השפה עצמה, בלי צורך בחבילה. |> מכניס את הערך הקודם כארגומנט הראשון של הקריאה הבאה:

c(1, 4, 9, 16) |> sqrt() |> round(1) הוא בדיוק round(sqrt(c(1, 4, 9, 16)), 1): R ממש כותב אחד מהם מחדש לשני בזמן הניתוח, כך שאין שום תקורה בזמן ריצה. אבל עכשיו הקוד נקרא כמו מתכון, מלמעלה למטה: הנתונים, ואז כל טרנספורמציה לפי הסדר. זה עובד עם כל פונקציה, לא רק עם כלי נתונים: mtcars |> head(3) הוא R בסיסי מתחילתו ועד סופו.

שימו לב כמה טוב pipes מתאימים לפונקציות שהפרמטר הראשון שלהן הוא "הנתונים": head, sort, unique, summary וכל פועל של dplyr מתוכננים כך, ולכן pipelines מרגישים כל כך טבעיים ב-R.

הכללים של |>

ה-pipe המובנה נוקשה בכוונה. שלושה כללים מכסים כמעט הכול:

1. השלב הבא חייב להיות קריאה, עם סוגריים. x |> sqrt היא שגיאת תחביר; חייבים לכתוב x |> sqrt():

c(1, 4, 9) |> sqrt     # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt()   # correct

2. הערך נוחת במיקום הארגומנט הראשון. ארגומנטים נוספים שאתם כותבים נשארים במקומם אחריו: x |> round(1) הוא round(x, 1).

3. כדי לנחות במקום אחר, השתמשו בממלא המקום _ עם ארגומנט בעל שם (R 4.2+). המקרה הקלאסי: lm מקבלת קודם נוסחה ואחר כך את הנתונים, ולכן העברת data frame אליה ב-pipe צריכה data = _:

ה-_ יכול להופיע פעם אחת, ורק כארגומנט בעל שם. כשזה מגביל מדי, העבירו ב-pipe לפונקציה אנונימית, שיכולה לשים את הערך איפה שהיא רוצה:

ה-lambda עטופה בסוגריים ואז נקראת עם (), מה שממשיך לקיים את כלל 1. אם אתם מוצאים את עצמכם עושים את זה הרבה ב-pipeline אחד, השלב הזה כנראה רוצה להיות פונקציה בעלת שם.

ה-pipe של magrittr %>%

לפני שלשפה היה pipe, החבילה magrittr סיפקה אחד, ו-%>% הפך לחתימה של קוד tidyverse: טעינת dplyr נותנת לכם אותו אוטומטית. תראו אותו כמעט בכל מדריך ובכל סקריפט של dplyr שנכתבו בעשור האחרון:

library(dplyr)

mtcars %>%
  filter(mpg > 30) %>%
  select(mpg, wt)

מכיוון ש-%>% מגיע מחבילה, אי אפשר להריץ את הדוגמאות האלה בסשן R חשוף: Error: could not find function "%>%" הוא בדיוק מה שמקבלים אם שוכחים את library(dplyr) (או את library(magrittr)).

ההתנהגות במקרה הנפוץ זהה ל-|>: להעביר את הערך הקודם לארגומנט הראשון של הקריאה הבאה. אבל magrittr מתירני יותר:

c(1, 4, 9) %>% sqrt                 # bare function name - no parentheses needed

mtcars %>% lm(mpg ~ wt, data = .)   # . placeholder, usable anywhere

c(-2, 3) %>% { max(abs(.)) }        # . can even appear several times, in nested calls

|> מול %>%: ההבדלים שחשובים

ב-pipeline שעובד על הארגומנט הראשון, שהוא הרוב המכריע של קוד אמיתי, השניים ניתנים להחלפה. ההבדלים חיים בשוליים:

  • זמינות: |> הוא R בסיסי 4.1+; %>% צריך את magrittr (בדרך כלל דרך dplyr).
  • סוגריים: |> דורש sqrt(); %>% מקבל sqrt חשוף.
  • ממלא מקום: |> משתמש ב-_, פעם אחת, רק כארגומנט בעל שם (4.2+); %>% משתמש ב-., בכל מקום, כמה פעמים שרוצים, כולל בתוך קריאות מקוננות.
  • מהירות: |> נכתב מחדש בזמן הניתוח לקריאה המקוננת, בלי שום מחיר. %>% הוא קריאת פונקציה רגילה עם תקורה קטנה (זניחה בפועל).

שום דבר כאן לא הופך את %>% לשגוי: הוא נבדק היטב בשטח וגמיש מעט יותר. אבל כל מה שהוא עושה מעבר ל-|> אפשר לעשות עם lambda, וה-pipe הבסיסי לא צריך שום תלות.

באיזה מהם להשתמש?

לקוד חדש: השתמשו ב-|>. הוא חלק מהשפה, עובד בכל מקום שבו R 4.1+ רץ, לא צריך חבילה, והנוקשות שלו היא תכונה: pipelines נשארים פשוטים או עוברים ארגון מחדש לפונקציות בעלות שם.

אבל חייבים להיות מסוגלים לקרוא %>% בשטף, כי בסיסי קוד של tidyverse, תשובות ב-Stack Overflow ורוב ספרי ה-R שנכתבו מאז 2014 מלאים בו. צוותים עם סגנון dplyr קיים משאירים לעיתים קרובות את %>% לשם עקביות, וזו החלטה סבירה. הבחירה הגרועה ביותר היא לערבב את שני האופרטורים בקובץ אחד. בכל אחד שתכתבו, מוסכמה אחת עוברת: ב-pipelines ארוכים, שימו כל שלב בשורה משלו עם ה-pipe בסוף השורה, כך שהמתכון נקרא כרשימה של שלבים.

אותו טעם חל כאן כמו עם משפחת apply: pipes מיועדים לטרנספורמציות לינאריות, שלב אחרי שלב. כשחישוב מתפצל או צריך תוצאות ביניים פעמיים, הציבו משתנה עם שם טוב במקום לדחוס הכול לשרשרת אחת.

מה לקחת מכאן

  • pipe מעביר את הערך הקודם לקריאה הבאה: x |> f() |> g() הוא g(f(x)), כתוב בסדר שבו זה קורה.
  • |> הוא R בסיסי (4.1+): דורש סוגריים, מכוון לארגומנט הראשון, ממלא המקום _ עם ארגומנט בעל שם (4.2+).
  • %>% הוא magrittr/tidyverse: שמות חשופים מותרים, ממלא המקום . בכל מקום, אבל הוא צריך חבילה.
  • העדיפו |> בקוד חדש; קראו %>% בכל מקום אחר בלי למצמץ.
  • pipes מתאימים למתכונים לינאריים; לוגיקה מתפצלת ראויה למשתנים בעלי שם.

בהמשך: חבילות, המקור של %>%, של dplyr ושל 20,000 ההרחבות האחרות של R.

שאלות נפוצות

מה פירוש %>% ב-R?

%>% הוא ה-pipe של magrittr: הוא לוקח את הערך שלפניו ומעביר אותו כארגומנט הראשון של הקריאה שאחריו, כך ש-x %>% f() %>% g() פירושו g(f(x)). הוא מגיע מהחבילה magrittr ונטען אוטומטית עם dplyr וה-tidyverse; הוא אינו חלק מ-R הבסיסי.

האם צריך חבילה כדי להשתמש ב-|> ב-R?

לא. |> הוא ה-pipe המובנה, שנבנה לתוך R הבסיסי מאז גרסה 4.1: הוא עובד בסשן R רגיל בלי שום דבר מותקן. %>% הוא זה שצריך חבילה (magrittr, או כל חבילה שמייצאת אותו מחדש, כמו dplyr).

מה ההבדל בין |> ל-%>% ב-R?

שניהם מעבירים את הערך הקודם לקריאה הבאה. |> הוא R בסיסי, דורש סוגריים מפורשים (x |> sqrt()), ומשתמש בממלא המקום _ רק עם ארגומנט בעל שם. %>% צריך את magrittr, מקבל שמות פונקציות חשופים (x %>% sqrt), וממלא המקום . שלו יכול להופיע בכל מקום, כמה פעמים שרוצים. ההתנהגות זהה במקרה הנפוץ של הארגומנט הראשון.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל