Cell phenotyping

Hyperspectral unmixing of Raman spectroscopic data to analyse the biomolecular composition of cells. Data from [1].

Prerequisites

import ramanspy
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.pyplot as plt
import random

Set random seed for reproducibility

random.seed(12345)

Data loading

We load the data corresponding to THP-1 cells from [1] and select the first cell volume.

dir_ = r'../../../data/kallepitis_data'

volumes = ramanspy.datasets.volumetric_cells(cell_type='THP-1', folder=dir_)

# select the first volume
volume = volumes[0]

Preprocessing

We define a preprocessing pipeline to apply to the volume.

preprocessing_pipeline = ramanspy.preprocessing.Pipeline([
    ramanspy.preprocessing.misc.Cropper(region=(700, 1800)),
    ramanspy.preprocessing.despike.WhitakerHayes(),
    ramanspy.preprocessing.denoise.SavGol(window_length=7, polyorder=3),
    ramanspy.preprocessing.baseline.ASLS(),
    ramanspy.preprocessing.normalise.MinMax(pixelwise=False),
])

preprocessed_volume = preprocessing_pipeline.apply(volume)

Visualising the effect of plotting.

selected_image_layer = 5
selected_spectrum_index = (15, 25, selected_image_layer)

bands = [789, 1008, 1303]
band_components = ['DNA', 'Protein', 'Lipids']

labels = [f'{comp}\n{band} cm$^{{{-1}}}$' for band, comp in zip(bands, band_components)]

Data before preprocessing.

ax = ramanspy.plot.spectra(volume[selected_spectrum_index])
Raman spectra

Data before preprocessing with fingerprint region highlighted.

plt.subplots(figsize=(4, 3))
ax = ramanspy.plot.spectra(volume[selected_spectrum_index], title="Raw spectrum")
ax.axvspan(700, 1800, alpha=0.25, color='red', zorder=0)

ax.axvline(700, linestyle='--', c='red', zorder=0)
ax.text(730, .95, 700, transform=ax.get_xaxis_transform())
ax.axvline(1800, linestyle='--', c='red', zorder=0)
ax.text(1460, .95, 1800, transform=ax.get_xaxis_transform())
plt.show()
Raw spectrum

The raw data from the fingerprint region.

cropped = ramanspy.preprocessing.misc.Cropper(region=(700, 1800)).apply(volume[selected_spectrum_index])

ax = ramanspy.plot.spectra(cropped, title="Raw spectrum (zoomed in)")
Raw spectrum (zoomed in)

Fingerprint region data after preprocessing.

ax = ramanspy.plot.spectra(preprocessed_volume[selected_spectrum_index], title="Preprocessed spectrum", ylabel="Normalised intensity")
Preprocessed spectrum

Plotting spectral slices across relevant bands corresponding to biomolecular components, such as DNA, protein and lipids.

axs = ramanspy.plot.volume([preprocessed_volume.band(band) for band in bands], title=labels)
  • DNA 789 cm$^{-1}$
  • Protein 1008 cm$^{-1}$
  • Lipids 1303 cm$^{-1}$
ax = ramanspy.plot.volume(preprocessed_volume.band(bands[1]), title=labels[1])
Protein 1008 cm$^{-1}$
ramanspy.plot.image([preprocessed_volume.layer(selected_image_layer).band(band) for band in bands], title=labels)
  • DNA 789 cm$^{-1}$
  • Protein 1008 cm$^{-1}$
  • Lipids 1303 cm$^{-1}$
[<Axes: title={'center': 'DNA\n789 cm$^{-1}$'}>, <Axes: title={'center': 'Protein\n1008 cm$^{-1}$'}>, <Axes: title={'center': 'Lipids\n1303 cm$^{-1}$'}>]
ax = ramanspy.plot.image(preprocessed_volume.layer(selected_image_layer).band(bands[1]), title=labels[1])
Protein 1008 cm$^{-1}$

Spectral unmixing

We use the N-FINDR [2] algorithm to unmix the volume into endmembers and FCLS [3] to derive the corresponding abundance maps.

nfindr_unmixer = ramanspy.analysis.unmix.NFINDR(n_endmembers=5)
abundance_maps, endmembers = nfindr_unmixer.apply(preprocessed_volume)

Plotting results

Plotting the derived endmembers.

ax = ramanspy.plot.spectra(endmembers, wavenumber_axis=preprocessed_volume.spectral_axis, plot_type='single stacked')
Raman spectra

Plotting a selection of endmembers that are representative of the different biomolecular components with relevant peaks used to identify the components highlighted.

selected_indices = [0, 1, 3, 4]
labels_ = ['Lipids', 'Nucleus', 'Cytoplasm', 'Background']

selected_endmembers = [endmembers[i] for i in selected_indices]
selected_abundances = [abundance_maps[i] for i in selected_indices]
plt.figure(figsize=(10, 5))

ax = ramanspy.plot.spectra(selected_endmembers, wavenumber_axis=preprocessed_volume.spectral_axis, plot_type='single stacked', label=labels_, title='Endmembers')

peaks = [789, 1008, 1066, 1134, 1303, 1443, 1747]

ax.axvline(789, linestyle='--', c='black', zorder=0)
ax.text(725, .95, 789, transform=ax.get_xaxis_transform())

ax.axvline(1008, linestyle='--', c='black', zorder=0)
ax.text(930, .9, 1008, transform=ax.get_xaxis_transform())

ax.axvline(1066, linestyle='--', c='black', zorder=0)
ax.text(1027, .95, 1066, transform=ax.get_xaxis_transform())

ax.axvline(1134, linestyle='--', c='black', zorder=0)
ax.text(1145, .9, 1134, transform=ax.get_xaxis_transform())

ax.axvline(1303, linestyle='--', c='black', zorder=0)
ax.text(1310, .95, 1303, transform=ax.get_xaxis_transform())

ax.axvline(1443, linestyle='--', c='black', zorder=0)
ax.text(1450, .95, 1443, transform=ax.get_xaxis_transform())

ax.axvline(1747, linestyle='--', c='black', zorder=0)
ax.text(1660, .95, 1747, transform=ax.get_xaxis_transform())
plt.show()
Endmembers

Plotting the abundance maps corresponding to the selected endmembers.

axs = ramanspy.plot.volume(selected_abundances, title=labels_, cbar=False)
  • Lipids
  • Nucleus
  • Cytoplasm
  • Background
axs = ramanspy.plot.image([abundance_map[..., selected_image_layer] for abundance_map in selected_abundances], title=labels_, cbar=False)
  • Lipids
  • Nucleus
  • Cytoplasm
  • Background

Plotting a merged reconstruction of the selected image slice by plotting the abundance maps in one plot.

fig, ax = plt.subplots()

cmap = plt.cm.get_cmap()(np.linspace(0, 1, len(selected_abundances)))

white = [1, 1, 1, 0]

order = ['Background', 'Cytoplasm', 'Nucleus', 'Lipids']
for label in order:
    i = labels_.index(label)
    ax.imshow(selected_abundances[i][..., selected_image_layer], cmap=LinearSegmentedColormap.from_list('', [white, cmap[i]]))

ax.set_title('Merged')

plt.show()
Merged

References

Total running time of the script: ( 0 minutes 51.544 seconds)