Skip to content

Commit 146212b

Browse files
authored
Merge pull request #11 from s-ccs/docfixes
Docfixes
2 parents 9992588 + 39b6be2 commit 146212b

File tree

11 files changed

+215
-121
lines changed

11 files changed

+215
-121
lines changed

Project.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ authors = ["Benedikt V. Ehinger", "Maanik Marathe"]
44
version = "0.2.1"
55

66
[deps]
7-
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
87
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
98
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
109
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
1110

1211
[compat]
13-
Images = "0.25"
1412
Random = "1"
1513
SparseArrays = "1"
1614
StatsBase = "0.33, 0.34"

docs/make.jl

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ using ClusterDepth
22
using Documenter
33
using Glob
44
using Literate
5-
DocMeta.setdocmeta!(ClusterDepth, :DocTestSetup, :(using ClusterDepth); recursive=true)
5+
DocMeta.setdocmeta!(ClusterDepth, :DocTestSetup, :(using ClusterDepth); recursive = true)
66

77

88
GENERATED = joinpath(@__DIR__, "src")
@@ -13,17 +13,17 @@ for subfolder ∈ ["explanations", "HowTo", "tutorials", "reference"]
1313

1414
end
1515
makedocs(;
16-
modules=[ClusterDepth],
17-
authors="Benedikt V. Ehinger, Maanik Marathe",
18-
repo="https://github.com/s-ccs/ClusterDepth.jl/blob/{commit}{path}#{line}",
19-
sitename="ClusterDepth.jl",
20-
format=Documenter.HTML(;
21-
prettyurls=get(ENV, "CI", "false") == "true",
22-
canonical="https://s-ccs.github.io/ClusterDepth.jl",
23-
edit_link="main",
24-
assets=String[],
16+
modules = [ClusterDepth],
17+
authors = "Benedikt V. Ehinger, Maanik Marathe",
18+
repo = "https://github.com/s-ccs/ClusterDepth.jl/blob/{commit}{path}#{line}",
19+
sitename = "ClusterDepth.jl",
20+
format = Documenter.HTML(;
21+
prettyurls = get(ENV, "CI", "false") == "true",
22+
canonical = "https://s-ccs.github.io/ClusterDepth.jl",
23+
edit_link = "main",
24+
assets = String[],
2525
),
26-
pages=[
26+
pages = [
2727
"Home" => "index.md",
2828
"Tutorials" => [
2929
"An EEG Example" => "tutorials/eeg.md",
@@ -36,7 +36,4 @@ makedocs(;
3636
],
3737
)
3838

39-
deploydocs(;
40-
repo="github.com/s-ccs/ClusterDepth.jl",
41-
devbranch="main",
42-
)
39+
deploydocs(; repo = "github.com/s-ccs/ClusterDepth.jl", devbranch = "main")

docs/src/reference/type1.jl

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ using DataFrames
1515
# ## Setup Simulation
1616
# Let's setup a simulation using UnfoldSim.jl. We simulate a simple 1x2 design with 20 subjects
1717
n_subjects = 20
18-
design = MultiSubjectDesign(n_subjects=n_subjects, n_items=2, items_between=Dict(:condition => ["small", "large"]))
18+
design = MultiSubjectDesign(
19+
n_subjects=n_subjects,
20+
n_items=2,
21+
items_between=Dict(:condition => ["small", "large"]),
22+
)
1923
first(generate_events(design), 5)
2024

2125
#
@@ -33,19 +37,32 @@ signal = MixedModelComponent(;
3337
# Let's move the actual simulation into a function, so we can call it many times.
3438
# Note that we use (`RedNoise`)[https://unfoldtoolbox.github.io/UnfoldSim.jl/dev/literate/reference/noisetypes/] which has lot's of Autocorrelation between timepoints. nice!
3539
function run_fun(r)
36-
data, events = simulate(MersenneTwister(r), design, signal, UniformOnset(; offset=5, width=4), RedNoise(noiselevel=1); return_epoched=true)
40+
data, events = simulate(
41+
MersenneTwister(r),
42+
design,
43+
signal,
44+
UniformOnset(; offset=5, width=4),
45+
RedNoise(noiselevel=1);
46+
return_epoched=true,
47+
)
3748
data = reshape(data, size(data, 1), :)
3849
data = data[:, events.condition.=="small"] .- data[:, events.condition.=="large"]
3950

40-
return data, clusterdepth(data'; τ=quantile(TDist(n_subjects - 1), 0.95), nperm=1000)
51+
return data,
52+
clusterdepth(data'; τ=quantile(TDist(n_subjects - 1), 0.95), nperm=1000)
4153
end;
4254

4355
# ## Understanding the simulation
4456
# let's have a look at the actual data by running it once, plotting condition wise trials, the ERP and histograms of uncorrected and corrected p-values
4557
data, pval = run_fun(5)
4658
conditionSmall = data[:, 1:2:end]
4759
conditionLarge = data[:, 2:2:end]
48-
pval_uncorrected = 1 .- cdf.(TDist(n_subjects - 1), abs.(ClusterDepth.studentt(conditionSmall .- conditionLarge)))
60+
pval_uncorrected =
61+
1 .-
62+
cdf.(
63+
TDist(n_subjects - 1),
64+
abs.(ClusterDepth.studentt(conditionSmall .- conditionLarge)),
65+
)
4966
sig = pval_uncorrected .<= 0.025;
5067

5168
# For the uncorrected p-values based on the t-distribution, we get a type1 error over "time":
@@ -67,8 +84,8 @@ sig = allowmissing(sig)
6784
sig[sig.==0] .= missing
6885
@show sum(skipmissing(sig))
6986
lines!(sig, color=:gray, linewidth=4)
70-
lines!(ax, mean(conditionSmall, dims=2)[:, 1], solid_color=:red)
71-
lines!(ax, mean(conditionLarge, dims=2)[:, 1], solid_color=:blue)
87+
lines!(ax, mean(conditionSmall, dims=2)[:, 1], color=:red)
88+
lines!(ax, mean(conditionLarge, dims=2)[:, 1], color=:blue)
7289

7390
hist!(Axis(f[3, 1], title="uncorrected pvalues"), pval_uncorrected, bins=0:0.01:1.1)
7491
hist!(Axis(f[3, 2], title="clusterdepth corrected pvalues"), pval, bins=0:0.01:1.1)
@@ -81,12 +98,13 @@ res = fill(NaN, reps, 2)
8198
Threads.@threads for r = 1:reps
8299
data, pvals = run_fun(r)
83100
res[r, 1] = mean(pvals .<= 0.05)
84-
res[r, 2] = mean(abs.(ClusterDepth.studentt(data)) .>= quantile(TDist(n_subjects - 1), 0.975))
101+
res[r, 2] =
102+
mean(abs.(ClusterDepth.studentt(data)) .>= quantile(TDist(n_subjects - 1), 0.975))
85103
end;
86104
# Finally, let's calculate the percentage of simulations where we find a significant effect somewhere
87105
mean(res .> 0, dims=1) |> x -> (:clusterdepth => x[1], :uncorrected => x[2])
88106

89-
# Nice, correction seems to work in principle :) Clusterdepth is not exactly 5%, but with more repetitions we should get there (e.g. with 5000 repetitions, we get 0.051%).
107+
# Nice, correction seems to work in principle :) Clusterdepth is not necessarily exactly 5%, but with more repetitions we should get there (e.g. with 5000 repetitions, we got 0.051%).
90108

91109
# !!! info
92-
# if you look closely, the `:uncorrected` value (around 60%) is not as bad as the 99% promised in the introduction. This is due to the correlation between the tests introduced by the noise. Indeed, a good exercise is to repeat everything, but put `RedNoise` to `WhiteNoise`
110+
# if you look closely, the `:uncorrected` value (can be around 60%) is not as bad as the 99% promised in the introduction. This is due to the correlation between the tests introduced by the noise. Indeed, a good exercise is to repeat everything, but put `RedNoise` to `WhiteNoise`

docs/src/reference/type1_troendle.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,22 @@ end;
5050
res = any(pvals_all[:, :, :] .<= 0.05, dims=3)[:, :, 1]
5151
mean(res .> 0, dims=1) |> x -> (:troendle => x[1], :uncorrected => x[2])
5252

53-
# Nice. Troendle fits perfectly and the uncorrected is pretty close to what we calculated above!
53+
# Nice. Troendle corrects the data and we are below 0.05. The uncorrected simulations are close to what we calculated above!
5454

5555
# Finally we end this with a short figure to get a better idea of how this data looks like and a histogram of the p-values
5656

5757
f = Figure()
58-
ax = f[1, 1] = Axis(f)
58+
ax = f[1, 1] = Axis(f, title="t-values")
5959

6060

6161

6262
lines!(ax, abs.(ClusterDepth.studentt(data)))
63-
heatmap!(Axis(f[2, 1]), data)
64-
series!(Axis(f[2, 2]), data[:, 1:7]')
65-
h1 = scatter!(Axis(f[1, 2]; yscale=log10), pvals, label="troendle")
63+
heatmap!(Axis(f[1, 2], title="heatmap of data"), data)
64+
series!(Axis(f[2, 2], title="data: subset of left plot"), data[:, 1:7]')
65+
h1 = scatter!(Axis(f[2, 1]; yscale=log10, title="troendle pvals"), pvals, label="troendle")
6666

6767
hlines!([0.05, 0.01])
6868

69-
hist!(Axis(f[3, 1]), pvals_all[:, 1, :][:], bins=0:0.01:1.1)
70-
hist!(Axis(f[3, 2]), pvals_all[:, 2, :][:], bins=0:0.01:1.1)
69+
hist!(Axis(f[3, 1], title="troendle corrected"), pvals_all[:, 1, :][:], bins=0:0.01:1.1)
70+
hist!(Axis(f[3, 2], title="uncorrected"), pvals_all[:, 2, :][:], bins=0:0.01:1.1)
7171
f
72-

docs/src/tutorials/demo.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,34 @@ using Random
33
using CairoMakie
44

55

6-
n_t =40 # timepoints
6+
n_t = 40 # timepoints
77
n_sub = 50
88
n_perm = 5000
99

1010
snr = 0.5 # signal to nois
1111

1212
## add a signal to the middle
13-
signal = vcat(zeros(n_t÷4), sin.(range(0,π,length=n_t÷2)), zeros(n_t÷4))
13+
signal = vcat(zeros(n_t ÷ 4), sin.(range(0, π, length = n_t ÷ 2)), zeros(n_t ÷ 4))
1414

1515
## same signal for all subs
16-
signal = repeat(signal,1,n_sub)
16+
signal = repeat(signal, 1, n_sub)
1717

1818

1919
## add noise
20-
data = randn(MersenneTwister(123),n_t,n_sub).+ snr .* signal
20+
data = randn(MersenneTwister(123), n_t, n_sub) .+ snr .* signal
2121

2222
## by default assumes τ=2.3 (~alpha=0.05), and one-sample ttest
2323
@time pvals = clusterdepth(data);
2424

2525
f = Figure()
26-
ax = f[1,1] = Axis(f)
26+
ax = f[1, 1] = Axis(f)
2727

2828

2929
lines!(abs.(ClusterDepth.studentt(data)))
30-
h1 = scatter(f[1,2],pvals;axis=(;yscale=log10),label="troendle")
30+
h1 = scatter(f[1, 2], pvals; axis = (; yscale = log10), label = "troendle")
3131

32-
pvals2 = clusterdepth(data;pval_type=:naive)
33-
h2 = scatter!(1.2:40.2,pvals2,color="red",label="naive")
32+
pvals2 = clusterdepth(data; pval_type = :naive)
33+
h2 = scatter!(1.2:40.2, pvals2, color = "red", label = "naive")
3434
#hlines!(([0.05]))
3535
axislegend()
36-
f
36+
f

docs/src/tutorials/eeg-multichannel.jl

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@ using Unfold
66
using UnfoldMakie
77
using Statistics
88

9-
# ## How to use clusterDepth multiple comparison correction on multichannel data
9+
# # How to use the ClusterDepth multiple comparison correction on multichannel data
1010

11-
# This tutorial is adapted from the first EEG example and uses the HArtMuT NYhead model (https://github.com/harmening/HArtMuT) to simulate multiple channels.
11+
# This tutorial is adapted from the single-channel EEG example, and complements it with the HArtMuT NYhead model (https://github.com/harmening/HArtMuT) to simulate multiple channels.
1212

1313
# First set up the EEG simulation as before, with one subject and 40 trials:
14-
design = SingleSubjectDesign(conditions=Dict(:condition => ["car", "face"])) |> x -> RepeatDesign(x, 40);
14+
design =
15+
SingleSubjectDesign(conditions=Dict(:condition => ["car", "face"])) |>
16+
x -> RepeatDesign(x, 40);
1517
p1 = LinearModelComponent(;
1618
basis=p100(; sfreq=250),
1719
formula=@formula(0 ~ 1),
18-
β=[1.0]
20+
β=[1.0],
1921
);
2022

2123
n170 = LinearModelComponent(;
@@ -39,46 +41,66 @@ src_coords = [
3941
];
4042

4143
headmodel_HArtMuT = headmodel()
42-
get_closest = coord -> UnfoldSim.closest_src(coord, headmodel_HArtMuT.cortical["pos"]) |> pi -> magnitude(headmodel_HArtMuT; type="perpendicular")[:, pi]
44+
get_closest =
45+
coord ->
46+
UnfoldSim.closest_src(coord, headmodel_HArtMuT.cortical["pos"]) |>
47+
pi -> magnitude(headmodel_HArtMuT; type="perpendicular")[:, pi]
4348

4449
p1_l = p1 |> c -> MultichannelComponent(c, get_closest([-20, -78, -10]))
4550
p1_r = p1 |> c -> MultichannelComponent(c, get_closest([20, -78, -10]))
4651
n170_r = n170 |> c -> MultichannelComponent(c, get_closest([50, -40, -25]))
4752
p300_do = p300 |> c -> MultichannelComponent(c, get_closest([0, -50, -40]))
4853
p300_up = p300 |> c -> MultichannelComponent(c, get_closest([0, 5, 20]))
4954

50-
data, events = simulate(MersenneTwister(1), design, [p1_l, p1_r, n170_r, p300_do, p300_up],
55+
data, events = simulate(
56+
MersenneTwister(1),
57+
design,
58+
[p1_l, p1_r, n170_r, p300_do, p300_up],
5159
UniformOnset(; offset=0.5 * 250, width=100),
52-
RedNoise(noiselevel=1); return_epoched=true);
60+
RedNoise(noiselevel=1);
61+
return_epoched=true,
62+
);
5363

5464

5565
# ## Plotting
5666
# This is what the data looks like, for one channel/trial respectively:
5767
f = Figure()
5868
Axis(f[1, 1], title="Single channel, all trials", xlabel="time", ylabel="y")
59-
series!(data[1, :, :]', solid_color=:black)
69+
series!(data[1, :, :]', solid_color=(:black, 0.1))
6070
lines!(mean(data[1, :, :], dims=2)[:, 1], color=:red)
61-
6271
hlines!([0], color=:gray)
72+
6373
Axis(f[2, 1], title="All channels, average over trials", xlabel="time", ylabel="y")
64-
series!(mean(data, dims=3)[:, :, 1], solid_color=:black)
74+
series!(mean(data, dims=3)[:, :, 1], solid_color=(:black, 0.1))
6575
hlines!([0], color=:gray)
6676
f
6777

6878
# And some topoplots:
69-
positions = [Point2f(p[1] + 0.5, p[2] + 0.5) for p in to_positions(headmodel_HArtMuT.electrodes["pos"]')]
79+
positions = [
80+
Point2f(p[1] + 0.5, p[2] + 0.5) for
81+
p in to_positions(headmodel_HArtMuT.electrodes["pos"]')
82+
]
7083

71-
df = UnfoldMakie.eeg_matrix_to_dataframe(mean(data, dims=3)[:, :, 1], string.(1:length(positions)));
84+
df = UnfoldMakie.eeg_array_to_dataframe(
85+
mean(data, dims=3)[:, :, 1],
86+
string.(1:length(positions)),
87+
);
7288
Δbin = 20 # 20 samples / bin
73-
plot_topoplotseries(df, Δbin; positions=positions, visual=(; enlarge=1, label_scatter=false))
89+
plot_topoplotseries(
90+
df,
91+
bin_width=20,
92+
positions=positions,
93+
visual=(; enlarge=1, label_scatter=false),
94+
)
7495

7596
# ## ClusterDepth
7697
# Now that the simulation is done, let's try out ClusterDepth and plot our results
7798
# Note that this is a simple test of "activity" vs. 0
78-
pvals = clusterdepth(data; τ=1.6, nperm=100);
79-
fig, ax, hm = heatmap(transpose(pvals))
99+
pvals = clusterdepth(data; τ=1.6, nperm=200);
100+
fig, ax, hm = heatmap(transpose(pvals), colorscale=log10)
80101
ax.title = "pvals";
81102
ax.xlabel = "time";
82103
ax.ylabel = "channel";
83104
Colorbar(fig[:, end+1], hm);
84105
fig
106+
loopvectorization

0 commit comments

Comments
 (0)