Applying Layers to Quantitative Maps

The primary way of getting data from a layers object is as a Pandas DataFrame. Your quantitative map and mask/anatomical image don’t need to have the same field of view and voxel size, they are resampled to a common space as part of the add_map method. The space parameter of the QLayers class dictates whether any maps added to the object are resampled of the same space as the layers (space=layers) or if the layers are resampled to the native space of the map (space=map). The advantage of working in layer space is that you can produce a wide DataFrame where each row represents a region of tissue in the image and allows you to directly compare the depth and multiple quantitative parameters however it does involve a resampling operation to the quantitative parameter which may not be desirable (especially for noisy data). Working with space=map only allows you to produce a long DataFrame where each row is a single voxel of each quantitative map. Here we’re going to work with space=layers and return both formats of DataFrame so you can see the difference. Most analysis is easier with a wide DataFrame so we’ll save that variable for use in subsequent steps. Start by importing the necessary libraries.

[1]:
import matplotlib.pyplot as plt
import nibabel as nib
import numpy as np
import seaborn as sns

from qlayers import QLayers

sns.set()

Now we’re going to import a mask and generate layers with a thickness of 1 mm excluding tissue within 10 mm of the pelvis.

[2]:
mask_img = nib.load('data/kidney_mask.nii.gz')
qlayers = QLayers(mask_img, thickness=1, pelvis_dist=10, space='layers')
r2star_map = nib.load('data/r2star.nii.gz')
t1_map = nib.load('data/t1_registered.nii.gz')
qlayers.add_map(r2star_map, 'r2star')
qlayers.add_map(t1_map, 't1')
Making Mesh
Smoothing Mesh
Distance Calculation: 100%|██████████| 3/3 [00:10<00:00,  3.65s/it]
Pelvis Distance Calculation: 100%|██████████| 3/3 [00:11<00:00,  3.81s/it]
[3]:
qlayers.df_long
[3]:
depth layer measurement value
0 0.691532 1.0 r2star 35.173016
1 0.640596 1.0 r2star 35.546364
2 0.885229 1.0 r2star 34.576439
3 0.680305 1.0 r2star 33.275753
4 0.622988 1.0 r2star 27.070265
... ... ... ... ...
25993 1.011228 2.0 t1 2372.752930
25994 1.099274 2.0 t1 1094.152222
25995 0.145987 1.0 t1 1351.835815
25996 1.185435 2.0 t1 922.929932
25997 0.378006 1.0 t1 1491.548584

51996 rows × 4 columns

DataFrames

Here is an example of how to retrieve a layers dataframe in long format. Note that every measurement is on a different row of the DataFrame.

[4]:
print('An example of a long DataFrame')
qlayers.get_df('long').sample(n=100)
An example of a long DataFrame
[4]:
depth layer measurement value
1452 7.921090 8.0 t1 1438.690796
23167 3.070961 4.0 r2star 16.509024
9873 9.106622 10.0 r2star 19.951300
15462 7.044881 8.0 t1 1522.299683
21780 9.554491 10.0 t1 1860.597168
... ... ... ... ...
5030 8.614227 9.0 r2star -0.000061
16761 11.531033 12.0 t1 1811.737183
17264 3.927006 4.0 r2star -0.000005
19380 10.379864 11.0 r2star 27.924768
83 0.151749 1.0 t1 1996.951416

100 rows × 4 columns

And now an example of a wide DataFrame. Here each row represents properties of a single region of tissue i.e. the \(T_1\) and \(R_2^*\) of the same region.

[5]:
df = qlayers.get_df('wide')
# A little bit of data cleaning
df = df.loc[df['r2star'] > 0.1]
df = df.loc[df['t1'] > 0.1]
df = df.loc[df['layer'] > 0]
df = df.dropna()
print('An example of a wide DataFrame')
df.sample(n=100)
An example of a wide DataFrame
[5]:
depth layer r2star t1
18182 2.479600 3.0 49.999969 830.443848
25187 7.304027 8.0 17.250000 1491.552002
981 7.609881 8.0 18.200880 1696.885132
225 3.661174 4.0 16.167454 1498.427856
9813 5.483145 6.0 17.793419 1505.549683
... ... ... ... ...
9085 3.738994 4.0 16.756792 2189.016602
14667 2.410548 3.0 18.561234 648.623413
4872 6.246225 7.0 20.113871 1326.467773
12614 5.033549 6.0 18.116539 1553.667480
21237 14.467604 15.0 27.227434 1818.611694

100 rows × 4 columns

Layer Statistics

These large voxel-by-voxel DataFrames can be used to generate statistics for each layer.

[6]:
df[['layer', 'r2star', 't1']].groupby('layer').agg(('mean', 'median', 'std', np.count_nonzero))
[6]:
r2star t1
mean median std count_nonzero mean median std count_nonzero
layer
1.0 28.777927 27.386422 10.120994 1306 1427.316554 1432.761292 687.366178 1306
2.0 24.798214 21.119667 9.922550 1152 1514.067368 1498.551514 521.878771 1152
3.0 22.699811 18.791880 9.507372 1117 1548.529686 1502.680298 418.154056 1117
4.0 21.311056 17.593157 8.999270 1079 1584.577592 1504.680908 387.597601 1079
5.0 20.659930 17.642798 8.391686 1070 1555.793643 1515.112427 245.470044 1070
6.0 20.322622 17.716091 8.013432 998 1535.698166 1518.424866 183.277963 998
7.0 20.008837 17.834057 6.875631 939 1556.361710 1526.548950 222.644851 939
8.0 20.357808 18.393726 6.256812 939 1590.342322 1545.794434 261.664944 939
9.0 21.486777 18.754271 7.210381 1249 1647.641340 1608.914917 251.010749 1249
10.0 21.896243 19.833168 6.528540 973 1668.680325 1629.775635 242.975416 973
11.0 22.848923 20.875158 6.762447 800 1699.698935 1657.771057 270.711344 800
12.0 24.367501 22.374521 7.467851 780 1724.054959 1686.763916 314.218625 780
13.0 25.761510 23.751545 8.236693 681 1768.263152 1727.257935 334.313298 681
14.0 27.090914 25.005579 8.590587 627 1816.391434 1759.498901 373.740812 627
15.0 26.376643 24.508926 8.273556 937 1794.624822 1747.250732 358.172194 937
16.0 26.683109 25.606609 7.597819 489 1911.763796 1814.361206 469.923051 489
17.0 26.739680 25.025603 8.242154 370 1903.686099 1863.104370 428.545985 370
18.0 27.080702 26.185752 8.125888 246 1885.025251 1872.476135 404.513906 246
19.0 27.964653 26.761284 8.542669 167 1854.224052 1826.735474 416.656298 167
20.0 27.312497 26.466608 7.030809 164 1822.542544 1736.938843 416.622615 164
21.0 26.563904 26.340168 7.246280 65 2048.189515 1752.625488 643.742097 65
22.0 32.333422 30.151463 11.298019 25 1842.424917 1678.638062 394.083905 25
23.0 33.817001 29.618130 9.046841 5 1972.630908 1783.244507 534.599383 5
[7]:
fig, ax = plt.subplots()
sns.lineplot(df, x='layer', y='r2star', ax=ax, estimator='median')
ax.set_xlabel('Depth (mm)')
ax.set_ylabel('$R_2^*$ (Hz)')
ax.set_ylim((15, 35))
ax.axvspan(0, 5, alpha=0.2, label='Outer Layers', color='C2')
ax.axvspan(15, 23, alpha=0.2, label='Inner Layers', color='C5')
ax.legend()
[7]:
<matplotlib.legend.Legend at 0x1b6ed946790>
../_images/examples_applying_layers_to_quantitative_maps_11_1.png