# Why doesn't this work with DataFrames.jl \`combine\`?

**URL:** https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549
**Category:** Data
**Created:** [July 30, 2021, 11:49am UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549 "2021-07-30T11:49:05Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [July 30, 2021, 11:49am UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/1 "2021-07-30T11:49:05Z")

</div>

This works

```julia
df = DataFrame(
        a = rand(1:8, 1000),
        b = rand(1:8, 1000),
        c = rand(1:8, 1000),
    )

combine(groupby(df, :a), nrow => :meh) # this works

function _meh(sdf)
  [sdf[!, Not(:a)]]
end

combine(groupby(df, :a), _meh) # this works

combine(groupby(df, :a), _meh => :meh) # this DOESN't

```

Why is this? Is this a bug?

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [July 30, 2021, 12:06pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/2 "2021-07-30T12:06:05Z")

</div>

> [@xiaodai](#):
>
> `combine(groupby(df, :a), _meh => :meh) # this DOESN't`

The `x => y` syntax (with only two arguments), as it is documented, is reserved for the case where `x` specifies source columns and `y` specifies transformations.

You try `x` to be a transformation and `y` to be target column name. Allowing this would lead to ambiguity.

When you read `x => y`, our position is, that must be clear what this expression means without evaluating `x` and `y` (in this way it is possible to decide what code will do statically which I think is an important thing to ensure).

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [July 30, 2021, 12:09pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/3 "2021-07-30T12:09:18Z")

</div>

Do

```julia
function _meh(sdf)
    (meh = [sdf[!, Not(:a)]],)
end

combine(groupby(df, :a), _meh) # this works

```

instead.

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [July 30, 2021, 12:42pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/4 "2021-07-30T12:42:21Z")

</div>

> [@xiaodai](#):
>
> `nrow => :meh`

I have forgotten to add that `nrow` is an exception to this rule, as `nrow` is a very common usage scenario, so we have decided that we can allow for an exception in this case.

---

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [July 30, 2021, 12:43pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/5 "2021-07-30T12:43:50Z")

</div>

> [@bkamins](#):
>
> exception in this case.

Ah, I thought the exception was the rule.

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [July 30, 2021, 12:48pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/6 "2021-07-30T12:48:58Z")

</div>

As you know docstring for `combine` has pages of text (as we cover so many scenarios). This is a special rule 5 in the list below (quoted from the docsting):

```julia
  All these functions take a specification of one or more functions to apply to each subset of the DataFrame. This specification can be of the following forms:

    1. standard column selectors (integers, Symbols, strings, vectors of integers, vectors of Symbols, vectors of strings, All, Cols, :, Between, Not and regular expressions)

    2. a cols => function pair indicating that function should be called with positional arguments holding columns cols, which can be any valid column selector; in this case target
       column name is automatically generated and it is assumed that function returns a single value or a vector; the generated name is created by concatenating source column name
       and function name by default (see examples below).

    3. a cols => function => target_cols form additionally explicitly specifying the target column or columns.

    4. a col => target_cols pair, which renames the column col to target_cols, which must be single name (as a Symbol or a string), a vector of names or AsTable.

    5. a nrow or nrow => target_cols form which efficiently computes the number of rows in a group; without target_cols the new column is called :nrow, otherwise it must be single
       name (as a Symbol or a string).

    6. vectors or matrices containing transformations specified by the Pair syntax described in points 2 to 5

    7. a function which will be called with a SubDataFrame corresponding to each group; this form should be avoided due to its poor performance unless the number of groups is small
       or a very large number of columns are processed (in which case SubDataFrame avoids excessive compilation)

```

Incidentally `nrow` even has a separate code path in implementation to ensure it is fast.

---

<div class="post-metadata">

### Author: ![hdavid16](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hdavid16/32/11531_2.png) [@hdavid16](https://discourse.julialang.org/u/hdavid16)
#### Post date: [August 3, 2021, 3:04pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/7 "2021-08-03T15:04:41Z")

</div>

Hi @bkamins, is there a way to get the following to work without having to convert the vector of user IDs into a string?

I want to group by `:id` and the list the number of users and the vector of user IDs for each `:id`. However, the `col => function => new_col` syntax isn’t working for me unless I convert the vector of user IDs into a `string`. I can always then parse the column of strings using `eval(Meta.parse.())`, but I was wondering if there is a cleaner solution.

Thanks!

```julia
julia> using DataFrames

julia> df = DataFrame(id = [1,1,1,2,2], user = [101,102,103,104,105])
5×2 DataFrame
 Row │ id user  
     │ Int64 Int64 
─────┼──────────────
   1 │ 1 101
   2 │ 1 102
   3 │ 1 103
   4 │ 2 104
   5 │ 2 105

julia> gdf = groupby(df, :id);

julia> df2 = combine(gdf, nrow => :number_of_users, :user => (x -> collect(x)) => :user_list)
5×3 DataFrame
 Row │ id number_of_users user_list 
     │ Int64 Int64 Int64     
─────┼───────────────────────────────────
   1 │ 1 3 101
   2 │ 1 3 102
   3 │ 1 3 103
   4 │ 2 2 104
   5 │ 2 2 105

julia> df2 = combine(gdf, nrow => :number_of_users, :user => (x -> string(collect(x))) => :user_list) 
2×3 DataFrame
 Row │ id number_of_users user_list       
     │ Int64 Int64 String
─────┼─────────────────────────────────────────
   1 │ 1 3 [101, 102, 103]
   2 │ 2 2 [104, 105]

```

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [August 3, 2021, 3:15pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/8 "2021-08-03T15:15:06Z")

</div>

You want `string.(collect(x))`. Remember the broadcasting.

Arrays can be converted to `string`, which is the behavior you are seeing here.

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [August 3, 2021, 3:15pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/9 "2021-08-03T15:15:14Z")

</div>

This is a non-copying approach:

```julia
julia> df2 = combine(gdf, nrow => :number_of_users, :user => Ref => :user_list)
2×3 DataFrame
 Row │ id number_of_users user_list
     │ Int64 Int64 SubArray…
─────┼─────────────────────────────────────────
   1 │ 1 3 [101, 102, 103]
   2 │ 2 2 [104, 105]

```

and this is with un-aliasing:

```julia
julia> df2 = combine(gdf, nrow => :number_of_users, :user => Ref∘copy => :user_list)
2×3 DataFrame
 Row │ id number_of_users user_list
     │ Int64 Int64 Array…
─────┼─────────────────────────────────────────
   1 │ 1 3 [101, 102, 103]
   2 │ 2 2 [104, 105]

```

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [August 3, 2021, 3:16pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/10 "2021-08-03T15:16:06Z")

</div>

@pdeffebach - I think @hdavid16 wants to produce a vector of vectors of original data.

---

<div class="post-metadata">

### Author: ![hdavid16](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hdavid16/32/11531_2.png) [@hdavid16](https://discourse.julialang.org/u/hdavid16)
#### Post date: [August 3, 2021, 3:30pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/11 "2021-08-03T15:30:47Z")

</div>

Awesome! Thank you @bkamins. What is the symbol between `Ref` and `copy` in the second example?

Also, is it possible to remove duplicates (`unique`) and `skipmissing` using this method?

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [August 3, 2021, 3:44pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/12 "2021-08-03T15:44:42Z")

</div>

> [@hdavid16](#):
>
> What is the symbol between `Ref` and `copy` in the second example?

it is function composition:

```julia
help?> ∘
"∘" can be typed by \circ<tab>

search: ∘

  f ∘ g

  Compose functions: i.e. (f ∘ g)(args...) means f(g(args...)). The ∘ symbol
  can be entered in the Julia REPL (and most editors, appropriately
  configured) by typing \circ<tab>.

```

You can do whatever you like. `Ref` just serves as a protection from broadcasting like in Julia Base.

---

<div class="post-metadata">

### Author: ![hdavid16](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hdavid16/32/11531_2.png) [@hdavid16](https://discourse.julialang.org/u/hdavid16)
#### Post date: [August 3, 2021, 3:46pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/13 "2021-08-03T15:46:05Z")

</div>

Thank you!

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [August 3, 2021, 3:46pm UTC](https://discourse.julialang.org/t/why-doesnt-this-work-with-dataframes-jl-combine/65549/14 "2021-08-03T15:46:35Z")

</div>

Yes. Just make an anonymous function which performs those operations as well, something like `x -> Ref(collect(skipmissing(unique(x)))`.
