Two hundred measurements, one per sample, and your PCA, your cluster plot, or your supervisor wants one dataset. Here is the shortest honest path for each situation, including the case everyone hits sooner or later: the files do not share the same wavenumber grid.
If every CSV has the same two columns and the same axis points, a ten-line script is enough:
import glob, os
import pandas as pd
frames = []
for f in glob.glob("exports/*.csv"):
df = pd.read_csv(f, sep=None, engine="python", comment="#").iloc[:, :2]
df.columns = ["wavenumber", "intensity"]
df["sample"] = os.path.basename(f)
frames.append(df)
combined = pd.concat(frames, ignore_index=True) # tidy long format
This works until it doesn't. The quiet failure modes:
| Symptom | Cause | What the script does |
|---|---|---|
| NaN everywhere after pivoting | grids differ by one point or range | silently misaligns |
| Values look shifted | one file in nm, another in cm-1 | merges different units |
| Sample names lost | names lived in a header line or a metadata file | drops them |
| Half the folder missing | semicolon or tab exports, comment headers | parses wrong |
eQCX converts the whole
folder into the open eQC format: one spectra.csv per
modality (rows = axis points, columns = samples) plus
metadata.csv linked by sample_id. That single
table is the combined file, and it loads in three lines:
import eqc, pandas as pd
g = eqc.read("my_eqc_output")[0] # first modality group
spectra = pd.DataFrame(g.samples, index=g.axis) # rows = axis, cols = samples
meta = pd.DataFrame(g.metadata) # sample_id + every detail
spectra.T.to_excel("all_samples.xlsx")
(pandas writes xlsx through openpyxl). Or just open
spectra.csv in Excel directly.Because the columns are sample ids linked to metadata rows, group labels, concentrations, or instrument details travel with the data. No filename parsing, no side tables.
Different lasers, different instruments, different export menus: the ranges and point spacing will not match. eQCX never invents points and never intersects grids, so by default spectra with different ranges go to separate output files instead of a corrupted merge. When you need one shared axis for the whole set, the opt-in unification merges them:
| Choice | What it does |
|---|---|
| Majority | picks the axis most samples already share, so most spectra need no maths at all |
| Finest | adopts the densest axis |
| Canonical | your own min, max, and step |
Interpolation is linear, PCHIP, or Lanczos, a fidelity guard warns if a resampled spectrum drifts from its measured shape, and the merge is logged with an alteration report. Measured values stay untouched unless you ask for this step by name.
| Situation | Route |
|---|---|
| 20 files, one instrument, same export | pandas concat (route 1) |
| A real folder: mixed layouts, metadata in siblings | eQCX conversion (route 2) |
| Different grids or ranges, need one axis | route 2 + opt-in unification |
| The group wants one Excel file | route 2, then open spectra.csv or one to_excel line |
Same grid: pandas concat with a sample column. Different layouts or grids: convert the folder once with eQCX and load the single spectra table.
spectra.csv opens in Excel as-is, or
spectra.T.to_excel("all_samples.xlsx") writes it from pandas.
No. They go to separate output files by default. The opt-in unification merges them onto one axis with a fidelity guard and a logged alteration report.
No. After conversion every column is a sample_id linked to a
metadata row; names and groups come from the metadata table.
One folder in, one combined table out.
eQCX runs on Windows and processes everything locally. Free with a university email.
Download for WindowsComing from Bruker? Read Convert Bruker CSV to Python first. Full docs: the wiki