Skip to content

Commit b8dd3fa

Browse files
authored
More general constructors (#21)
1 parent 19020e7 commit b8dd3fa

9 files changed

Lines changed: 144 additions & 101 deletions

File tree

Project.toml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
name = "Observers"
22
uuid = "338f10d5-c7f1-4033-a7d1-f9dec39bcaa0"
33
authors = ["Giacomo Torlai <gttorlai@amazon.com>", "Matthew Fishman <mfishman@flatironinstitute.org>"]
4-
version = "0.2.2"
4+
version = "0.2.3"
55

66
[deps]
7-
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
87
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
9-
ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
10-
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
118
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
129

1310
[compat]
14-
Accessors = "0.1.28"
1511
Compat = "4.6.1"
16-
ConstructionBase = "1.5.1"
17-
DataAPI = "1.14.0"
1812
DataFrames = "1.5.0"
1913
julia = "1.6"

README.md

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ julia> ] add Observers
4545
## Basic Usage
4646

4747
```julia
48-
using Observers
48+
using Observers: Observers, observer
4949

5050
# Series for π/4
5151
f(k) = (-1)^(k + 1) / (2k - 1)
@@ -55,7 +55,7 @@ function my_iterative_function(niter; observer!, observe_step)
5555
for n in 1:niter
5656
π_approx += f(n)
5757
if iszero(n % observe_step)
58-
update!(observer!; iteration=n, π_approx=_approx)
58+
Observers.update!(observer!; iteration=n, π_approx=_approx)
5959
end
6060
end
6161
return _approx
@@ -336,7 +336,7 @@ You can also rename the columns to more desirable names using the `rename!`
336336
function from `DataFrames`:
337337

338338
```julia
339-
julia> using DataFrames
339+
julia> using DataFrames: rename!
340340

341341
julia> rename!(obs, ["Iteration", "Error"])
342342
10×2 DataFrame
@@ -394,23 +394,23 @@ for more details.
394394

395395

396396

397-
You can access and modify functions of an observer with `get_function`, `set_function!`, and `insert_function!`:
397+
You can access and modify functions of an observer with `Observers.get_function`, `Observers.set_function!`, and `Observers.insert_function!`:
398398

399399
```julia
400-
julia> get_function(obs, "Iteration") == iter
400+
julia> Observers.get_function(obs, "Iteration") == iter
401401
true
402402

403-
julia> get_function(obs, "Error") == err
403+
julia> Observers.get_function(obs, "Error") == err
404404
true
405405

406-
julia> set_function!(obs, "Error" => sin);
406+
julia> Observers.set_function!(obs, "Error" => sin);
407407

408-
julia> get_function(obs, "Error") == sin
408+
julia> Observers.get_function(obs, "Error") == sin
409409
true
410410

411-
julia> insert_function!(obs, "New column" => cos);
411+
julia> Observers.insert_function!(obs, "New column" => cos);
412412

413-
julia> get_function(obs, "New column") == cos
413+
julia> Observers.get_function(obs, "New column") == cos
414414
true
415415

416416
julia> obs
@@ -431,13 +431,13 @@ julia> obs
431431
```
432432

433433

434-
`set_function!` just updates the function of an existing column but doesn't create new columns,
435-
while `insert_function!` creates a new column and sets the function of that new column
434+
`Observers.set_function!` just updates the function of an existing column but doesn't create new columns,
435+
while `Observers.insert_function!` creates a new column and sets the function of that new column
436436
but won't update an existing column.
437437
For example, these will both throw errors:
438438
```julia
439-
set_function!(obs, "New column 2", cos)
440-
insert_function!(obs, "Error", cos)
439+
Observers.set_function!(obs, "New column 2", cos)
440+
Observers.insert_function!(obs, "Error", cos)
441441
```
442442

443443

@@ -514,7 +514,8 @@ Another option is saving and loading as a
514514
though this will drop information about the functions associated with each column:
515515

516516
```julia
517-
using CSV
517+
using CSV: CSV
518+
using DataFrames: DataFrame
518519
CSV.write("results.csv", obs)
519520
obs_loaded = DataFrame(CSV.File("results.csv"))
520521
```
@@ -537,8 +538,9 @@ This [README](https://github.com/GTorlai/Observers.jl#readme) file was generated
537538
[Weave.jl](https://github.com/JunoLab/Weave.jl) with the following commands:
538539

539540
```julia
540-
using Observers, Weave
541-
weave(
541+
using Observers: Observers
542+
using Weave: Weave
543+
Weave.weave(
542544
joinpath(pkgdir(Observers), "examples", "README.jl");
543545
doctype="github",
544546
out_path=pkgdir(Observers),

examples/Project.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[deps]
2+
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
3+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
4+
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
5+
Observers = "338f10d5-c7f1-4033-a7d1-f9dec39bcaa0"
6+
Weave = "44d3d7a6-8a23-5bf8-98c5-b353f8df5ec9"

examples/README.jl

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
#+ results="hidden"
3434

35-
using Observers
35+
using Observers: Observers, observer
3636

3737
# Series for π/4
3838
f(k) = (-1)^(k + 1) / (2k - 1)
@@ -42,7 +42,7 @@ function my_iterative_function(niter; observer!, observe_step)
4242
for n in 1:niter
4343
π_approx += f(n)
4444
if iszero(n % observe_step)
45-
update!(observer!; iteration=n, π_approx=_approx)
45+
Observers.update!(observer!; iteration=n, π_approx=_approx)
4646
end
4747
end
4848
return _approx
@@ -139,7 +139,7 @@ obs[!, Symbol(err)]
139139
#' You can also rename the columns to more desirable names using the `rename!`
140140
#' function from `DataFrames`:
141141
#+ term=true
142-
using DataFrames
142+
using DataFrames: rename!
143143
rename!(obs, ["Iteration", "Error"])
144144
obs.Iteration
145145
obs.Error
@@ -152,23 +152,23 @@ obs.Error
152152

153153
#' ## Accessing and modifying functions
154154

155-
#' You can access and modify functions of an observer with `get_function`, `set_function!`, and `insert_function!`:
155+
#' You can access and modify functions of an observer with `Observers.get_function`, `Observers.set_function!`, and `Observers.insert_function!`:
156156
#+ term=true
157-
get_function(obs, "Iteration") == iter
158-
get_function(obs, "Error") == err
159-
set_function!(obs, "Error" => sin);
160-
get_function(obs, "Error") == sin
161-
insert_function!(obs, "New column" => cos);
162-
get_function(obs, "New column") == cos
157+
Observers.get_function(obs, "Iteration") == iter
158+
Observers.get_function(obs, "Error") == err
159+
Observers.set_function!(obs, "Error" => sin);
160+
Observers.get_function(obs, "Error") == sin
161+
Observers.insert_function!(obs, "New column" => cos);
162+
Observers.get_function(obs, "New column") == cos
163163
obs
164164

165-
#' `set_function!` just updates the function of an existing column but doesn't create new columns,
166-
#' while `insert_function!` creates a new column and sets the function of that new column
165+
#' `Observers.set_function!` just updates the function of an existing column but doesn't create new columns,
166+
#' while `Observers.insert_function!` creates a new column and sets the function of that new column
167167
#' but won't update an existing column.
168168
#' For example, these will both throw errors:
169169
#' ```julia
170-
#' set_function!(obs, "New column 2", cos)
171-
#' insert_function!(obs, "Error", cos)
170+
#' Observers.set_function!(obs, "New column 2", cos)
171+
#' Observers.insert_function!(obs, "Error", cos)
172172
#' ```
173173

174174
#' Alternatively, if you define the observer with column names to begin with,
@@ -198,7 +198,8 @@ obs_loaded.Error == obs.Error
198198
#' [CSV file](https://dataframes.juliadata.org/stable/man/importing_and_exporting/#CSV-Files),
199199
#' though this will drop information about the functions associated with each column:
200200
#+ results="hidden"
201-
using CSV
201+
using CSV: CSV
202+
using DataFrames: DataFrame
202203
CSV.write("results.csv", obs)
203204
obs_loaded = DataFrame(CSV.File("results.csv"))
204205
#+ term=true
@@ -211,8 +212,9 @@ obs_loaded.Error == obs.Error
211212
#' [Weave.jl](https://github.com/JunoLab/Weave.jl) with the following commands:
212213
#+ eval=false
213214

214-
using Observers, Weave
215-
weave(
215+
using Observers: Observers
216+
using Weave: Weave
217+
Weave.weave(
216218
joinpath(pkgdir(Observers), "examples", "README.jl");
217219
doctype="github",
218220
out_path=pkgdir(Observers),

examples/example1.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Observers
1+
using Observers: observer
22

33
# Series for π/4
44
f(k) = (-1)^(k + 1) / (2k - 1)
@@ -8,7 +8,7 @@ function my_iterative_function(niter; observer!, observe_step)
88
for n in 1:niter
99
π_approx += f(n)
1010
if iszero(n % observe_step)
11-
update!(observer!; π_approx=_approx, iteration=n)
11+
Observers.update!(observer!; π_approx=_approx, iteration=n)
1212
end
1313
end
1414
return _approx

src/Observers.jl

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
11
module Observers
2-
32
export observer, update!, get_function, set_function!, insert_function!
43
# Deprecated
54
export results
6-
7-
using Accessors
8-
using Compat
9-
using ConstructionBase
10-
using DataFrames
11-
using DataAPI
12-
135
include("base/method_utils.jl")
146
include("abstractdataframe/column_functions.jl")
157
include("abstractdataframe/deprecated.jl")
168
include("dataframe/observer.jl")
179
include("dataframe/deprecated.jl")
18-
19-
end # module
10+
end

src/abstractdataframe/column_functions.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using Compat: @compat
2+
using DataFrames: AbstractDataFrame, colmetadata, colmetadata!, insertcols!
3+
14
# Function accessors interface
25
get_function(df::AbstractDataFrame, name) = colmetadata(df, name, "function")
36

src/dataframe/observer.jl

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,58 @@
1+
using DataFrames: DataFrame
2+
13
# Convenient constructors of DataFrames with functions as column metadata,
24
# called an "observer".
35

4-
observer() = DataFrame()
6+
const ColumnName = Union{Symbol,String}
7+
8+
function observer(names::Vector{<:ColumnName}, functions::Vector{<:Function}; kwargs...)
9+
df = DataFrame(map(name -> name => Union{}[], names); kwargs...)
10+
for (name, func) in zip(names, functions)
11+
set_function!(df, name, func)
12+
end
13+
return df
14+
end
515

6-
# In general, fall back to `DataFrame` constructors.
7-
observer(args...; kwargs...) = DataFrame(args...; kwargs...)
16+
function observer(
17+
names::Tuple{Vararg{ColumnName}}, functions::Tuple{Vararg{Function}}; kwargs...
18+
)
19+
return observer(collect(names), collect(functions); kwargs...)
20+
end
821

922
# Treat function column data as column metadata.
1023
# Default to empty columns with element type `Union{}`
1124
# so they get automatically promoted to the first type that gets pushed
1225
# into them.
13-
function observer(
14-
name_function_pairs::Vector{<:Pair{T,<:Function}}; kwargs...
15-
) where {T<:Union{Symbol,String}}
16-
df = DataFrame(
17-
[first(name_function) => Union{}[] for name_function in name_function_pairs]; kwargs...
18-
)
19-
name_function_dict = Dict(name_function_pairs)
20-
for name in keys(name_function_dict)
21-
set_function!(df, name, name_function_dict[name])
22-
end
23-
return df
26+
function observer(name_function_pairs::Vector{<:Pair{<:ColumnName,<:Function}}; kwargs...)
27+
return observer(first.(name_function_pairs), last.(name_function_pairs); kwargs...)
2428
end
2529

26-
function observer(
27-
key_function_pairs::Pair{T,<:Function}...; kwargs...
28-
) where {T<:Union{Symbol,String}}
29-
return observer(Pair{T,Function}[key_function_pairs...]; kwargs...)
30+
function observer(name_function_pairs::Pair{<:ColumnName,<:Function}...; kwargs...)
31+
return observer(first.(name_function_pairs), last.(name_function_pairs); kwargs...)
32+
end
33+
34+
function observer(name_function_pairs::NamedTuple; kwargs...)
35+
return observer(keys(name_function_pairs), values(name_function_pairs); kwargs...)
36+
end
37+
38+
function observer(names::Tuple{}, functions::Tuple{}; kwargs...)
39+
return DataFrame(; kwargs...)
3040
end
3141

42+
observer(; kwargs...) = observer(NamedTuple(kwargs))
43+
3244
function observer(functions::Vector{<:Function}; kwargs...)
33-
return observer(
34-
Pair{String,Function}[string(func) => func for func in functions]; kwargs...
35-
)
45+
return observer(string.(functions), functions; kwargs...)
46+
end
47+
48+
function observer(::Tuple{}; kwargs...)
49+
return observer((), (); kwargs...)
50+
end
51+
52+
function observer(functions::Tuple{Vararg{Function}}; kwargs...)
53+
return observer(string.(functions), functions; kwargs...)
3654
end
3755

3856
function observer(functions::Function...; kwargs...)
39-
return observer(
40-
Pair{String,Function}[string(func) => func for func in functions]; kwargs...
41-
)
57+
return observer(string.(functions), functions; kwargs...)
4258
end

0 commit comments

Comments
 (0)