Skip to content

Commit 7ff936b

Browse files
authored
Merge pull request #159 from formal-methods-mpi/filters_rework
Filter for RecordDataBase
2 parents 7db93f7 + 09de611 commit 7ff936b

File tree

10 files changed

+678
-36
lines changed

10 files changed

+678
-36
lines changed

src/Taxonomy.jl

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,26 @@ module Taxonomy
1717

1818
export DOI, UsualDOI, UnusualDOI
1919
include("metadata/doi.jl")
20+
21+
export Study
22+
include("study.jl")
23+
24+
export Record, id, judgements, location, spec, data, ExtractStudy
25+
include("record.jl")
26+
27+
export RecordDatabase, check_uuid, check_url
28+
import Base: UUID
29+
include("database.jl")
2030

2131
import Base.convert, Base.==
2232
export Judgements, J, Judgement, NoJudgement, convert, rating, comment, certainty
33+
export Empirical, Lang, N, Standardized, Quest
2334
export JudgementNumber, JudgementBool, JudgementInt, JudgementString
2435
export JudgementVecNumber, JudgementVecBool, JudgementVecInt, JudgementVecString
2536
include("judgements/Judgements.jl")
2637

2738
using Taxonomy.Judgements
2839

29-
export Study
30-
include("study.jl")
31-
32-
export Record, id, judgements, location, spec, data, ExtractStudy
33-
include("record.jl")
34-
3540
import HTTP
3641
import JSON
3742
export MetaData, MinimalMeta, IncompleteMeta, ExtensiveMeta, url, year, author, journal, apa, json
@@ -57,17 +62,15 @@ module Taxonomy
5762
export generate_id
5863
include("uuid.jl")
5964

60-
export RecordDatabase, check_uuid, check_url
61-
import Base: UUID
62-
include("database.jl")
65+
export Model
66+
include("model.jl")
6367

64-
export factor_variance, structural_model, doi
68+
export factor_variance, structural_model, doi, measurement_model
6569
include("extractors.jl")
6670

67-
include("pretty_printing.jl")
71+
include("filter.jl")
6872

69-
export Model
70-
include("model.jl")
73+
include("pretty_printing.jl")
7174

7275
include("deprecated.jl")
7376
end

src/extractors.jl

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ rating(factor_variance(f))
1717
1.0
1818
```
1919
"""
20-
function factor_variance(x::Measurement)
21-
x.factor_variance
22-
end
23-
20+
factor_variance(x::Measurement) = x.factor_variance
2421

2522
"""
2623
Function to extract the `StenoGraphs` structural model from [`Structural`].
@@ -47,11 +44,7 @@ struct_model = Structural(structural_model = graph)
4744
structural_model(struct_model)
4845
```
4946
"""
50-
function structural_model(x::Structural)
51-
x.structural_model
52-
end
53-
54-
47+
structural_model(x::Structural) = x.structural_model
5548

5649
"""
5750
Function to extract a Judgement from a `JudgementLevel` (e.g. [Model](@ref), [Record](@ref), [Study](@ref)).
@@ -81,4 +74,32 @@ Dict{Symbol, Vector{Union{Study, AbstractJudgement}}} with 1 entry:
8174
"""
8275
judgements(x::JudgementLevel) = x.judgements
8376
url(x::Record) = url(location(x))
84-
url(x::RecordDatabase) = map(x -> url(x.second), collect(x))
77+
url(x::RecordDatabase) = map(x -> url(x.second), collect(x))
78+
79+
Base.get(r::JudgementLevel, field::Symbol, default=Vector{Union{JudgementLevel,AbstractJudgement}}[]) = get(judgements(r), field, default)
80+
81+
"""
82+
Extract all `Studies` from a `Record`.
83+
"""
84+
Study(r::Record)::Vector{Union{Study,AbstractJudgement}} = get(judgements(r), :Study, [])
85+
Study(r::Dict{Symbol,Vector{Union{Study,AbstractJudgement}}})::Vector{Union{Study,AbstractJudgement}} = get(r, :Study, [])
86+
87+
"""
88+
Extract all `Models` from a `Study`.
89+
"""
90+
Model(s::Study)::Vector{Union{Model,AbstractJudgement}} = get(judgements(s), :Model, [])
91+
92+
"""
93+
Extract all Taxons from a Model.
94+
"""
95+
Taxon(m::Model)::Vector{Union{Taxon,AbstractJudgement}} = get(judgements(m), :Taxon, [])
96+
97+
"""
98+
Extract Measurement part from a Taxon.
99+
"""
100+
measurement_model(t::Taxon) = t.measurement_model
101+
102+
"""
103+
Extract Structural part from a Taxon.
104+
"""
105+
structural_model(t::Taxon) = t.structural_model

src/filter.jl

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
function Base.filter(f, s::Vector{Union{T,AbstractJudgement}} where {T<:JudgementLevel})
2+
res_vec = eltype(s)[]
3+
for i in eachindex(s)
4+
if f(s[i])
5+
res_vec = push!(res_vec, s[i])
6+
end
7+
end
8+
9+
return res_vec
10+
end
11+
Base.filter(f, s::JudgementLevel)::Vector{Union{JudgementLevel,AbstractJudgement}} = f(s) ? [s] : []
12+
13+
"""
14+
filter(f, db::RecordDatabase, level::AbstractString)::RecordDatabase
15+
16+
Filter the database at a given nesting level.
17+
18+
# Arguments
19+
- `f`: A function returning a boolean value for filtering a dict at the specified level.
20+
- `db`: A RecordDatabase object.
21+
- `level`: The character string of the level at which to filter the database. Possible values are `"Record"`, `"Study"`, `"Model"`, `"Taxon"`.
22+
23+
# Examples
24+
```jldoctest filter-examples
25+
26+
## Example just for demonstration, coding is not actually derived from the paper!
27+
28+
using Taxonomy
29+
using Taxonomy.Judgements
30+
31+
test_db = RecordDatabase()
32+
33+
test_db += Record(
34+
rater="NH",
35+
id="2a129694-550c-4396-be6f-00507b1dc7ba",
36+
location=DOI("10.1007/s10869-019-09648-5"),
37+
Lang("en"),
38+
Study(
39+
N(100, 0.8),
40+
Model(Standardized(false),
41+
LatentPathmodel(
42+
Structural(structural_model=missing),
43+
Dict(
44+
:IP => Measurement(n_variables=3, factor_variance=missing, loadings=missing, quest_scale=7),
45+
:IN => Measurement(n_variables=4, factor_variance=missing, loadings=missing, quest_scale=6),
46+
:DN => Measurement(n_variables=3, factor_variance=missing, loadings=missing, quest_scale=7),
47+
:BC => Measurement(n_variables=3, factor_variance=missing, loadings=missing, quest_scale=5),
48+
:IB => Measurement(n_variables=5, factor_variance=missing, loadings=missing, quest_scale=5)
49+
)
50+
)
51+
),
52+
Model(Standardized(true),
53+
NoTaxon()
54+
)
55+
),
56+
Study(
57+
N(200, 0.98),
58+
Model(Standardized(false),
59+
),
60+
Model(Standardized(false))
61+
)
62+
)
63+
64+
## Filtering on Taxon level
65+
filter_Pathmodel = filter(x -> typeof(x) == NoTaxonEver, test_db, "Taxon")
66+
Model(Study(filter_Pathmodel[Base.UUID("2a129694-550c-4396-be6f-00507b1dc7ba")])[1])[2]
67+
68+
# output
69+
Model(Dict{Symbol, Vector{Union{Taxon, AbstractJudgement}}}(:Taxon => [NoTaxonEver
70+
], :Standardized => [Standardized{Bool}(true, 1.0, missing)]))
71+
```
72+
73+
```jldoctest filter-examples
74+
75+
## Filtering on Model level
76+
filter_Standardized = filter(x -> rating(x, :Standardized) == true, test_db, "Model")
77+
Model(Study(filter_Standardized[Base.UUID("2a129694-550c-4396-be6f-00507b1dc7ba")])[1])
78+
79+
# output
80+
1-element Vector{Union{Model, AbstractJudgement}}:
81+
Model(Dict{Symbol, Vector{Union{Taxon, AbstractJudgement}}}(:Taxon => [NoTaxonEver
82+
], :Standardized => [Standardized{Bool}(true, 1.0, missing)]))
83+
84+
```
85+
86+
```jldoctest filter-examples
87+
88+
## Filtering on Study level
89+
filter_N = filter(x -> certainty(x, :N) > 0.9, test_db, "Study")
90+
Study(filter_N[Base.UUID("2a129694-550c-4396-be6f-00507b1dc7ba")])
91+
92+
# output
93+
1-element Vector{Union{Study, AbstractJudgement}}:
94+
Study(Dict{Symbol, Vector{Union{JudgementLevel, AbstractJudgement}}}(:N => [N{Int64}(200, 0.98, missing)]))
95+
```
96+
"""
97+
function Base.filter(f, db::RecordDatabase, level::AbstractString)::RecordDatabase
98+
99+
@assert level in ["Record", "Study", "Model", "Taxon"] "Invalid level. Choose from 'Record', 'Study', 'Model', 'Taxon'"
100+
101+
if level == "Record"
102+
db = filter(f, db)
103+
else
104+
105+
for current_record in keys(db)
106+
studies = Study(db[current_record])
107+
108+
if level == "Study"
109+
filtered_study = filter(f, studies)
110+
if length(filtered_study) > 0
111+
judgements(db[current_record])[:Study] = filtered_study
112+
else
113+
delete!(judgements(db[current_record]), :Study)
114+
end
115+
else
116+
117+
for current_study in eachindex(studies)
118+
models = Model(studies[current_study])
119+
if level == "Model"
120+
filtered_model = filter(f, models)
121+
if length(filtered_model) > 0
122+
judgements(Study(db[current_record])[current_study])[:Model] = filtered_model
123+
else
124+
delete!(judgements(judgements(db[current_record])[:Study][current_study]), :Model)
125+
end
126+
else
127+
for current_model in eachindex(models)
128+
taxons = Taxon(models[current_model])
129+
if level == "Taxon"
130+
filtered_taxons = filter(f, taxons)
131+
132+
if length(filtered_taxons) > 0
133+
judgements(Model(Study(db[current_record])[current_study])[current_model])[:Taxon] = filtered_taxons
134+
else
135+
delete!(judgements(Model(Study(db[current_record])[current_study])[current_model]), :Taxon)
136+
end
137+
end
138+
end
139+
end
140+
end
141+
end
142+
end
143+
end
144+
return db
145+
end

src/judgements/Judgements.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
module Judgements
2-
import Taxonomy: AbstractJudgement, Taxon, JudgementLevel
2+
import Taxonomy: AbstractJudgement, Taxon, JudgementLevel, Record, Base.==
33
check_certainty(c) = ((c < 0.0) || (c > 1.0)) ? throw(ArgumentError("Certainty must be between 0 and 1.")) : nothing
44
export AnyLevelJudgement, RecordJudgement, StudyJudgement, ModelJudgement
55
export check_judgement_level, correct_judgement_level, judgements
66
include("level.jl")
77

88
export @newjudgement
9-
export J, Judgement, NoJudgement, convert, rating, comment, certainty, judgement_key
9+
export J, Judgement, NoJudgement, convert, rating, comment, certainty, judgement_key, judgement_level
1010
include("judgement.jl")
1111

1212
# exports via code gen within the file
@@ -15,6 +15,6 @@ module Judgements
1515
export judgement_dict
1616
include("dict.jl")
1717

18-
export CFI, Empirical, Lang, N
18+
export CFI, Empirical, Lang, N, Standardized, Quest
1919
include("predefined_judgements.jl")
2020
end

src/judgements/judgement.jl

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,19 @@ macro newjudgement(name, level, doc, type=Any, check=x -> nothing, unique=true)
6363
unique = :(Judgements.judgement_unique(::$name) = $unique)
6464
key = :(Judgements.judgement_key(::$name) = Symbol($name))
6565
level = :(Judgements.judgement_level(::$name) = $level())
66-
66+
6767
# Create a new function with the same name as the judgement
6868
# This function accepts keyword arguments and creates a NamedTuple
6969
# The NamedTuple is then passed to the judgement constructor
7070
keyword_func = quote
71-
function $name(;kwargs...)
71+
function $name(; kwargs...)
7272
kwargs_tuple = NamedTuple{tuple(keys(kwargs)...)}(tuple(values(kwargs)...))
7373
$name(kwargs_tuple)
7474
end
7575
end
76-
7776

78-
77+
78+
7979
return quote
8080
$(esc(inner))
8181
$(esc(outer))
@@ -86,7 +86,7 @@ macro newjudgement(name, level, doc, type=Any, check=x -> nothing, unique=true)
8686
$(esc(doc))
8787
$(esc(keyword_func))
8888
end
89-
end
89+
end
9090

9191
@newjudgement(
9292
Judgement,
@@ -109,16 +109,43 @@ macro newjudgement(name, level, doc, type=Any, check=x -> nothing, unique=true)
109109
"""
110110
Extract rating from Judgement.
111111
112-
If `rating` is called on a `Judgement` it returns the rating, on everything it returns identity.
112+
If `rating` is called on a `Judgement` it returns the rating, on everything it returns identity.
113+
If `rating` is called on a `JudgementLevel` together with a field name, it returns the rating of that field.
113114
114115
"""
115116
rating(x) = x
116117
rating(x::AbstractJudgement) = x.rating
118+
rating(x::JudgementLevel, field::Symbol) = rating(get(x, field))
119+
rating(x::Pair{Base.UUID, Record}, field::Symbol) = rating(x.second, field)
120+
function rating(x::Vector{Union{T,AbstractJudgement}}) where T <: JudgementLevel
121+
if length(x) == 1
122+
return rating(x[1])
123+
elseif length(x) == 0
124+
return x
125+
else
126+
error("It seems like you have provided a vector that contains multiple judgements. Only the rating of single judgement can be extracted.")
127+
end
128+
end
117129

118130
"""
119131
Extract certainty from Judgement.
132+
133+
If `certainty` is called on a `Judgement` it returns the certainty, on everything it returns identity.
134+
If `certainty` is called on a `JudgementLevel` together with a field name, it returns the certainty of that field.
135+
120136
"""
121137
certainty(x::AbstractJudgement) = x.certainty
138+
certainty(x::Pair{Base.UUID, Record}, field::Symbol) = certainty(x.second, field)
139+
certainty(x::JudgementLevel, field::Symbol) = certainty(get(x, field))
140+
function certainty(x::Vector{Union{T,AbstractJudgement}}) where T <: JudgementLevel
141+
if length(x) == 1
142+
return certainty(x[1])
143+
elseif length(x) == 0
144+
return x
145+
else
146+
error("It seems like you have provided a vector that contains multiple judgements. Only the certainty of single judgement can be extracted.")
147+
end
148+
end
122149

123150
"""
124151
Extract comment from Judgement.

src/judgements/predefined_judgements.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,34 @@ Judgement for N.
6060
Int # Input type. In this case Integer.
6161
)
6262

63+
@newjudgement(
64+
Standardized,
65+
ModelJudgement,
66+
"""
67+
Any part of the data or model is standardized
68+
69+
Procedure:
70+
* Search for standard*
71+
* If present, give True
72+
""",
73+
Bool # Input type. In this case boolean.
74+
)
6375

76+
@newjudgement(
77+
Quest,
78+
ModelJudgement,
79+
"""
80+
What questionnaire was used to measure the latent variable.
81+
82+
Procedure:
83+
* Look for first instance of questionnaire,
84+
* acronym or name is fine, copy and paste citation if availible
85+
* Do not bother whether the scale is translated, modified or shortened.
86+
* spend little time (<30s) on searching for it
87+
* should be given multiple times for all quests present in a model.
88+
89+
""",
90+
AbstractString,
91+
x -> nothing,
92+
unique = false
93+
)

0 commit comments

Comments
 (0)