Skip to content

Commit

Permalink
Spectral: Use spectra.ID rather than index
Browse files Browse the repository at this point in the history
Spectra: Add ability to modify Elevation/Zoom/Rotation for 3D
Spectra: Add Style modifier to view as line/bar/contour
Spectra: Add Hash() method to consolidate input hash generation

Spectra: Fix Improper, Premature iteration termination
Spectra: Add proper labeling to Distance Matrix
  • Loading branch information
kkernick committed Jul 30, 2024
1 parent e50ccf3 commit 5478117
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 74 deletions.
2 changes: 1 addition & 1 deletion 3d/example_input/FinalBaseMesh.obj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# s Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 21.08.2014 17:37:51

mtllib FinalBaseMesh.mtl
Expand Down
2 changes: 1 addition & 1 deletion site/spectral/app.json

Large diffs are not rendered by default.

178 changes: 107 additions & 71 deletions spectral/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
from pymzml.run import Reader
from pandas import DataFrame
from scipy.spatial.distance import squareform
from numpy import zeros, unique, array, concatenate, asarray, hstack, column_stack, newaxis, full_like
from numpy import zeros, unique, array, concatenate, asarray, hstack, column_stack, newaxis, full_like, zeros_like

from shared import Cache, MainTab, NavBar, FileSelection, Filter, ColumnType, TableOptions, InitializeConfig, ColorMaps, Update, Msg, File, InterpolationMethods
from shared import Cache, MainTab, NavBar, FileSelection, Filter, ColumnType, TableOptions, InitializeConfig, ColorMaps, Update, Msg, File, InterpolationMethods, Error

try:
from user import config
Expand All @@ -43,6 +43,39 @@ def server(input, output, session):
"1min.mzml": "An Example mzML from https://github.com/HUPO-PSI/mzML"
}


def Hash():
tab = input.MainTab()
if tab == "HeatmapTab":
return [
File(input),
config.ColorMap(),
config.Opacity(),
config.Features(),
config.TextSize(),
config.ID(),
config.Peaks(),
config.DPI(),
config.Width(),
config.Style(),
config.Elevation(),
config.Zoom(),
config.Rotation(),
input.mode(),
]
elif tab == "SimilarityTab":
return [
File(input),
config.ColorMap(),
config.Features(),
config.TextSize(),
config.ID(),
config.DPI(),
config.Interpolation(),
input.mode(),
]


def HandleData(path, p=None):
"""
@brief A custom Data Handler for the Cache.
Expand All @@ -68,7 +101,13 @@ async def UpdateData():

reader = Data()
if reader is None: return
ui.update_select(id="Index", selected=[0], choices=list(range(reader.get_spectrum_count())))

ids = set()
first = None
for spectra in reader:
ids.add(spectra.ID)
if first is None: first = spectra.ID
ui.update_select(id="ID", selected=[first], choices=list(ids))


def GetData(): return Table.data_view() if Valid() else Data()
Expand Down Expand Up @@ -139,10 +178,10 @@ def ColoredLine(x, y, c, ax, z=None, **lc_kwargs):
def SpectraHeatmap(reader, p):

peaks = config.Peaks().lower()
indicies = [int(i) for i in config.Index()]
indices = [int(i) for i in config.ID()]

if len(indicies) == 1:
spectra = indicies[0]
if len(indices) == 1:
spectra = indices[0]

# Collect spectrum attributes
mz_values = []
Expand All @@ -151,19 +190,27 @@ def SpectraHeatmap(reader, p):

# This is the "Pythonic" way of doing things, because
# Heaven forbid we just index the values we want.
for i, spectrum in enumerate(reader):
print(spectrum.scan_time)
if i == spectra:
for spectrum in reader:
if spectrum.ID == spectra:
for mz, intensity in spectrum.peaks(peaks):
mz_values.append(mz)
intensities.append(intensity)
break

p.inc(message="Plotting...")
color = input.mode()
with style.context('dark_background' if color == "dark" else "default"):
fig, ax = subplots()
plot = ColoredLine(mz_values, intensities, intensities, ax)

if config.Style() == "Line":
plot = ColoredLine(mz_values, intensities, intensities, ax)
elif config.Style() == "Bar":
cmap = get_cmap(config.ColorMap().lower())
norm = Normalize(vmin=min(intensities), vmax=max(intensities))
plot = ax.bar(mz_values, intensities, color=cmap(norm(intensities)), width=config.Width())
else:
Error("Contour Style is only supported when more than one spectra ID is specified!")
return None, None, None

ax.set_xlim(min(mz_values), max(mz_values))
ax.set_ylim(min(intensities), max(intensities))

Expand All @@ -172,10 +219,10 @@ def SpectraHeatmap(reader, p):
else:
fig, ax = subplots(subplot_kw={"projection": "3d"})

x_min, x_max, y_min, y_max, z_min, z_max = 0, 0, 0, len(indicies), 0, 0
x_min, x_max, y_min, y_max, z_min, z_max = 0, 0, 0, len(indices), 0, 0

for i, spectrum in enumerate(reader):
if i in indicies:
for spectrum in reader:
if spectrum.ID in indices:
mz_values = []
intensities = []
for mz, intensity in spectrum.peaks(peaks):
Expand All @@ -187,16 +234,25 @@ def SpectraHeatmap(reader, p):
z_min = min(z_min, min(intensities))
z_max = max(z_max, max(intensities))

position = [float(indicies.index(i))] * len(mz_values)

position = [float(indices.index(spectrum.ID))] * len(mz_values)
cmap = get_cmap(config.ColorMap().lower())

# Plot the line for each spectrum
plot = ColoredLine(mz_values, position, intensities, ax, cmap=config.ColorMap().lower(), z=intensities)
if config.Style() == "Line":
plot = ColoredLine(mz_values, position, intensities, ax, cmap=config.ColorMap().lower(), z=intensities)
elif config.Style() == "Bar":
z = intensities
norm = Normalize(vmin=min(intensities), vmax=max(intensities))
c = norm(intensities)
width = depth = 1
plot = ax.bar3d(mz_values, position, zeros_like(mz_values), width, depth, z, color=cmap(c))


ax.set_xlim(x_min, x_max)
ax.set_ylim(y_min, y_max)
ax.set_zlim(z_min, z_max)
ax.view_init(elev=config.Elevation(), azim=config.Rotation())
ax.set_box_aspect(None, zoom=config.Zoom())

if "z" in config.Features():
ax.tick_params(axis="z", labelsize=config.TextSize())
Expand All @@ -206,17 +262,9 @@ def SpectraHeatmap(reader, p):


def GenerateSimilarity():
inputs = [
File(input),
config.ColorMap(),
config.Features(),
config.TextSize(),
config.Index(),
config.DPI(),
config.Interpolation(),
input.mode(),
]
if input.MainTab() != "SimilarityTab": return

inputs = Hash()
if not DataCache.In(inputs):
with ui.Progress() as p:
p.inc(message="Loading input...")
Expand All @@ -226,21 +274,22 @@ def GenerateSimilarity():

distances = {}

indices = [int(i) for i in config.Index()]
indices = [int(i) for i in config.ID()]
spectra = []
for s in reader:
if s.ID in indices: spectra.append(s)

for i, s in enumerate(reader):
if i in indices:
distances[i] = {}
for x, s2 in enumerate(reader):
if x in indices:
if x == i:
distances[i][x] = 1.0
elif x in distances:
distances[i][x] = distances[x][i]
else:
distances[i][x] = s.similarity_to(s2)
for s in spectra:
distances[s.ID] = {}
for s2 in spectra:
if s.ID == s2.ID:
distances[s.ID][s2.ID] = 1.0
elif s2.ID in distances:
distances[s.ID][s2.ID] = distances[s2.ID][s.ID]
else:
distances[s.ID][s2.ID] = s.similarity_to(s2)

df = DataFrame(distances)
df = DataFrame(distances, columns=indices, index=indices)

fig, ax = subplots()
interpolation = config.Interpolation().lower()
Expand All @@ -253,13 +302,18 @@ def GenerateSimilarity():
cbar.ax.tick_params(labelsize=config.TextSize())
except Exception: pass

if "y" in config.Features(): ax.tick_params(axis="y", labelsize=config.TextSize())
if "y" in config.Features():
ax.set_yticklabels(df.columns)
ax.set_yticks(range(len(df.columns)))
ax.tick_params(axis="y", labelsize=config.TextSize())
else: ax.set_yticklabels([])

if "x" in config.Features(): ax.tick_params(axis="x", labelsize=config.TextSize())
if "x" in config.Features():
ax.set_xticklabels(df.columns)
ax.set_xticks(range(len(df.columns)))
ax.tick_params(axis="x", labelsize=config.TextSize())
else: ax.set_xticklabels([])


b = BytesIO()
fig.savefig(b, format="png", dpi=config.DPI())
b.seek(0)
Expand All @@ -274,19 +328,9 @@ def GenerateSimilarity():


def GenerateHeatmap():
inputs = [
File(input),
config.ColorMap(),
config.Opacity(),
config.Features(),
config.TextSize(),
config.Index(),
config.Peaks(),
config.DPI(),
config.Width(),
input.mode(),
]
if input.MainTab() != "HeatmapTab": return

inputs = Hash()
if not DataCache.In(inputs):
with ui.Progress() as p:
p.inc(message="Loading input...")
Expand All @@ -309,7 +353,6 @@ def GenerateHeatmap():
if "x" in config.Features(): ax.tick_params(axis="x", labelsize=config.TextSize())
else: ax.set_xticklabels([])


b = BytesIO()
fig.savefig(b, format="png", dpi=config.DPI())
b.seek(0)
Expand Down Expand Up @@ -350,20 +393,7 @@ def DownloadTable(): yield GetData().to_string()


@render.download(filename="heatmap.png")
def DownloadHeatmap():
yield DataCache.Get([
File(input),
config.ColorMap(),
config.Opacity(),
config.Features(),
config.TextSize(),
config.Index(),
config.Peaks(),
config.DPI(),
config.Width(),
input.mode(),
input.MainTab()
])
def DownloadHeatmap(): yield DataCache.Get(Hash())


app_ui = ui.page_fluid(
Expand All @@ -385,7 +415,7 @@ def DownloadHeatmap():

ui.HTML("<b>Heatmap</b>"),

config.Index.UI(ui.input_select, id="Index", label="Index", selectize=True, multiple=True, choices=[0]),
config.ID.UI(ui.input_select, id="ID", label="ID", selectize=True, multiple=True, choices=[0]),


config.TextSize.UI(ui.input_numeric, id="TextSize", label="Text", min=1, max=50, step=1),
Expand All @@ -394,9 +424,15 @@ def DownloadHeatmap():

config.Peaks.UI(ui.input_select, id="Peaks", label="Peak Type", choices=["Raw", "Centroided", "Reprofiled"], conditional="input.MainTab === 'HeatmapTab'"),
config.Width.UI(ui.input_numeric, id="Width", label="Width", min=0.1, max=10.0, step=0.1, conditional="input.MainTab === 'HeatmapTab'"),
config.Style.UI(ui.input_select, id="Style", label="Style", choices=["Line", "Bar", "Contour"], conditional="input.MainTab === 'HeatmapTab'"),

config.Interpolation.UI(ui.input_select, id="Interpolation", label="Inter", choices=InterpolationMethods, conditional="input.MainTab === 'SimilarityTab'"),

ui.HTML("<b>3D</b>"),
config.Elevation.UI(ui.input_numeric, id="Elevation", label="Elevation"),
config.Rotation.UI(ui.input_numeric, id="Rotation", label="Rotation", step=1, min=1),
config.Zoom.UI(ui.input_numeric, id="Zoom", label="Zoom", step=1, min=1),


ui.HTML("<b>Image Settings</b>"),
config.Size.UI(ui.input_numeric, id="Size", label="Size", min=1),
Expand Down
7 changes: 6 additions & 1 deletion spectral/src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
config = ConfigHandler({

# Dependent on input
"Index": Config(selected=[0]),
"ID": Config(selected=[]),
"Peaks": Config(selected="Raw"),
"Width": Config(value=1.0),
"Style": Config(selected="Line"),
"Interpolation": Config(selected="Nearest"),

"Elevation": Config(value=45),
"Rotation": Config(value=300),
"Zoom": Config(value=1),

# Any value in between 1-50
"TextSize": Config(value=8),

Expand Down

0 comments on commit 5478117

Please sign in to comment.