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

[DOC] multi-echo reports #457

Merged
merged 61 commits into from
Jul 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
58a2c32
Added html template for report
Nov 11, 2019
b61fb1e
doc: initial commit of reporting template
emdupre Nov 11, 2019
34c9133
doc: save out html for testing
emdupre Nov 11, 2019
fcaf13b
fix: add missing bokeh js script
emdupre Nov 11, 2019
942a469
fix: unescape HTML ugly way
emdupre Nov 11, 2019
b7262ab
fix: add missing cdn's
emdupre Nov 11, 2019
fbb2659
enh: switch to generate_report structure
emdupre Nov 11, 2019
31bf94f
sty: switch back to pure CSS from bootstrap
emdupre Nov 11, 2019
f0db0f5
fix: differently update template for content
emdupre Nov 11, 2019
1901196
sty: add jupyter notebooks to git ignore
emdupre Nov 12, 2019
2bffccf
enh:initial commit of dynamic plots
javiergcas Nov 12, 2019
1627b10
fix: patch unpaired bracket
emdupre Nov 12, 2019
89fd655
Merge remote-tracking branch 'emdupre/doc/me-reports' into dynamic_re…
javiergcas Nov 12, 2019
8bf5310
enh:modularized dynamic kappa/rho plots
javiergcas Nov 12, 2019
0b44fa0
enh:docstrings added to KappaRho_DynPlot.py
javiergcas Nov 12, 2019
70bbe10
Force figures default, fold viz into reporting module
emdupre Nov 12, 2019
d834b30
enh:TS and Spectrum are now dynamic plots
javiergcas Nov 12, 2019
27f7ac8
Merge pull request #20 from javiergcas/dynamic_reports
emdupre Nov 12, 2019
ebea3c9
sty: initial refactor of dynamic figures
emdupre Nov 13, 2019
5ff0d3d
fix: re-introduce func to load comp_table
emdupre Nov 13, 2019
ed44834
sty: finalize rough refactor
emdupre Nov 13, 2019
7302e1e
fix: patch linting errors
emdupre Nov 13, 2019
d82e284
Merge remote-tracking branch 'upstream/master' into doc/me-reports
emdupre Nov 13, 2019
ef318b6
doc: continue refactor
emdupre Nov 14, 2019
632900b
Merge branch 'master' of https://github.com/me-ica/tedana into doc/me…
eurunuela Nov 15, 2019
96a896f
Advanced on dynamic_figures
eurunuela Nov 15, 2019
d1a417b
Made the refactored code work
eurunuela Nov 15, 2019
b9b9117
fix: new year, new approach
emdupre Jan 3, 2020
636d9ed
tmp: non-functioning reporting code
emdupre Jan 17, 2020
8986fab
Merge remote-tracking branch 'refs/remotes/origin/doc/me-reports' int…
emdupre Jan 17, 2020
bc668d0
Merge remote-tracking branch 'upstream/master' into doc/me-reports
emdupre Jan 21, 2020
748c9a9
Merge remote-tracking branch 'upstream/master' into doc/me-reports
emdupre Mar 20, 2020
8192fb2
Fixes bug
eurunuela Mar 21, 2020
d7c3745
Fixes bug
eurunuela Mar 21, 2020
ed3500a
Added new function to generate html report and removed tempita calls
eurunuela Apr 8, 2020
3ce5d4d
Added FFT and time series plots
eurunuela Apr 13, 2020
b66d097
Made template subsitute work and updated necessary bokeh version
eurunuela Apr 13, 2020
8a8fe7e
Changes html title
eurunuela Apr 13, 2020
b48aa7f
remove time series, fft plots
emdupre Apr 14, 2020
55e5900
fix: remove unescapes
emdupre Apr 14, 2020
2fd4683
fix: patch incorrect imports
emdupre Apr 14, 2020
46c3eb4
Merge remote-tracking branch 'upstream/master' into doc/me-reports
emdupre Apr 14, 2020
53b2248
fix: add missing bokeh import
emdupre Apr 14, 2020
de61e24
fix: spelling mistake 🤦
emdupre Apr 14, 2020
4f8be84
fix: reflect reorg in get_coeffs call
emdupre Apr 14, 2020
797f735
fix: correct expected test outputs
emdupre Apr 14, 2020
133b7e2
fix: don't designate file path when saving report
emdupre Apr 14, 2020
464bef0
fix: combine generate, html report functions
emdupre Apr 14, 2020
b66d578
fix: ignore missing columns in df.drop, to better handle multiple params
emdupre Apr 14, 2020
a8f9900
Dynamic figures are now two rows, static figure is on the right. Remo…
eurunuela Apr 17, 2020
131dc82
Added new line after References in report.txt
eurunuela Apr 17, 2020
1a4a174
Removes BeautifulSoup dependency in favor of an object tag and change…
eurunuela Apr 18, 2020
3f162a5
Merge pull request #23 from eurunuela/enh/dynamic_reports
emdupre Apr 18, 2020
72cd396
enh: switch to grid layout, style About section, add links to navbar
emdupre Apr 19, 2020
1363c01
doc: update no-png to no-reports
emdupre Apr 21, 2020
2b5a94b
modifiation of setup.py to properly install all reporting directories
javiergcas May 9, 2020
ba68bfd
Merge pull request #24 from javiergcas/doc/me-reports
emdupre May 10, 2020
05e91a4
Use relative path for static figures on report
eurunuela Jun 4, 2020
663f5eb
Merge pull request #25 from eurunuela/doc/me-reports
emdupre Jun 4, 2020
1b517b6
Add version check for bokeh figures
emdupre Jun 11, 2020
4817b8a
Merge branch 'doc/me-reports' of https://github.com/emdupre/tedana in…
emdupre Jun 11, 2020
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,8 @@ ENV/
.mypy_cache/

# vscode
.vscode
.vscode

# jupyter notebooks
.ipynb_checkpoints/
*.ipynb
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pandas
scikit-learn>=0.22
scipy>=1.3.3
threadpoolctl
bokeh
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hit an error on my first run, as my version of bokeh was apparently outdated, should this be set to specific version num? Updating from 1.3.4 (?!) to 2.0.1 fixed it.
For completion sake, error:

Traceback (most recent call last):
  File "/Users/logan/opt/anaconda3/bin/tedana", line 11, in <module>
    load_entry_point('tedana', 'console_scripts', 'tedana')()
  File "/Users/logan/local_code/tedana/tedana/workflows/tedana.py", line 671, in _main
    tedana_workflow(**kwargs)
  File "/Users/logan/local_code/tedana/tedana/workflows/tedana.py", line 616, in tedana_workflow
    reporting.generate_report(out_dir=out_dir, tr=img_t_r)
  File "/Users/logan/local_code/tedana/tedana/reporting/html_report.py", line 82, in generate_report
    kappa_rho_plot = df._create_kr_plt(comptable_cds)
  File "/Users/logan/local_code/tedana/tedana/reporting/dynamic_figures.py", line 151, in _create_kr_plt
    legend_group='classif')
  File "fakesource", line 5, in circle
  File "/Users/logan/opt/anaconda3/lib/python3.7/site-packages/bokeh/plotting/helpers.py", line 857, in func
    glyph = _make_glyph(glyphclass, kwargs, glyph_ca)
  File "/Users/logan/opt/anaconda3/lib/python3.7/site-packages/bokeh/plotting/helpers.py", line 398, in _make_glyph
    return glyphclass(**kws)
  File "/Users/logan/opt/anaconda3/lib/python3.7/site-packages/bokeh/model.py", line 307, in __init__
    super(Model, self).__init__(**kwargs)
  File "/Users/logan/opt/anaconda3/lib/python3.7/site-packages/bokeh/core/has_props.py", line 253, in __init__
    setattr(self, name, value)
  File "/Users/logan/opt/anaconda3/lib/python3.7/site-packages/bokeh/core/has_props.py", line 288, in __setattr__
    (name, self.__class__.__name__, text, nice_join(matches)))
AttributeError: unexpected attribute 'legend_group' to Circle, possible attributes are angle, angle_units, fill_alpha, fill_color, js_event_callbacks, js_property_callbacks, line_alpha, line_cap, line_color, line_dash, line_dash_offset, line_join, line_width, name, radius, radius_dimension, radius_units, size, subscribed_events, tags, x or y

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think version 1.4 is the minimum required but I'm not completely sure right now. Let me check.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, on OS X I got a working version. Would be good to set a minimum version in the .txt file though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was using an anaconda install, from not so long ago (~months). As you can see, the error message is super useless - so I just had to assume it was a version issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, if we require Bokeh 2.0.0 then we have to drop python 3.5 support (since it requires python 3.6+). How would you feel about that @tsalo ?

2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def main():
pkg_data = {
'tedana': [
'tests/data/*',
'reporting/data/*',
'reporting/data/html/*',
]
}

Expand Down
8 changes: 8 additions & 0 deletions tedana/reporting/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Reporting code for tedana
"""

from .html_report import generate_report
from .static_figures import comp_figures

__all__ = ['generate_report', 'comp_figures']
3 changes: 3 additions & 0 deletions tedana/reporting/data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory contains data required for tedana reporting.

html/ : HTML templates
Empty file.
25 changes: 25 additions & 0 deletions tedana/reporting/data/html/report_body_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<style type="text/css">
.content {
padding: 35px 20px 10px 20px;
margin-left: 50px;
margin-top: 50px;
height: 900px;
}
.about {
position: relative;
top: -50px;
margin-left: 5%;
margin-right: 5%;
white-space: pre-line;
font-family: Arial, Helvetica, sans-serif;
font-size: larger;
}
</style>
<div class="content">
$content
</div>
<div class="about">
$about
</div>

$javascript
38 changes: 38 additions & 0 deletions tedana/reporting/data/html/report_head_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>tedana report</title>
<meta name="viewport" content="width = device-width, initial-scale = 1" charset="UTF-8">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-2.0.1.min.js"
crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.0.1.min.js"
crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.0.1.min.js"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/build/pure-min.css"
integrity="sha384-oAOxQR6DkCoMliIh8yFnu25d7Eq/PHS21PClpwjOTeU2jRSq11vu66rf90/cZr47" crossorigin="anonymous">
<!-- CSS for the report -->
<style type="text/css">
body {
padding: 10px 20px 10px 20px;
}
</style>

</head>

<nav>
<div class="pure-menu pure-menu-horizontal">
<a href="https://github.com/ME-ICA/tedana" class="pure-menu-heading pure-menu-link">tedana $version</a>
<ul class="pure-menu-list">
<li class="pure-menu-item">
<a href="https://tedana.readthedocs.io" class="pure-menu-link">Reporting documentation</a>
</li>
</ul>
</div>
</nav>

<body>
$body
</body>
</html>
284 changes: 284 additions & 0 deletions tedana/reporting/dynamic_figures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
import numpy as np
from math import pi
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from bokeh import (events, models, plotting, transform)

color_mapping = {'accepted': '#2ecc71',
'rejected': '#e74c3c',
'ignored': '#3498db'}

tap_callback_jscode = """
// Accessing the selected component ID
var data = source_comp_table.data;
var selected_idx = source_comp_table.selected.indices;
if(selected_idx > 0) {
// A component has been selected
// -----------------------------
var components = data['component']
var selected = components[selected_idx]
var selected_padded = '' + selected;
while (selected_padded.length < 2) {
selected_padded = '0' + selected_padded;
}
var selected_padded_forIMG = '0' + selected_padded
var selected_padded_C = 'ica_' + selected_padded

// Find color for selected component
var colors = data['color']
var this_component_color = colors[selected_idx]

// Image Below Plots
div.text = ""
var line = "<span><img src='./figures/comp_"+selected_padded_forIMG+".png'" +
" alt='Component Map'><span>\\n";
console.log('Linea: ' + line)
var text = div.text.concat(line);
var lines = text.split("\\n")
if (lines.length > 35)
lines.shift();
div.text = lines.join("\\n");

} else {
// No component has been selected
// ------------------------------
// Set Component color to Black
var this_component_color = '#000000'

// Image Below Plots
div.text = ""
var line = "<p>Please select an individual component to view it in more detail</p>\\n"
var text = div.text.concat(line);

}
"""


def _create_data_struct(comptable_path, color_mapping=color_mapping):
"""
Create Bokeh ColumnDataSource with all info dynamic plots need

Parameters
----------
comptable: str
file path to component table, JSON format

Returns
-------
cds: bokeh.models.ColumnDataSource
Data structure with all the fields to plot or hover over
"""
unused_cols = ['normalized variance explained',
'countsigFR2', 'countsigFS0',
'dice_FS0', 'countnoise', 'dice_FR2',
'signal-noise_t', 'signal-noise_p',
'd_table_score', 'kappa ratio',
'rationale', 'd_table_score_scrub']

df = pd.read_json(comptable_path)
df.drop('Description', axis=0, inplace=True)
df.drop('Method', axis=1, inplace=True)
df = df.T
n_comps = df.shape[0]

# remove space from column name
df.rename(columns={'variance explained': 'var_exp'}, inplace=True)

# For providing sizes based on Var Explained that are visible
mm_scaler = MinMaxScaler(feature_range=(4, 20))
df['var_exp_size'] = mm_scaler.fit_transform(
df[['var_exp', 'normalized variance explained']])[:, 0]

# Calculate Kappa and Rho ranks
df['rho_rank'] = df['rho'].rank(ascending=False).values
df['kappa_rank'] = df['kappa'].rank(ascending=False).values
df['var_exp_rank'] = df['var_exp'].rank(ascending=False).values

# Remove unused columns to decrease size of final HTML
# set errors to 'ignore' in case some columns do not exist in
# a given data frame
df.drop(unused_cols, axis=1, inplace=True, errors='ignore')

# Create additional Column with colors based on final classification
df['color'] = [color_mapping[i] for i in df['classification']]

# Create additional column with component ID
df['component'] = np.arange(n_comps)

# Compute angle and re-sort data for Pie plots
df['angle'] = df['var_exp'] / df['var_exp'].sum() * 2 * pi
df.sort_values(by=['classification', 'var_exp'], inplace=True)

cds = models.ColumnDataSource(data=dict(
kappa=df['kappa'],
rho=df['rho'],
varexp=df['var_exp'],
kappa_rank=df['kappa_rank'],
rho_rank=df['rho_rank'],
varexp_rank=df['var_exp_rank'],
component=[str(i) for i in df['component']],
color=df['color'],
size=df['var_exp_size'],
classif=df['classification'],
angle=df['angle']))

return cds


def _create_kr_plt(comptable_cds):
"""
Create Dymamic Kappa/Rho Scatter Plot

Parameters
----------
comptable_cds: bokeh.models.ColumnDataSource
Data structure containing a limited set of columns from the comp_table

Returns
-------
fig: bokeh.plotting.figure.Figure
Bokeh scatter plot of kappa vs. rho
"""
# Create Panel for the Kappa - Rho Scatter
kr_hovertool = models.HoverTool(tooltips=[('Component ID', '@component'),
('Kappa', '@kappa{0.00}'),
('Rho', '@rho{0.00}'),
('Var. Expl.', '@varexp{0.00}%')])
fig = plotting.figure(plot_width=400, plot_height=400,
tools=["tap,wheel_zoom,reset,pan,crosshair,save", kr_hovertool],
title="Kappa / Rho Plot")
fig.circle('kappa', 'rho', size='size', color='color', alpha=0.5, source=comptable_cds,
legend_group='classif')
fig.xaxis.axis_label = 'Kappa'
fig.yaxis.axis_label = 'Rho'
fig.toolbar.logo = None
fig.legend.background_fill_alpha = 0.5
fig.legend.orientation = 'horizontal'
fig.legend.location = 'bottom_right'
return fig


def _create_sorted_plt(comptable_cds, n_comps, x_var, y_var, title=None,
x_label=None, y_label=None):
"""
Create dynamic sorted plots

Parameters
----------
comptable_ds: bokeh.models.ColumnDataSource
Data structure containing a limited set of columns from the comp_table

x_var: str
Name of variable for the x-axis

y_var: str
Name of variable for the y-axis

title: str
Plot title

x_label: str
X-axis label

y_label: str
Y-axis label

Returns
-------
fig: bokeh.plotting.figure.Figure
Bokeh plot of components ranked by a given feature
"""
hovertool = models.HoverTool(tooltips=[('Component ID', '@component'),
('Kappa', '@kappa{0.00}'),
('Rho', '@rho{0.00}'),
('Var. Expl.', '@varexp{0.00}%')])
fig = plotting.figure(plot_width=400, plot_height=400,
tools=["tap,wheel_zoom,reset,pan,crosshair,save", hovertool],
title=title)
fig.line(x=np.arange(1, n_comps + 1),
y=comptable_cds.data[y_var].sort_values(ascending=False).values,
color='black')
fig.circle(x_var, y_var, source=comptable_cds,
size=5, color='color', alpha=0.7)
fig.xaxis.axis_label = x_label
fig.yaxis.axis_label = y_label
fig.x_range = models.Range1d(-1, n_comps + 1)
fig.toolbar.logo = None

return fig


def _create_varexp_pie_plt(comptable_cds, n_comps):
fig = plotting.figure(plot_width=400, plot_height=400, title='Variance Explained View',
tools=['hover,tap,save'],
tooltips=[('Component ID', ' @component'),
('Kappa', '@kappa{0.00}'),
('Rho', '@rho{0.00}'),
('Var. Exp.', '@varexp{0.00}%')])
fig.wedge(x=0, y=1, radius=.9,
start_angle=transform.cumsum('angle', include_zero=True),
end_angle=transform.cumsum('angle'),
line_color="white",
fill_color='color', source=comptable_cds, fill_alpha=0.7)
fig.axis.visible = False
fig.grid.visible = False
fig.toolbar.logo = None

circle = models.Circle(x=0, y=1, size=150, fill_color='white', line_color='white')
fig.add_glyph(circle)

return fig


def _tap_callback(comptable_cds, div_content, out_dir):
"""
Javacript function to animate tap events and show component info on the right

Parameters
----------
CDS: bokeh.models.ColumnDataSource
Data structure containing a limited set of columns from the comp_table
div: bokeh.models.Div
Target Div element where component images will be loaded

Returns
-------
CustomJS: bokeh.models.CustomJS
Javascript function that adds the tapping functionality
"""
return models.CustomJS(args=dict(source_comp_table=comptable_cds,
div=div_content,
outdir=out_dir), code=tap_callback_jscode)


def _link_figures(fig, comptable_ds, div_content, out_dir):
"""
Links figures and adds interaction on mouse-click.

Parameters
----------
fig : bokeh.plotting.figure
Figure containing a given plot

comptable_ds : bokeh.models.ColumnDataSource
Data structure with a limited version of the comptable
suitable for dynamic plot purposes

div_content : bokeh.models.Div
Div element for additional HTML content.

out_dir : str
Output directory of tedana results.

Returns
-------
fig : bokeh.plotting.figure
Same as input figure, but with a linked method to
its Tap event.

"""
fig.js_on_event(events.Tap,
_tap_callback(comptable_ds,
div_content,
out_dir))
return fig
Loading