Arjan Geers
This notebook reproduces* the data analysis presented in:
Geers AJ, Larrabide I, Radaelli AG, Bogunovic H, Kim M, Gratama van Andel HAF, Majoie CB, VanBavel E, Frangi AF. Patient-specific computational hemodynamics of intracranial aneurysms from 3D rotational angiography and CT angiography: An in vivo reproducibility study. American Journal of Neuroradiology, 32(3):581–586, 2011.
The goal of the study was to determine the reproducibility of blood flow simulations of cerebral aneurysms. Patients with a total of 10 cerebral aneurysms were imaged with both 3D rotational angiography (3DRA) and computed tomographic angiography (CTA). Each image independently was segmented to obtain a vascular model, the same boundary conditions were imposed, and a CFD simulation was obtained.
*Originally, data was analyzed in MATLAB R2010b and the boxplot was created in Mathematica 7.
In [ ]:
%matplotlib inline
In [ ]:
import os
import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plt
The data used in this notebook is also available on FigShare:
Geers AJ, Larrabide I, Radaelli AG, Bogunovic H, Kim M, Gratama van Andel HAF, Majoie CB, VanBavel E, Frangi AF. Reproducibility of hemodynamic simulations of cerebral aneurysms across imaging modalities 3DRA and CTA: Geometric and hemodynamic data. FigShare, 2015. DOI: 10.6084/m9.figshare.1354056
Variables are defined as follows (TA: time-averaged; PS: peak systole; ED: end diastole):
In [ ]:
df_input = pd.read_csv(os.path.join('data', '3dracta.csv'), index_col=[0, 1])
df_input
Extract separate dataframes for 3DRA and CTA.
In [ ]:
df_3dra = df_input.xs('3dra', level='modality')
df_cta = df_input.xs('cta', level='modality')
Calculate the relative difference between 3DRA and CTA wrt 3DRA. Per variable, get the mean and standard error of this relative difference over all aneurysms.
In [ ]:
df_reldiff = 100 * abs(df_3dra - df_cta)/df_3dra
s_mean = df_reldiff.mean()
s_standarderror = pd.Series(stats.sem(df_reldiff), index=df_input.columns)
Note: MATLAB was used to perform this test for the paper. Its 'signrank' function defaults to using the 'exact method' if a dataset has 15 or fewer observations and the 'approximate method' otherwise. See the documentation for more details. SciPy's 'wilcoxon' function has currently (version 1.3.0) no equivalent option and always uses the 'approximate method'.
In [ ]:
pvalue = np.empty(len(df_input.columns))
for i, variable in enumerate(df_input.columns):
pvalue[i] = stats.wilcoxon(df_3dra[variable], df_cta[variable])[1]
s_pvalue = pd.Series(pvalue, index=df_input.columns)
Determine the number of aneurysms for which a variable is lower for CTA than for 3DRA.
In [ ]:
numberofcases = np.empty(len(df_input.columns))
for i, variable in enumerate(df_input.columns):
numberofcases[i] = sum(df_3dra.loc[j, variable] > df_cta.loc[j, variable]
for j in df_input.index.levels[0])
s_numberofcases = pd.Series(numberofcases, index=df_input.columns)
Compose a dataframe with the obtained statistical results, corresponding to the 'online table' of the journal paper.
In [ ]:
d = {'M': s_numberofcases,
'P': s_pvalue,
'Mean (%)': s_mean,
'SE (%)': s_standarderror}
df_output = pd.DataFrame(d, columns=['M', 'P', 'Mean (%)', 'SE (%)'])
df_output
Make boxplots showing the distributions of the relative differences over all aneurysms.
In [ ]:
# extract arrays to plot from dataframe
array_yticklabels = ['$\mathregular{' + variable.replace('%', '\%') + '}$'
for variable in df_reldiff.columns]
array_reldiff = df_reldiff.values
# create plot
fig, ax = plt.subplots()
bp = ax.boxplot(array_reldiff, sym='+', vert=0, patch_artist=True)
# set labels
ax.set_xlabel('Relative difference (%)', fontsize=18)
ax.set_xlim(0, 130)
ax.set_yticklabels(array_yticklabels, fontsize=12)
# format box, whiskers, etc.
plt.setp(ax.get_xticklabels(), fontsize=12)
plt.setp(bp['boxes'], color='black')
plt.setp(bp['medians'], color='white')
plt.setp(bp['whiskers'], color='black', linestyle='-')
plt.setp(bp['fliers'], color='black', markersize=5)
plt.tight_layout()
In [ ]: