# Using a (computed) function in DataFrames with multiple arguments

**URL:** <https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833>\
**Category:** Data\
**Tags:** macros, dataframes\
**Created:** [December 19, 2022, 7:01am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833 "2022-12-19T07:01:32Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![mreichMPI-BGC](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mreichmpi-bgc/32/43775_2.png) [@mreichMPI-BGC](https://discourse.julialang.org/u/mreichMPI-BGC)\
**Post date:** [December 19, 2022, 7:01am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/1 "2022-12-19T07:01:32Z")

</div>

In Julia I can do:

```
meanratio(x,y) = mean(x) / mean(y)

function calc(fun, vbl) 
  fun(vbl...)  
end

julia> x1=rand(10); x2=rand(10);calc(meanratio, (x1, x2))
0.8271083961061552

```

I want `fun` to be flexible in term of how many arguments. In my current use case fun gives back a single value, like here the ratio of two means.

Is there a way to do it in with `Dataframes`, ideally `DataframesMacros`? Obviously I want to apply this for GroupedDataFrames.

I tried somthing like:

```
@combine df {outvar} = fun({vbl}...)

```

but that did not work.

---

<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:** [December 19, 2022, 7:11am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/2 "2022-12-19T07:11:36Z")

</div>

> [@mreichMPI-BGC](#):
>
> I want `fun` to be flexible in term of how many arguments.

Is this what you want? `f` below is flexible in how many positional arguments it takes:

```julia
julia> f(x...) = x
f (generic function with 1 method)

julia> f(1)
(1,)

julia> f(1, 2)
(1, 2)

julia> f(1, 2, 3)
(1, 2, 3)

```

---

<div class="post-metadata">

**Author:** ![mreichMPI-BGC](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mreichmpi-bgc/32/43775_2.png) [@mreichMPI-BGC](https://discourse.julialang.org/u/mreichMPI-BGC)\
**Post date:** [December 19, 2022, 7:29am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/3 "2022-12-19T07:29:49Z")

</div>

Thanks - but that’s not what I meant. The point is the `fun` is passed as parameter. In plain Julia it all works.

E.g. I can define:

```
crazyRatio(x,y,z) = mean(x) / mean(y) / std(z)

```

and

```julia
julia> x1=rand(10); x2=rand(10); x3=rand(10); 
calc(crazyRatio, (x1, x2, x3))

```

would work (did not test now).  
So I have no “problem” in Base Julia, but to do it in Dataframes. Maybe it is trivial and I am blind…, but my naive

```julia
@combine df {outvar} = fun({vbl}...)

```

where `vbl` is a tuple of Symbols referring to columns.

… did not work.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 19, 2022, 8:30am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/4 "2022-12-19T08:30:54Z")

</div>

I think for this you need to use `{{ vbl }}` because that’s meant to splice a tuple of column values into the expression which you can then splat with `...`.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [December 19, 2022, 8:32am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/5 "2022-12-19T08:32:16Z")

</div>

I don’t understand:

> [@mreichMPI-BGC](#):
>
> The point is the `fun` is passed as parameter.

but then the code `@combine df {outvar} = fun({vbl}...)` does not pass `fun` as parameter…

It seems to me that the minilanguage in DataFrames.jl already works the way you want: you can specifiy a variable number of columns and the function will be called with this number of parameters. Is the following not flexible enough?

```julia
using DataFrames
using Statistics

crazyRatio(x,y,z) = mean(x) / mean(y) / std(z)

# Just to show that all these things can be passed as parameters
fun = crazyRatio
vbl = [:x1, :x2, :x3]
outvar = :y

df = DataFrame(x1=rand(10), x2=rand(10), x3=rand(10))

combine(df, vbl => fun => outvar)

# Output
1×1 DataFrame
 Row │ y       
     │ Float64 
─────┼─────────
   1 │ 7.65015

```

---

<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:** [December 19, 2022, 8:50am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/6 "2022-12-19T08:50:32Z")

</div>

> [@sijo](#):
>
> It seems to me that the minilanguage in DataFrames.jl already works the way you want

Exactly, that is why I thought OP wanted to learn how to define a vararg function.

---

<div class="post-metadata">

**Author:** ![mreichMPI-BGC](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mreichmpi-bgc/32/43775_2.png) [@mreichMPI-BGC](https://discourse.julialang.org/u/mreichMPI-BGC)\
**Post date:** [December 19, 2022, 9:45am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/7 "2022-12-19T09:45:01Z")

</div>

Thanks! … yes indeed - I was thinking too complicated (“new to Julia”). And was trying to do it with `DataFramesMacros`…

This is what I wanted:

```
function calc_df(df, vbl, gr, fun)

  combine(groupby(df, gr), vbl => fun => string(fun))
   # This DataframeMacros would not work this way
   #@chain df begin
   # @groupby {gr}
   # @combine {string(fun)} = fun({{vbl}})
   #end     
end

 df = DataFrame(x1=rand(20), x2=rand(20), x3=rand(20), grp = repeat('A':'D'; inner=5))
 calc_df(df, [:x1, :x2, :x3], :grp, crazyRatio)

4×2 DataFrame
 Row │ grp crazyRatio 
     │ Char Float64    
─────┼──────────────────
   1 │ A 1.55756
   2 │ B 3.45605
   3 │ C 5.95207
   4 │ D 2.23287

```

Still would be nice if it works with DataFramesMacros (@jules ? ) 🙂

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 19, 2022, 9:53am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/8 "2022-12-19T09:53:21Z")

</div>

> [@mreichMPI-BGC](#):
>
> ```julia
> @chain df begin
> # @groupby {gr}
> # @combine {string(fun)} = fun({{vbl}})
> #end  
> 
> ```

Without testing it, I think it would work with `@combine {string(fun)} = fun({{vbl}}...)`, so you splat the tuple resulting from `{{vbl}}` into `fun`. This is nice when you have to pass other parameters as well, for example `fun(first_arg, {{vbl}}...)` because then you cannot do `vbl => fun` in the minilanguage anymore, which is otherwise concise as well.

And I still think you don’t need to do `@combine {string(fun)} = ` but just `@combine string(fun) = ...` because the `{}` was supposed to mean something else on the left side. But I have to check the implementation again why this even works…

---

<div class="post-metadata">

**Author:** ![mreichMPI-BGC](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mreichmpi-bgc/32/43775_2.png) [@mreichMPI-BGC](https://discourse.julialang.org/u/mreichMPI-BGC)\
**Post date:** [December 19, 2022, 10:03am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/9 "2022-12-19T10:03:14Z")

</div>

Thanks! You are right on both points! I thought I had tried `{{vbl}}...`, and it did not work . But I does work.

So this is, what I initially intended:

```julia
function calc_df(df, vbl, gr, fun)

   @chain df begin
   @groupby {gr}
   @combine string(fun)= fun({{vbl}}...)
   #end     
end

crazyRatio(x,y,z) = mean(x) / mean(y) / std(z)

 df = DataFrame(x1=rand(20), x2=rand(20), x3=rand(20), grp = repeat('A':'D'; inner=5))
 calc_df(df, [:x1, :x2, :x3], :grp, crazyRatio)

```

Ah, now I remember why it did not work: passing a Tuple of symbols did not work, i.e.

```
 calc_df(df, (:x1, :x2, :x3), :grp, crazyRatio)

```

I am still lacking the needed intuition I guess…

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 19, 2022, 10:04am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/10 "2022-12-19T10:04:20Z")

</div>

That’s just because a tuple of symbols is not in the list of types I expect for column identifiers. Does `names(df, (:x, :y, :z))` work? If it does, I should add it too, if not, I won’t 🙂

---

<div class="post-metadata">

**Author:** ![mreichMPI-BGC](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mreichmpi-bgc/32/43775_2.png) [@mreichMPI-BGC](https://discourse.julialang.org/u/mreichMPI-BGC)\
**Post date:** [December 19, 2022, 10:24am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/11 "2022-12-19T10:24:46Z")

</div>

Doesn’t work

```
ERROR: MethodError: no method matching getindex(::DataFrames.Index, ::Tuple{Symbol, Symbol})

```

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 19, 2022, 10:26am UTC](https://discourse.julialang.org/t/using-a-computed-function-in-dataframes-with-multiple-arguments/91833/12 "2022-12-19T10:26:31Z")

</div>

Then that column specification is not supported by DataFrames.jl
