Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weak extensions #253

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Contribution guide
Contributions are very welcome. These could be typos, bug reports, feature requests, speed optimization, better code, and better documentation.
You are very welcome to raise issues and start pull requests.

## Issues
If you notice any bugs, such as crashing code, incorrect results or speed issues, please raise a GitHub issue.

Before filing an issue please
- check that there are no similar existing issues already
- check that your versions are up to date

If you want to report a bug, include your version and system information, as well as stack traces with all relevant information.
If possible, condense your bug into the shortest example possible that the maintainers can replicate, a so called "minimal working example" or MWE.

If you want to suggest a new feature, for example functionality that other plotting packages offer already, include supplementary material such as example images if possible, so it's clear what you are asking for.

## Code contributions (Pull requests)
When opening a pull request, please add a short but meaningful description of the changes/features you implemented. Moreover, please add tests (where appropriate) to ensure that your code is working as expected.

For each feature you want to contribute, please file a separate PR to keep the complexity down and time to merge short.
Add PRs in draft mode if you want to discuss your approach first.


## Adding documentation
1. We recommend to write a Literate.jl document and place it in `docs/literate/FOLDER/FILENAME.jl` with `FOLDER` being `HowTo`, `Explanations`, `Tutorials` or `Intro` ([recommended reading on the 4 categories](https://documentation.divio.com/)).
2. Literate.jl converts the `.jl` file to a `.md` automatically and places it in `docs/src/generated/FOLDER/FILENAME.md`.
3. Edit [make.jl](https://github.com/unfoldtoolbox/Unfold.jl/blob/main/docs/make.jl) with a reference to `docs/src/generated/FOLDER/FILENAME.md`.

## Formatting (Beware of reviewdog :dog:)
We use the [julia-format](https://github.com/julia-actions/julia-format) Github action to ensure that the code follows the formatting rules defined by [JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl).
When opening a pull request [reviewdog](https://github.com/reviewdog/reviewdog) will automatically make formatting suggestions for your code.

## Seeking Help

If you get stuck, here are some options to seek help:

- Use the REPL `?` help mode.
- Check the Documentation.
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
TopoPlots = "2bdbdf9c-dbd8-403f-947b-1a4e0dd41a7a"
Unfold = "181c99d8-e21b-4ff3-b70b-c233eddec679"

[weakdeps]
PyMNE = "6c5003b2-cbe8-491c-a0d1-70088e6a0fd6"
Unfold = "181c99d8-e21b-4ff3-b70b-c233eddec679"


[extensions]
UnfoldMakiePyMNEExt = "PyMNE"
UnfoldMakieUnfoldExt = "Unfold"

[compat]
AlgebraOfGraphics = "0.7, 0.8"
Expand All @@ -56,7 +58,7 @@ SparseArrays = "1"
StaticArrays = "1"
Statistics = "1"
TopoPlots = "0.1"
Unfold = "0.3, 0.4, 0.5, 0.6, 0.7"
Unfold = "0.6, 0.7"
julia = "1"

[extras]
Expand Down
2 changes: 2 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[deps]
AlgebraOfGraphics = "cbdf2221-f076-402e-a563-3d30da359d67"
BSplineKit = "093aae92-e908-43d7-9660-e50ee39d5a0a"
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
Expand All @@ -14,6 +15,7 @@ Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
MakieThemes = "e296ed71-da82-5faf-88ab-0034a9761098"
PyMNE = "6c5003b2-cbe8-491c-a0d1-70088e6a0fd6"
PythonPlot = "274fc56d-3b97-40fa-a1cd-1b4a50311bf9"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d"
Expand Down
2 changes: 1 addition & 1 deletion docs/example_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function example_data(example = "TopoPlots.jl")
dat, evts =
UnfoldSim.predef_eeg(; onset = LogNormalOnset(μ = 3.5, σ = 0.4), noiselevel = 5)
dat_e, times = Unfold.epoch(dat, evts, [-0.1, 1], 100)
evts, dat_e = UnfoldMakie.drop_missing_epochs(evts, dat_e)
evts, dat_e = Unfold.drop_missing_epochs(evts, dat_e)
evts.Δlatency = vcat(diff(evts.latency), 0)
dat_e = dat_e[1, :, :]
#evts = filter(row -> row.Δlatency > 0, evts)
Expand Down
2 changes: 0 additions & 2 deletions docs/literate/how_to/hide_deco.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,13 @@ plot_butterfly!(
f[1, 1],
data;
positions = pos,
topomarkersize = 10,
topo_axis = (; height = Relative(0.4), width = Relative(0.4)),
axis = (; title = "With decorations"),
)
plot_butterfly!(
f[2, 1],
data;
positions = pos,
topomarkersize = 10,
topo_axis = (; height = Relative(0.4), width = Relative(0.4)),
axis = (; title = "Without decorations"),
layout = (; hidedecorations = (:label => true, :ticks => true, :ticklabels => true)),
Expand Down
1 change: 0 additions & 1 deletion docs/literate/how_to/mult_vis_in_fig.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ plot_butterfly!(
gb,
d_topo;
positions = pos,
topomarkersize = 10,
topo_axis = (; height = Relative(0.4), width = Relative(0.4)),
)
hlines!(0, color = :gray, linewidth = 1)
Expand Down
77 changes: 77 additions & 0 deletions docs/literate/intro/speed.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Speed measurement
using UnfoldMakie
using TopoPlots
using PyMNE
using PythonPlot
using BenchmarkTools

# Data input
dat, positions = TopoPlots.example_data()
df = UnfoldMakie.eeg_array_to_dataframe(dat[:, :, 1], string.(1:length(positions)));

# # Topoplots

# UnfoldMakie.jl
@benchmark plot_topoplot(dat[:, 320, 1]; positions = positions)

# UnfoldMakie.jl with DelaunayMesh
@benchmark plot_topoplot(
dat[:, 320, 1];
positions = positions,
topo_interpolation = (; interpolation = DelaunayMesh()),
)

# MNE
posmat = collect(reduce(hcat, [[p[1], p[2]] for p in positions])')
pypos = Py(posmat).to_numpy()
pydat = Py(dat[:, 320, 1])

@benchmark begin
f = PythonPlot.figure()
PyMNE.viz.plot_topomap(
pydat,
pypos,
sphere = 1.1,
extrapolate = "box",
cmap = "RdBu_r",
sensors = false,
contours = 6,
)
f.show()
end

# # Topoplot series
# UnfoldMakie.jl
@benchmark begin
plot_topoplotseries(
df;
bin_num = 20,
positions = positions,
axis = (; xlabel = "Time windows [s]"),
)
end

# UnfoldMakie.jl with DelaunayMesh
@benchmark begin
plot_topoplotseries(
df;
bin_num = 50,
positions = positions,
topo_attributes = (; interpolation = DelaunayMesh()),
)
end

# MNE
easycap_montage = PyMNE.channels.make_standard_montage("standard_1020")
ch_names = pyconvert(Vector{String}, easycap_montage.ch_names)[1:64]
info = PyMNE.create_info(PyList(ch_names), ch_types = "eeg", sfreq = 1)
info.set_montage(easycap_montage)
simulated_epochs = PyMNE.EvokedArray(Py(dat[:, :, 1]), info)

@benchmark simulated_epochs.plot_topomap(1:50)

# MATLAB

# ```@raw html
# <img src="../../../assets/MATLAB_benchmarking.png" align="middle"/>
# ```
vladdez marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions docs/literate/tutorials/butterfly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ plot_butterfly(df)

plot_butterfly(df; positions = pos)

# You want to change size of topomarkers and size of topoplot:
# You want to change size of topoplot markers and size of topoplot:

plot_butterfly(
df;
positions = pos,
topomarkersize = 10,
topo_attributes = (; label_scatter = (; markersize = 30)),
topo_axis = (; height = Relative(0.4), width = Relative(0.4)),
)

Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ makedocs(;
"Installation" => "generated/intro/installation.md",
"Plot types" => "generated/intro/plot_types.md",
"Code principles" => "generated/intro/code_principles.md",
"Benchmarking" => "generated/intro/speed.md",
],
"ERP Visualizations" => [
"ERP plot" => "generated/tutorials/erp.md",
Expand Down
Binary file added docs/src/assets/MATLAB_benchmarking.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions ext/UnfoldMakieUnfoldExt/UnfoldMakieUnfoldExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module UnfoldMakieUnfoldExt

using Unfold
using UnfoldMakie
using GridLayoutBase
using Makie

# Unfold Backward Compatability. AbstractDesignMatrix was introduced only in v0.7
if isdefined(Unfold, :AbstractDesignMatrix)
# nothing to do for AbstractDesignMatrix, already imprted
# backward compatible accessor
#const drop_missing_epochs = Unfold.drop_missing_epochs
#const modelmatrices = Unfold.modelmatrices
else
const AbstractDesignMatrix = Unfold.DesignMatrix
#const drop_missing_epochs = Unfold.dropMissingEpochs
#const modelmatrices = Unfold.get_Xs
end

import UnfoldMakie.supportive_defaults
import UnfoldMakie._docstring
include("plot_splines.jl")
include("plot_designmatrix.jl")




end
144 changes: 144 additions & 0 deletions ext/UnfoldMakieUnfoldExt/plot_designmatrix.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"""
plot_designmatrix!(f::Union{GridPosition, GridLayout, Figure}, data::Unfold.DesignMatrix; kwargs...)
plot_designmatrix(data::Unfold.DesignMatrix; kwargs...)


Plot a designmatrix.
## Arguments
- `f::Union{GridPosition, GridLayout, Figure}`\\
`Figure`, `GridLayout`, or `GridPosition` to draw the plot.
- `data::Unfold.DesignMatrix`\\
Data for the plot visualization.

## Keyword arguments (kwargs)
- `standardize_data::Bool = true`\\
Indicates whether the data is standardized by pointwise division of the data with its sampled standard deviation.
- `sort_data::Bool = true`\\
Indicates whether the data is sorted. It uses `sortslices()` of Base Julia.
- `xticks::Num = nothing`\\
Returns the number of labels on the x axis.
- `xticks` = 0: no labels are placed.
- `xticks` = 1: first possible label is placed.
- `xticks` = 2: first and last possible labels are placed.
- 2 < `xticks` < `number of labels`: equally distribute the labels.
- `xticks` ≥ `number of labels`: all labels are placed.

$(_docstring(:designmat))

**Return Value:** `Figure` displaying the Design matrix.
"""
UnfoldMakie.plot_designmatrix(
data::Union{<:Vector{<:AbstractDesignMatrix},<:AbstractDesignMatrix};
kwargs...,
) = UnfoldMakie.plot_designmatrix!(Figure(), data; kwargs...)

function UnfoldMakie.plot_designmatrix!(f, data::Vector{<:AbstractDesignMatrix}; kwargs...)
if length(data) > 1
@warn "multiple $(length(data)) designmatrices found, plotting the first one"
end
UnfoldMakie.plot_designmatrix!(f, data[1]; kwargs...)
end
function UnfoldMakie.plot_designmatrix!(
f::Union{GridPosition,GridLayout,Figure},
data::AbstractDesignMatrix;
xticks = nothing,
sort_data = false,
standardize_data = false,
kwargs...,
)
config = PlotConfig(:designmat)
UnfoldMakie.config_kwargs!(config; kwargs...)
designmat = modelmatrix(data)
if standardize_data
designmat = designmat ./ std(designmat, dims = 1)
designmat[isinf.(designmat)] .= 1.0
end

if isa(designmat, Unfold.SparseMatrixCSC)
if sort_data
@warn "Sorting does not make sense for time-expanded designmatrices. sort_data has been set to `false`"
sort_data = false
end
designmat = Matrix(designmat[end÷2-2000:end÷2+2000, :]) # needs a size(designmat) of at least 4000 x Any
end

if sort_data
designmat = Base.sortslices(designmat, dims = 1)
end
labels0 = replace.(Unfold.get_coefnames(data), r"\s*:" => ":")

if length(split(labels0[1], ": ")) > 1
labels = map(x -> join(split(x, ": ")[3]), labels0)
labels_top1 = Unfold.extract_coef_info(Unfold.get_coefnames(data), 2)
unique_names = String[]
labels_top2 = String[""]
for el in labels_top1
if !in(el, unique_names)
push!(unique_names, el)
push!(labels_top2, el)
else
push!(labels_top2, "")
end
end
end
lLength = length(labels0)
# only change xticks if we want less then all
if (xticks !== nothing && xticks < lLength)
@assert(xticks >= 0, "xticks shouldn't be negative")
# sections between xticks
section_size = (lLength - 2) / (xticks - 1)
new_labels = []

# first tick. Empty if 0 ticks
if xticks >= 1
push!(new_labels, labels0[1])
else
push!(new_labels, "")
end

# fill in ticks in the middle
for i = 1:(lLength-2)
# checks if we're at the end of a section, but NO tick on the very last section
if i % section_size < 1 && i < ((xticks - 1) * section_size)
push!(new_labels, labels0[i+1])
else
push!(new_labels, "")
end
end

# last tick at the end
if xticks >= 2
push!(new_labels, labels0[lLength-1])
else
push!(new_labels, "")
end

labels0 = new_labels
end
if length(split(labels0[1], ": ")) > 1
ax2 = Axis(
f[1, 1],
xticklabelcolor = :red,
xaxisposition = :top;
xticks = (1:length(labels_top2), labels_top2),
)
hidespines!(ax2)
hidexdecorations!(ax2, ticklabels = false, ticks = false)
hm = heatmap!(ax2, designmat'; config.visual...)
else
labels = labels0
end

# plot Designmatrix
config.axis = merge(config.axis, (; xticks = (1:length(labels), labels)))
ax = Axis(f[1, 1]; config.axis...)
hm = heatmap!(ax, designmat'; config.visual...)

if isa(designmat, SparseMatrixCSC)
ax.yreversed = true
end

apply_layout_settings!(config; fig = f, hm = hm)

return f
end
Loading
Loading