# Creating Parameters.jl struct samples using variable keywords

**URL:** <https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639>\
**Category:** General Usage\
**Tags:** metaprogramming, parametersjl\
**Created:** [October 23, 2024, 1:12pm UTC](https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639 "2024-10-23T13:12:27Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![JPool](https://avatars.discourse-cdn.com/v4/letter/j/ebca7d/32.png) [@JPool](https://discourse.julialang.org/u/JPool)\
**Post date:** [October 23, 2024, 1:12pm UTC](https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639/1 "2024-10-23T13:12:27Z")

</div>

Hi, I’m trying to make a function that takes in a default struct which holds parameters for my model. I want to be able to make a sample list where some properties are kept constant, and some are sampled using a sampling algorithm. The properties/keywords I would like to change are defined in a vector of strings.

There is, as far as I know no simple way to input a string as key-word argument, so I used the Meta.parse and eval() function to convert a string to an expression, which when evaluated gives me the new object with the sampled parameters. I already felt that this is probably not ‘conventional’, but it worked.

A example can be seen below:

```julia
function create_samplelist(default::ParameterContainer, sample_params::Vector{String}, upper_bounds, lower_bounds; n_samples::Integer = 10)
    samples = typeof(default)[]
    # Make sure the lengths of parameters are all the same
    @assert length(sample_params) == length(upper_bounds) == length(lower_bounds)
    
    A = QuasiMonteCarlo.sample(n_samples, lower_bounds, upper_bounds, QuasiMonteCarlo.LatinHypercubeSample())

    for col in eachcol(A)
        #Build the expression string
        string = "$(typeof(default))(default, "
        for i in eachindex(col)
            string = string*"$(sample_params[i]) = $(col[i]),"
        end
        string = string*")"

        #Parse the string into an expression and add the evaluated expression to the sample list
        expr = Meta.parse(string)
        push!(samples, eval(expr))
    end

    return samples
end

# Define the variant, sample parameters to sample, and their upper&lower bounds
default = GridCell()
sample_params = ["growth_rate", "death_rate"]
upper_bounds = [0.1, 0.01]
lower_bounds = [0.0, 0.0]

# Create a sample list of GridCell to simulate for data fitting.
variant_samples = create_samplelist(default, sample_params, upper_bounds, lower_bounds)

```

This example works, until I put this function in a Module. Then I realized that eval() evaluates in the global scope, so it was using the default variable in the global scope the whole time instead of the one passed in the function (which is undefined in my Module, giving me an error)

I can always move the function back into the global scope to fix this problem, but I was wondering: Is there a better way, more conventional to do this?

---

<div class="post-metadata">

**Author:** ![JonasWickman](https://avatars.discourse-cdn.com/v4/letter/j/9de0a6/32.png) [@JonasWickman](https://discourse.julialang.org/u/JonasWickman)\
**Post date:** [October 23, 2024, 2:31pm UTC](https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639/2 "2024-10-23T14:31:52Z")

</div>

I can’t quite follow all of this, as I’m not familiar with the packages you are using, but this is almost certainly, as you suspected, not a good way to this. If I understand you correctly, you would like to make something like the following call:

```julia
typeof(default)(default; growth_rate = A[j,1], death_rate = A[j,2])

```

or something similar to it, but you have `growth_rate` and `death_rate` as strings. If you instead declare

```julia
sample_params = [:growth_rate, :death_rate]

```

you could inside your function do something like

```julia
#[...]
for col in eachcol(A)
      nt = (; (sample_params[i] => col[i] for i in eachindex(col))...)
      res = typeof(default)(default; nt...)
      push!(samples, res)
end
#[...]

```

Here, `nt` is a NamedTuple, which can be splatted (using `...`) into the keyword-argument slot of a function following a semicolon in the function call. If you need to use strings as inputs you can convert string to symbols using, e.g.,

```julia
sym_params = Symbol.(["growth_rate", "death_rate"])

```

I can’t check that the above works, but something along these lines should be better than what you’ve got.

Hopefully someone more familiar with the packages you’re using can come along and suggest an overall better design for your purposes.

---

<div class="post-metadata">

**Author:** ![JPool](https://avatars.discourse-cdn.com/v4/letter/j/ebca7d/32.png) [@JPool](https://discourse.julialang.org/u/JPool)\
**Post date:** [October 23, 2024, 3:25pm UTC](https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639/3 "2024-10-23T15:25:57Z")

</div>

Thank you so much, it works! Parameters.jl as far as I know works using named tuples, so this works perfectly!

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 23, 2024, 3:54pm UTC](https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639/4 "2024-10-23T15:54:14Z")

</div>

> [@JPool](#):
>
> The properties/keywords I would like to change are defined in a vector of strings.

Is there a specific reason why you define properties by a vector of strings?  
IMO a clean solution is:

```julia
using AccessorsExtra

default = GridCell()
sample_params = [(@o _.growth_rate), (@o _.death_rate)]
params_combined = concat(sample_params...)

samples = map(eachcol(A)) do col
    setall(default, params_combined, col)
end

```

As a bonus, you can use lots of stuff in `sample_params`, not just plain properties. For example, all of `@o log(_.growth_rate)`, `@o _.optimizer.rate`, `@o _.death_rates[1]` would work.

---

<div class="post-metadata">

**Author:** ![JPool](https://avatars.discourse-cdn.com/v4/letter/j/ebca7d/32.png) [@JPool](https://discourse.julialang.org/u/JPool)\
**Post date:** [October 23, 2024, 5:06pm UTC](https://discourse.julialang.org/t/creating-parameters-jl-struct-samples-using-variable-keywords/121639/5 "2024-10-23T17:06:13Z")

</div>

There was no specific reason, it was just convenient at the time. I will probably use a vector of symbols from now on, or something similar, as suggested by JonasWickman.

Thank you for your code suggestion, but I’m not familiar with the concept of optics. I will consider it if I ever need more functionality.
