|
| 1 | +# ## Native (and preferred) method of interpolating using `@eval` and `$` |
| 2 | + |
| 3 | +# TidierData relies on "non-standard evaluation," which has the side effect of making interpolation slightly more complicated. For example, in the expression `@mutate(df, a = b + 1)`, the `df` refers to a data frame, while `a` and `b` refer to column names within the data frame. What would happen if you created a variable `var` that contains the value `:a`. Would this interpolated expression work? |
| 4 | + |
| 5 | +# ```julia |
| 6 | +# using TidierData |
| 7 | +# df = DataFrame(a = 1:5, b = 6:10) |
| 8 | +# |
| 9 | +# var = :a |
| 10 | +# @mutate(df, $var = b + 1) |
| 11 | +# ``` |
| 12 | + |
| 13 | +# Unfortunately, this does not work because it produces `@mutate(df, :a = b + 1)`. Since TidierData uses bare variables (and not symbols) to refer to column names, this will result in an error. However, there is a slight modification we can apply to make this code work: prefixing it with an `@eval`. |
| 14 | + |
| 15 | +using TidierData |
| 16 | +df = DataFrame(a = 1:5, b = 6:10, c = 11:15) |
| 17 | + |
| 18 | +var = :a |
| 19 | +@eval @mutate(df, $var = b + 1) |
| 20 | + |
| 21 | +# ### Why does adding an `@eval` to the beginning of the expression make interpolation work? |
| 22 | + |
| 23 | +# Adding `@eval` to the beginning causes the interpolated expressions to be evaluated prior to be interpolated. So `$var`, which contains the value `:a`, is evaluated to `a`, which produces the desired expression `@mutate(df, a = b + 1)`. The need of `@eval` here then is primarily because TidierData expects an `a` rather than an `:a` to refer to the column "a" in a data frame. |
| 24 | + |
| 25 | +# ### How can I use `@eval` with a chained set of expressions? |
| 26 | + |
| 27 | +# The answer is simple: use `@eval @chain` instead of `@chain`. |
| 28 | + |
| 29 | +var = :a |
| 30 | + |
| 31 | +@eval @chain df begin |
| 32 | + @select($var) |
| 33 | + @mutate($var = $var + 1) |
| 34 | +end |
| 35 | + |
| 36 | +# If you want to select multiple variables, just use a `...` to splat the vector (or tuple) of variables. |
| 37 | + |
| 38 | +vars = [:a, :b] |
| 39 | + |
| 40 | +@eval @chain df begin |
| 41 | + @select($vars...) |
| 42 | +end |
| 43 | + |
| 44 | +# The `@eval`-based interpolation syntax is highly flexible in that it should work anywhere you might need it across the entire package. |
| 45 | + |
| 46 | +@eval @chain df begin |
| 47 | + @summarize(across($vars..., mean)) |
| 48 | +end |
| 49 | + |
| 50 | +# ### Does `@eval` work inside of user-defined functions? |
| 51 | + |
| 52 | +# Yes. Here's an example of how you could roll up a new `select_new` function wrapping the `@select` macros. |
| 53 | + |
| 54 | +function select_new(df, columns...) |
| 55 | + @eval @select(df, $columns...) |
| 56 | +end |
| 57 | + |
| 58 | +select_new(df, :a, :c) |
| 59 | + |
| 60 | +# Yes. Here's another example of an `add_one()` function that adds one to all numeric columns and returns the result in a new set of columns. |
| 61 | + |
| 62 | +function add_one(df) |
| 63 | + @eval @mutate(df, across(where(is_number), x -> x .+ 1)) |
| 64 | +end |
| 65 | + |
| 66 | +add_one(df) |
| 67 | + |
| 68 | +# ## Note: the below documentation is included here only for historical reasons. It will be removed in the future. |
| 69 | + |
| 70 | +# ## Superseded method of interpolating using the `!!` ("bang bang") operator |
| 71 | + |
1 | 72 | # The `!!` ("bang bang") operator can be used to interpolate values of variables from the parent environment into your code. This operator is borrowed from the R `rlang` package. At some point, we may switch to using native Julia interpolation, but for a variety of reasons that introduce some complexity with native interpolation, we plan to continue to support `!!` interpolation.
|
2 | 73 |
|
3 | 74 | # To interpolate multiple variables, the `rlang` R package uses the `!!!` "triple bang" operator. However, in `TidierData.jl`, the `!!` "bang bang" operator can be used to interpolate either single or multiple values as shown in the examples below.
|
|
0 commit comments