Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for Between, Not, All, Cols in @select #372

Merged
merged 1 commit into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/src/dplyr.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,22 +136,22 @@ Similarly, to select the first column, use the syntax `$1`.
@select msleep $1
```

To select all the columns *except* a specific column, use the `Not` function for inverse selection. We also need to wrap `Not` in the `$` sign, because it is not a symbol.
To select all the columns *except* a specific column, use the `Not` function for inverse selection.

```@repl 1
@select msleep $(Not(:name))
@select msleep Not(:name)
```

To select a range of columns by name, use the `Between` operator:

```@repl 1
@select msleep $(Between(:name, :order))
@select msleep Between(:name, :order)
```

To select all columns that start with the character string `"sl"` use [regular expressions](https://regexone.com/):
To select all columns that start with the character string `"sl"` use [regular expressions](https://regexone.com/) in conjunction with `Cols`.

```@repl 1
@select msleep $(r"^sl")
@select msleep Cols(r"^sl")
```

Regular expressions are powerful, but can be difficult for new users to understand. Here are some quick tips.
Expand Down
16 changes: 10 additions & 6 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@ but exported by DataFramesMeta for convenience.

# Provided macros

!!! note

Newer versions of DataFrames.jl support the operators `Between`, `All`, `Cols`,
and `Not` when selecting and transforming columns. DataFramesMeta does not currently
support this syntax.

## `@select` and `@select!`

Column selections and transformations. Only newly created columns are kept.
Expand All @@ -79,6 +73,16 @@ gd = groupby(df, :x);
@select!(gd, :y = 2 .* :y .* first(:y))
```

To select or de-select multiple columns, use `Not`, `Between`, `All`, and `Cols`.
These multi-column selectors are all re-exported from DataFrames.jl.

```julia
@select df Not(:x)
@select df Between(:x, :y)
@select df All()
@select df Cols(r"x") # Regular expressions.
```

## `@transform` and `@transform!`

Add additional columns based on keyword-like arguments. Operates on both a
Expand Down
24 changes: 22 additions & 2 deletions src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@

### Examples

```jldoctest

Check failure on line 863 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:863-954 ```jldoctest julia> using DataFramesMeta, Statistics julia> df = DataFrame(x = 1:3, y = [2, 1, 2]); julia> globalvar = [2, 1, 0]; julia> @subset(df, :x .> 1) 2×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 2 1 2 │ 3 2 julia> @subset(df, :x .> globalvar) 2×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 2 1 2 │ 3 2 julia> @subset df begin :x .> globalvar :y .== 3 end 0×2 DataFrame julia> df = DataFrame(n = 1:20, x = [3, 3, 3, 3, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 3, 1, 1, 2]); julia> g = groupby(df, :x); julia> @subset(g, :n .> mean(:n)) 8×2 DataFrame Row │ n x │ Int64 Int64 ─────┼────────────── 1 │ 12 1 2 │ 13 1 3 │ 15 2 4 │ 16 2 5 │ 17 3 6 │ 18 1 7 │ 19 1 8 │ 20 2 julia> @subset g begin :n .> mean(:n) :n .< 20 end 7×2 DataFrame Row │ n x │ Int64 Int64 ─────┼────────────── 1 │ 12 1 2 │ 13 1 3 │ 15 2 4 │ 16 2 5 │ 17 3 6 │ 18 1 7 │ 19 1 julia> df = DataFrame(a = [1, 2, missing], b = ["x", "y", missing]); julia> @subset(df, :a .== 1) 1×2 DataFrame Row │ a b │ Int64? String? ─────┼───────────────── 1 │ 1 x julia> @subset(df, :a .< 3; view = true) 2×2 SubDataFrame Row │ a b │ Int64? String? ─────┼───────────────── 1 │ 1 x 2 │ 2 y julia> @subset df begin :a .< 3 @kwarg view = true end 2×2 SubDataFrame Row │ a b │ Int64? String? ─────┼───────────────── 1 │ 1 x 2 │ 2 y ``` Subexpression: @subset df begin :x .> globalvar :y .== 3 end Evaluated output: 0×2 DataFrame Row │ x y │ Int64 Int64 ─────┴────────────── Expected output: 0×2 DataFrame diff = Warning: Diff output requires color. 0×2 DataFrameDataFrame Row │ x y │ Int64 Int64 ─────┴──────────────
julia> using DataFramesMeta, Statistics

julia> df = DataFrame(x = 1:3, y = [2, 1, 2]);
Expand Down Expand Up @@ -976,7 +976,7 @@
Use this function as an alternative to placing the `.` to broadcast row-wise operations.

### Examples
```jldoctest

Check failure on line 979 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:979-1009 ```jldoctest julia> using DataFramesMeta julia> df = DataFrame(A=1:5, B=["apple", "pear", "apple", "orange", "pear"]) 5×2 DataFrame Row │ A B │ Int64 String ─────┼─────────────── 1 │ 1 apple 2 │ 2 pear 3 │ 3 apple 4 │ 4 orange 5 │ 5 pear julia> @rsubset df :A > 3 2×2 DataFrame Row │ A B │ Int64 String ─────┼─────────────── 1 │ 4 orange 2 │ 5 pear julia> @rsubset df :A > 3 || :B == "pear" 3×2 DataFrame Row │ A B │ Int64 String ─────┼─────────────── 1 │ 2 pear 2 │ 4 orange 3 │ 5 pear ``` Subexpression: @rsubset df :A > 3 || :B == "pear" Evaluated output: 3×2 DataFrame Row │ A B │ Int64 String ─────┼─────────────── 1 │ 2 pear 2 │ 4 orange 3 │ 5 pear Expected output: 3×2 DataFrame Row │ A B │ Int64 String ─────┼─────────────── 1 │ 2 pear 2 │ 4 orange 3 │ 5 pear diff = Warning: Diff output requires color. 3×2 DataFrame DataFrame Row │ A B B │ Int64 String ─────┼─────────────── String ─────┼─────────────── 1 │ 2 pear pear 2 │ 4 orange orange 3 │ 5 pear
julia> using DataFramesMeta

julia> df = DataFrame(A=1:5, B=["apple", "pear", "apple", "orange", "pear"])
Expand Down Expand Up @@ -1128,7 +1128,7 @@

### Examples

```jldoctest

Check failure on line 1131 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:1131-1205 ```jldoctest julia> using DataFramesMeta, Statistics julia> df = DataFrame(x = 1:3, y = [2, 1, 2]); julia> globalvar = [2, 1, 0]; julia> @subset!(copy(df), :x .> 1) 2×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 2 1 2 │ 3 2 julia> @subset!(copy(df), :x .> globalvar) 2×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 2 1 2 │ 3 2 julia> @subset! copy(df) begin :x .> globalvar :y .== 3 end 0×2 DataFrame julia> df = DataFrame(n = 1:20, x = [3, 3, 3, 3, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 3, 1, 1, 2]); julia> g = groupby(copy(df), :x); julia> @subset!(g, :n .> mean(:n)) 8×2 DataFrame Row │ n x │ Int64 Int64 ─────┼────────────── 1 │ 12 1 2 │ 13 1 3 │ 15 2 4 │ 16 2 5 │ 17 3 6 │ 18 1 7 │ 19 1 8 │ 20 2 julia> g = groupby(copy(df), :x); julia> @subset! g begin :n .> mean(:n) :n .< 20 end 7×2 DataFrame Row │ n x │ Int64 Int64 ─────┼────────────── 1 │ 12 1 2 │ 13 1 3 │ 15 2 4 │ 16 2 5 │ 17 3 6 │ 18 1 7 │ 19 1 julia> d = DataFrame(a = [1, 2, missing], b = ["x", "y", missing]); julia> @subset!(d, :a .== 1) 1×2 DataFrame Row │ a b │ Int64? String? ─────┼───────────────── 1 │ 1 x ``` Subexpression: @subset! copy(df) begin :x .> globalvar :y .== 3 end Evaluated output: 0×2 DataFrame Row │ x y │ Int64 Int64 ─────┴────────────── Expected output: 0×2 DataFrame diff = Warning: Diff output requires color. 0×2 DataFrameDataFrame Row │ x y │ Int64 Int64 ─────┴──────────────
julia> using DataFramesMeta, Statistics

julia> df = DataFrame(x = 1:3, y = [2, 1, 2]);
Expand Down Expand Up @@ -1312,7 +1312,7 @@

### Examples

```jldoctest

Check failure on line 1315 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:1315-1387 ```jldoctest julia> using DataFramesMeta, Statistics julia> d = DataFrame(x = [3, 3, 3, 2, 1, 1, 1, 2, 1, 1], n = 1:10, c = ["a", "c", "b", "e", "d", "g", "f", "i", "j", "h"]); julia> @orderby(d, -:n) 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 10 h 2 │ 1 9 j 3 │ 2 8 i 4 │ 1 7 f 5 │ 1 6 g 6 │ 1 5 d 7 │ 2 4 e 8 │ 3 3 b 9 │ 3 2 c 10 │ 3 1 a julia> @orderby(d, invperm(sortperm(:c, rev = true))) 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 9 j 2 │ 2 8 i 3 │ 1 10 h 4 │ 1 6 g 5 │ 1 7 f 6 │ 2 4 e 7 │ 1 5 d 8 │ 3 2 c 9 │ 3 3 b 10 │ 3 1 a julia> @orderby d begin :x abs.(:n .- mean(:n)) end 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e 2 │ 1 6 f 3 │ 1 7 g 4 │ 1 9 i 5 │ 1 10 j 6 │ 2 4 d 7 │ 2 8 h 8 │ 3 3 c 9 │ 3 2 b 10 │ 3 1 a julia> @orderby d @byrow :x^2 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e 2 │ 1 6 f 3 │ 1 7 g 4 │ 1 9 i 5 │ 1 10 j 6 │ 2 4 d 7 │ 2 8 h 8 │ 3 1 a 9 │ 3 2 b 10 │ 3 3 c ``` Subexpression: @orderby d begin :x abs.(:n .- mean(:n)) end Evaluated output: 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 d 2 │ 1 6 g 3 │ 1 7 f 4 │ 1 9 j 5 │ 1 10 h 6 │ 2 4 e 7 │ 2 8 i 8 │ 3 3 b 9 │ 3 2 c 10 │ 3 1 a Expected output: 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e 2 │ 1 6 f 3 │ 1 7 g 4 │ 1 9 i 5 │ 1 10 j 6 │ 2 4 d 7 │ 2 8 h 8 │ 3 3 c 9 │ 3 2 b 10 │ 3 1 a diff = Warning: Diff output requires color. 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e d 2 │ 1 6 f g 3 │ 1 7 g f 4 │ 1 9 i j 5 │ 1 10 j h 6 │ 2 4 d e 7 │ 2 8 h i 8 │ 3 3 c b 9 │ 3 2 b c 10 │ 3 1 a

Check failure on line 1315 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:1315-1387 ```jldoctest julia> using DataFramesMeta, Statistics julia> d = DataFrame(x = [3, 3, 3, 2, 1, 1, 1, 2, 1, 1], n = 1:10, c = ["a", "c", "b", "e", "d", "g", "f", "i", "j", "h"]); julia> @orderby(d, -:n) 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 10 h 2 │ 1 9 j 3 │ 2 8 i 4 │ 1 7 f 5 │ 1 6 g 6 │ 1 5 d 7 │ 2 4 e 8 │ 3 3 b 9 │ 3 2 c 10 │ 3 1 a julia> @orderby(d, invperm(sortperm(:c, rev = true))) 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 9 j 2 │ 2 8 i 3 │ 1 10 h 4 │ 1 6 g 5 │ 1 7 f 6 │ 2 4 e 7 │ 1 5 d 8 │ 3 2 c 9 │ 3 3 b 10 │ 3 1 a julia> @orderby d begin :x abs.(:n .- mean(:n)) end 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e 2 │ 1 6 f 3 │ 1 7 g 4 │ 1 9 i 5 │ 1 10 j 6 │ 2 4 d 7 │ 2 8 h 8 │ 3 3 c 9 │ 3 2 b 10 │ 3 1 a julia> @orderby d @byrow :x^2 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e 2 │ 1 6 f 3 │ 1 7 g 4 │ 1 9 i 5 │ 1 10 j 6 │ 2 4 d 7 │ 2 8 h 8 │ 3 1 a 9 │ 3 2 b 10 │ 3 3 c ``` Subexpression: @orderby d @byrow :x^2 Evaluated output: 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 d 2 │ 1 6 g 3 │ 1 7 f 4 │ 1 9 j 5 │ 1 10 h 6 │ 2 4 e 7 │ 2 8 i 8 │ 3 1 a 9 │ 3 2 c 10 │ 3 3 b Expected output: 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e 2 │ 1 6 f 3 │ 1 7 g 4 │ 1 9 i 5 │ 1 10 j 6 │ 2 4 d 7 │ 2 8 h 8 │ 3 1 a 9 │ 3 2 b 10 │ 3 3 c diff = Warning: Diff output requires color. 10×3 DataFrame Row │ x n c │ Int64 Int64 String ─────┼────────────────────── 1 │ 1 5 e d 2 │ 1 6 f g 3 │ 1 7 g f 4 │ 1 9 i j 5 │ 1 10 j h 6 │ 2 4 d e 7 │ 2 8 h i 8 │ 3 1 a 9 │ 3 2 b c 10 │ 3 3 cb
julia> using DataFramesMeta, Statistics

julia> d = DataFrame(x = [3, 3, 3, 2, 1, 1, 1, 2, 1, 1], n = 1:10,
Expand Down Expand Up @@ -1407,7 +1407,7 @@
Use this function as an alternative to placing the `.` to broadcast row-wise operations.

### Examples
```jldoctest

Check failure on line 1410 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:1410-1447 ```jldoctest julia> using DataFramesMeta julia> df = DataFrame(x = [8,8,-8,7,7,-7], y = [-1, 1, -2, 2, -3, 3]) 6×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 8 -1 2 │ 8 1 3 │ -8 -2 4 │ 7 2 5 │ 7 -3 6 │ -7 3 julia> @rorderby df abs(:x) (:x * :y^3) Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 7 -3 2 │ -7 3 3 │ 7 2 4 │ 8 -1 5 │ 8 1 6 │ -8 -2 julia> @rorderby df :y == 2 ? -:x : :y 6×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 7 2 2 │ 7 -3 3 │ -8 -2 4 │ 8 -1 5 │ 8 1 6 │ -7 3 ``` Subexpression: @rorderby df abs(:x) (:x * :y^3) Evaluated output: 6×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 7 -3 2 │ -7 3 3 │ 7 2 4 │ 8 -1 5 │ 8 1 6 │ -8 -2 Expected output: Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 7 -3 2 │ -7 3 3 │ 7 2 4 │ 8 -1 5 │ 8 1 6 │ -8 -2 diff = Warning: Diff output requires color. 6×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 7 -3 2 │ -7 3 3 │ 7 2 4 │ 8 -1 5 │ 8 1 6 │ -8 -2
julia> using DataFramesMeta

julia> df = DataFrame(x = [8,8,-8,7,7,-7], y = [-1, 1, -2, 2, -3, 3])
Expand Down Expand Up @@ -1786,7 +1786,7 @@
function select_helper(x, args...)
x, exprs, outer_flags, kw = get_df_args_kwargs(x, args...; wrap_byrow = false)

t = (fun_to_vec(ex; gensym_names = false, outer_flags = outer_flags) for ex in exprs)
t = (fun_to_vec(ex; gensym_names = false, outer_flags = outer_flags, allow_multicol = true) for ex in exprs)
quote
$select($x, $(t...); $(kw...))
end
Expand Down Expand Up @@ -1851,6 +1851,18 @@
beginning of a block of selections (i.e. `@byrow begin... end`).
All transformations in the block will operate by row.

To select many columns at once use the tools `Not`, `Between`, `All`, and `Cols`.

* `@select df Not(:a)` keeps all columns except for `:a`
* `@select df Between(:a, :z)` keeps all columns between `:a` and `:z`, inclusive
* `@select df All()` keeps all columns
* `@select df Cols(...)` can be used to combine many different selectors, as well as use
regular expressions. For example `Cols(r"a")` selects all columns that start with `"a"`.

Expressions inside `Not(...)`, `Between(...)` etc. are untouched by DataFramesMeta's
parsing. To refer to a variable `x` which represents a column inside `Not`, write `Not(x)`,
rather than `Not($(DOLLAR)x)`.

$ASTABLE_MACRO_FLAG_DOCS

$ASTABLE_RHS_SELECT_TRANSFORM_DOCS
Expand All @@ -1869,7 +1881,7 @@
```
@select gd begin
:a
@select copycols = false
@kwarg copycols = false
end
```

Expand Down Expand Up @@ -2024,6 +2036,14 @@
beginning of a block of select!ations (i.e. `@byrow begin... end`).
All transformations in the block will operate by row.

To select many columns at once use the tools `Not`, `Between`, `All`, and `Cols`.

* `@select df Not(:a)` keeps all columns except for `:a`
* `@select df Between(:a, :z)` keeps all columns between `:a` and `:z`, inclusive
* `@select df All()` keeps all columns
* `@select df Cols(...)` can be used to combine many different selectors, as well as use
regular expressions. For example `Cols(r"a")` selects all columns that start with `"a"`.

$ASTABLE_MACRO_FLAG_DOCS

$ASTABLE_RHS_SELECT_TRANSFORM_DOCS
Expand Down Expand Up @@ -2510,7 +2530,7 @@

### Examples

```jldoctest

Check failure on line 2533 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:2533-2553 ```jldoctest julia> using DataFramesMeta; julia> df = DataFrame(x = 1:10, y = 10:-1:1); julia> @distinct(df, :x .+ :y) 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼─────────────── 1 │ 1 10 julia> @distinct df begin :x .+ :y end 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼─────────────── 1 │ 1 10 ``` Subexpression: @distinct(df, :x .+ :y) Evaluated output: 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 1 10 Expected output: 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼─────────────── 1 │ 1 10 diff = Warning: Diff output requires color. 1×2 DataFrame Row │ x y y │ Int64 Int64 ─────┼─────────────── Int64 ─────┼────────────── 1 │ 1 1 10

Check failure on line 2533 in src/macros.jl

View workflow job for this annotation

GitHub Actions / build

doctest failure in ~/work/DataFramesMeta.jl/DataFramesMeta.jl/src/macros.jl:2533-2553 ```jldoctest julia> using DataFramesMeta; julia> df = DataFrame(x = 1:10, y = 10:-1:1); julia> @distinct(df, :x .+ :y) 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼─────────────── 1 │ 1 10 julia> @distinct df begin :x .+ :y end 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼─────────────── 1 │ 1 10 ``` Subexpression: @distinct df begin :x .+ :y end Evaluated output: 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼────────────── 1 │ 1 10 Expected output: 1×2 DataFrame Row │ x y │ Int64 Int64 ─────┼─────────────── 1 │ 1 10 diff = Warning: Diff output requires color. 1×2 DataFrame Row │ x y y │ Int64 Int64 ─────┼─────────────── Int64 ─────┼────────────── 1 │ 1 1 10
julia> using DataFramesMeta;

julia> df = DataFrame(x = 1:10, y = 10:-1:1);
Expand Down
27 changes: 21 additions & 6 deletions src/parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,29 @@ a `QuoteNode` or an expression beginning with
If input is not a valid column identifier,
returns `nothing`.
"""
get_column_expr(x) = nothing
function get_column_expr(e::Expr)
get_column_expr(x; allow_multicol::Bool = false) = nothing
function get_column_expr(e::Expr; allow_multicol::Bool = false)
e.head == :$ && return e.args[1]
onearg(e, :AsTable) && return :($AsTable($(e.args[2])))
if onearg(e, :cols)
Base.depwarn("cols is deprecated use $DOLLAR to escape column names instead", :cols)
return e.args[2]
end
if e.head === :call
e1 = e.args[1]
if e1 === :All || e1 === :Not || e1 === :Between || e1 == :Cols
if allow_multicol
return e
else
s = "Multi-column references outside of @select, @rselect, @select!" *
" and @rselect! must be wrapped in AsTable"
throw(ArgumentError(s))
end
end
end
return nothing
end
get_column_expr(x::QuoteNode) = x
get_column_expr(x::QuoteNode; allow_multicol::Bool = false) = x

get_column_expr_rename(x) = nothing
function get_column_expr_rename(e::Expr)
Expand Down Expand Up @@ -314,10 +326,12 @@ end
function fun_to_vec(ex::Expr;
gensym_names::Bool=false,
outer_flags::NamedTuple=deepcopy(DEFAULT_FLAGS),
no_dest::Bool=false)
no_dest::Bool=false,
allow_multicol::Bool=false)
# classify the type of expression
# :x # handled via dispatch
# $:x # handled as though above
# All(), Between(...), Cols(...), Not(...), requires allow_multicol (only true in select)
# f(:x) # requires no_dest, for `@with` and `@subset` in future
# :y = :x # Simple pair
# :y = $:x # Extract and return simple pair (no function)
Expand All @@ -342,7 +356,7 @@ function fun_to_vec(ex::Expr;
# :x
# handled below via dispatch on ::QuoteNode

ex_col = get_column_expr(ex)
ex_col = get_column_expr(ex; allow_multicol = allow_multicol)
if ex_col !== nothing
return ex_col
end
Expand Down Expand Up @@ -404,7 +418,8 @@ end
fun_to_vec(ex::QuoteNode;
no_dest::Bool=false,
gensym_names::Bool=false,
outer_flags::Union{NamedTuple, Nothing}=nothing) = ex
outer_flags::Union{NamedTuple, Nothing}=nothing,
allow_multicol::Bool = false) = ex


"""
Expand Down
12 changes: 4 additions & 8 deletions test/dataframes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,10 @@ s = [:i, :g]
# not part of DataFramesMeta.
@test_throws LoadError @eval @transform(df, [:i, :g])
@test_throws LoadError @eval @transform(df, All())
@test_throws LoadError @eval @transform(df, Between(:i, :t)).Between == df.i
@test_throws LoadError @eval @transform(df, Not(:i)).Not == df.i
@test_throws LoadError @eval @transform(df, Between(:i, :t))
@test_throws LoadError @eval @transform(df, Not(:i))
@test_throws LoadError @eval @transform(df, Not([:i, :g]))
@test_throws MethodError @eval @transform(df, :n = sum(Between(:i, :t)))
@test_throws LoadError @eval @transform(df, :n = sum(Between(:i, :t)))
end

@testset "@select" begin
Expand Down Expand Up @@ -546,11 +546,7 @@ cr = "c"
@testset "limits of @select" begin
## Test for not-implemented or strange behavior
@test_throws LoadError @eval @select(df, [:i, :g])
@test_throws LoadError @eval @select(df, All())
@test_throws LoadError @eval @select(df, Between(:i, :t)).Between == df.i
@test_throws LoadError @eval @select(df, Not(:i)).Not == df.i
@test_throws LoadError @eval @select(df, Not([:i, :g]))
@test_throws MethodError @eval @select(df, :n = sum(Between(:i, :t)))
@test_throws LoadError @eval @select(df, :n = sum(Between(:i, :t)))
end

@testset "with" begin
Expand Down
4 changes: 2 additions & 2 deletions test/grouping.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ gd = groupby(df, :g)
newvar = :n

@testset "Limits of @combine" begin
@test_throws MethodError @eval @combine(gd, :n = sum(Between(:i, :t)))
@test_throws LoadError @eval @combine(gd, :n = sum(Between(:i, :t)))
@test_throws ArgumentError @eval @combine(gd, :n = mean(:i) + mean(cols(1)))
end

Expand Down Expand Up @@ -287,7 +287,7 @@ gd = groupby(df, :g)
newvar = :n

@testset "limits of @by" begin
@test_throws MethodError @eval @by(df, :g, :n = sum(Between(:i, :t)))
@test_throws LoadError @eval @by(df, :g, :n = sum(Between(:i, :t)))
@test_throws ArgumentError @eval @by(df, :g, :n = mean(:i) + mean(cols(1)))
end

Expand Down
90 changes: 90 additions & 0 deletions test/multicol.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
module TestMultiCol

using Test
using DataFrames
using DataFramesMeta

df = DataFrame(A = 1, AA = 2, B = 3)

@testset "select_multi" begin
df = DataFrame(A = 1, AA = 2, B = 3)

t = @select df Not(:A)
@test t == DataFrame(AA = 2, B = 3)

t = @select df All()
@test t == DataFrame(A = 1, AA = 2, B = 3)

t = @select df Cols(r"A")
@test t == DataFrame(A = 1, AA = 2)

t = @select df Between(:AA, :B)
@test t == DataFrame(AA = 2, B = 3)
end

@testset "othermacros_multi" begin
df = DataFrame(A = 1, AA = 2, B = 3)

@test_throws LoadError @eval @with df Not(:A)

@test_throws LoadError @eval @with df All()

@test_throws LoadError @eval @with df Cols(r"A")

@test_throws LoadError @eval @with df Between(:AA, :B)

@test_throws LoadError @eval @with(df, begin
1
Not(:A)
end)

@test_throws LoadError @eval @with df begin
1
All()
end

@test_throws LoadError @eval @with df begin
1
Cols(r"A")
end

@test_throws LoadError @eval @with df begin
1
Between(:AA, :B)
end
end

@testset "othermacros_multi" begin
df = DataFrame(A = 1, AA = 2, B = 3)

@test_throws LoadError @eval @select df :y = Not(:A)

@test_throws LoadError @eval @select df :y = All()

@test_throws LoadError @eval @select df :y = Cols(r"A")

@test_throws LoadError @eval @select df :y = Between(:AA, :B)

@test_throws LoadError @eval @select(df, :y = begin
1
Not(:A)
end)

@test_throws LoadError @eval @select df :y = begin
1
All()
end

@test_throws LoadError @eval @select df :y = begin
1
Cols(r"A")
end

@test_throws LoadError @eval @select df :y = begin
1
Between(:AA, :B)
end
end


end # module
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ my_tests = ["dataframes.jl",
"byrow.jl",
"astable.jl",
"astable_flag.jl",
"passmissing.jl"]
"passmissing.jl",
"multicol.jl"]

println("Running tests:")

Expand Down
Loading