Scientific Data Analysis with LabPlot in Python: Signal Processing, Spectral Peak Fitting, Visualization, and Batch Automation

2026年8月24日 03:32
站內 AI 整理稿

In this tutorial, we explore a LabPlot-inspired scientific data analysis workflow in Python while preserving the structure and terminology of LabPlot’s aspect tree, analysis kernels, plotting system, and project model.

We build reusable components to import tabular data, compute descriptive statistics, smooth and differentiate signals, perform Fourier analysis and filtering, detect peaks, integrate curves, reduce data, and fit nonlinear models with detailed statistical diagnostics.

We then apply these tools to a realistic spectroscopy example: removing periodic interference, identifying overlapping peaks, fitting a multi-Gaussian model, inspecting residuals, visualizing results through themed worksheets, exporting figures, and saving project data in LabPlot-compatible .

lml-style files.Finally, we extend the same workflow to batch processing so we can analyze multiple temperature-dependent spectra and fit secondary trends across the resulting measurements.

Copy CodeCopiedUse a different Browserimport os, sys, gzip, bz2, lzma, time, math, textwrap, warnings import xml.etree.ElementTree as ET from dataclasses import dataclass, field from enum import Enum import numpy as np, pandas as pd, matplotlib, matplotlib.pyplot as plt from matplotlib.

ticker import AutoMinorLocator import scipy from scipy import signal, stats, optimize warnings.filterwarnings("ignore", category=RuntimeWarning) np.random.seed(20260815) INCOLAB = "google.colab" in sys.modules OUT = "/content/labplotout" if INCOLAB else os.path.join(os.getcwd(), "labplotout") os.

makedirs(OUT, existok=True) try: from pylabplot import ; HAVESDK = True except Exception: HAVESDK = False banner = lambda t: print("\n" + "=" 76 + f"\n {t}\n" + "=" 76) banner("environment") print(f" numpy {np.version} | scipy {scipy.version} | mpl {matplotlib.

version} | " f"colab={INCOLAB} | pylabplot={'yes' if HAVESDK else 'no -> emulation'}\n -> {OUT}") class PlotDesignation(Enum): NoDesignation = 0; X = 1; Y = 2; Z = 3 XError = 4; XErrorMinus = 5; XErrorPlus = 6 YError = 7; YErrorMinus = 8; YErrorPlus = 9 class ColumnMode(Enum): Double = 0; Text = 1; Integer = 2; BigInt = 3; DateTime = 4 class AbstractAspect: def init(self, name, comment=""): self.

name, self.comment, self.parent, self.children = name, comment, None, [] def name(self): return self.name def addChild(self, a): a.parent = self; self.children.append(a); return a def tree(self, d=0): s = " " d + f"{'|- ' if d else ''}{type(self).name:<20} {self.

name}" if isinstance(self, Column): s += f" [{self.columnMode.name}, {self.rowCount()} rows, {self.plotDesignation.name}]" return "\n".join([s] + [c.tree(d+1) for c in self.children]) class Column(AbstractAspect): """LabPlot's fundamental data source: a typed vector + a plot designation.

""" def init(self, name, values=None, mode=ColumnMode.Double, designation=PlotDesignation.NoDesignation): super().init(name) self.columnMode, self.plotDesignation = mode, designation self.d = np.asarray([] if values is None else values, float) def values(self): return self.

d def rowCount(self): return len(self.d) def clean(self): return self.d[np.isfinite(self.d)] def statistics(self): """The 20 quantities in LabPlot's Column Statistics dialog.""" x = self.clean(); n = x.size if not n: return {} q1, med, q3 = np.

percentile(x, [25, 50, 75]); iqr, pos = q3 - q1, x[x > 0] h = 2 iqr / n(1/3) if iqr > 0 else 0 c, = np.histogram(x, bins=int(np.clip(np.ptp(x)/h, 1, 1000)) if h else 10) p = c[c > 0] / c.sum(); v, k = np.unique(np.round(x, 12), returncounts=True) return {"Count": n, "Minimum": x.min(), "Maximum": x.

max(), "Arithmetic mean": x.mean(), "Geometric mean": stats.gmean(pos) if pos.size else np.nan, "Harmonic mean": stats.hmean(pos) if pos.size else np.nan, "Contraharmonic mean": (x2).sum() / x.sum() if x.sum() else np.nan, "Mode": v[k.argmax()] if k.max() > 1 else np.

nan, "First quartile": q1, "Median": med, "Third quartile": q3, "Interquartile range": iqr, "Trimean": (q1 + 2med + q3) / 4, "Variance": x.var(ddof=1), "Standard deviation": x.std(ddof=1), "Skewness": stats.skew(x), "Mean absolute deviation": np.abs(x - x.mean()).

mean(), "Median absolute deviation": np.median(np.abs(x - med)), "Kurtosis": stats.kurtosis(x, fisher=False), "Entropy": float(-(p np.log2(p)).sum())} def sparkline(self, w=26): """LabPlot 2.11+ draws these in the column header; text version.""" b, x = ".-~^", self.clean() s = x[np.linspace(0, x.

size-1, min(w, x.size)).astype(int)] if x.size > 1 else x return "" if s.size < 2 or np.ptp(s) == 0 else "".join( b[i] for i in ((s - s.min()) / np.ptp(s) 4).round().astype(int)) class Spreadsheet(AbstractAspect): def columns(self): return [c for c in self.

children if isinstance(c, Column)] def column(self, k): cs = self.columns() return cs[k] if isinstance(k, int) else next(c for c in cs if c.name() == k) def columnCount(self): return len(self.columns()) def rowCount(self): return max([c.rowCount() for c in self.

columns()], default=0) def appendColumn(self, n, v, d=PlotDesignation.Y): return self.addChild(Column(n, v, designation=d)) def toDataFrame(self): return pd.DataFrame({c.name(): c.values() for c in self.columns()}) def info(self): print(f" Spreadsheet '{self.name}': {self.rowCount()} rows x {self.

columnCount()} cols") for c in self.columns(): s = c.statistics() print(f" {c.name():<12}{c.plotDesignation.name:<6}min {s['Minimum']:>9.4g} max " f"{s['Maximum']:>9.4g} mean {s['Arithmetic mean']:>9.4g} {c.

sparkline()}") class Project(AbstractAspect): XMLVERSION = 15 def init(self, name="project", author=""): super().init(name); self.author, self.version = author, "2.12.1" def spreadsheets(self): return [c for c in self.

children if isinstance(c, Spreadsheet)] class AsciiFilter: """LabPlot's text import: separator auto-detect, comments, row/col limits.""" def init(self, separator="auto", commentCharacter="#", headerEnabled=True, startRow=1, endRow=-1, startColumn=1, endColumn=-1): self.separator, self.

commentCharacter = separator, commentCharacter self.headerEnabled, self.startRow, self.endRow = headerEnabled, startRow, endRow self.startColumn, self.

endColumn = startColumn, endColumn def readDataFromFile(self, path, dataSource): with open(path, encoding="utf-8", errors="replace") as fh: lines = [l.rstrip("\n") for l in fh if l.strip() and not l.lstrip().startswith(self.commentCharacter)] lines = lines[self.startRow - 1: None if self.

endRow < 0 else self.endRow] if not lines: raise ValueError("AsciiFilter: nothing to import") sep = (next((s for s in (",", ";", "\t", "|") if s in lines[0]), None) if self.separator == "auto" else self.separator) split = lambda l: [p.strip() for p in (l.split(sep) if sep else l.split()) if p.

strip()] header = split(lines[0]) if self.headerEnabled else None rows = [split(l) for l in (lines[1:] if self.headerEnabled else lines)] ncol = max(map(len, rows)) c0, c1 = self.startColumn - 1, ncol if self.endColumn < 0 else self.

endColumn for j in range(c0, min(c1, ncol)): vals = [] for r in rows: try: vals.append(float(r[j])) except (IndexError, ValueError): vals.append(np.nan) dataSource.addChild(Column( header[j] if header and j < len(header) else f"Column {j+1}", vals, designation=PlotDesignation.

X if j == c0 else PlotDesignation.Y)) return dataSource We set up the Python environment, configure reproducibility, and establish the output directory for the tutorial.We recreate LabPlot’s core aspect-tree structure using projects, spreadsheets, columns, plot designations, and column modes.

We also implement the AsciiFilter workflow to import structured text data into our LabPlot-style data model.Copy CodeCopiedUse a different Browserclass nslsmooth: """Analysis -> Smooth (Savitzky-Golay; LabPlot also offers moving average/percentile).

""" @staticmethod def savitzkygolay(y, points=11, order=3, deriv=0): points += points % 2 == 0 return signal.savgol_filter(y, points, min(order, points-1), deriv=deriv, mode="interp") cla

Related

相關文章

韓國通過《個人信息保護法》修正案,允許 AI 開發使用個人數據

作者:潞源 責編:潞源 評論: 8 月 24 日消息,據韓媒 The Elec 今天報道,韓國個人信息保護委員會(PIPC)近日表示,國會已全體通過《個人信息保護法》修正案。《個人信息保護法》修訂後,允許人工智能開發商在經過韓國個人信息保護委員會審查後,使用限定範圍內的個人數據。

剛剛

DeepSeek Harness來了:AI開始製造AI了?

DeepSeek 近日推出名為「DeepSeek Harness」的新工具,消息一出立刻在科技圈引發討論:AI 是否真的開始自己製造 AI 了?這款產品在 36 氪的 AI 測評欄目中,與 Kimi、MiniMax 等熱門模型並列出現,顯示其已進入市場視野,但截至目前官方並未公布詳細的功能規格或應用場景。 從命名來看,Harness 一詞在工程領域通常指「控制系統」或「整合框架」,暗示這可能是一套用來管理、引導或自動化 AI 模型開發流程的基礎設施。

剛剛

智元聯合長隆打造全球首個具身智能主題樂園,含機器人服務酒店等

作者:浩渺 責編:浩渺 評論: 8 月 24 日消息,8 月 23 日,智元(AGIBOT)與長隆集團在橫琴長隆飛船樂園正式簽署戰略合作協議。從官方介紹獲悉,雙方將圍繞“文旅 + 科技”深度融合開展全方位合作,共同打造全球首個、規模最大、場景最豐富、技術最先進的大型具身智能主題樂園:包括全球首個沉浸式機器人主題樂園、全球規模最大機器動物總動員、全球首創大型機器人馬戲秀、全球最具特色的賽博主題巡遊,以及全球首個機器人服務酒店等等。

剛剛