OrbiTrack Dev Log 4: TOF Missing Peaks Adding

1. Motivation

In the previous post, I described the procedures for combining molecular information from both EESI-TOF and Orbitrap measurements. In our offline system, EESI-TOF can be regarded as semi-quantitative and more stable for long-term monitoring, making it ideal for tracking consistent ion trends. One key purpose of Orbitrap data is to guide the identification and fitting of TOF-detected ions, using its ultra-high resolution and accurate mass information.

However, in some cases, certain peaks are clearly detected by TOF but are either missing in Orbitrap or filtered out during the Orbitrap peak filtering and clustering process. These are often real chemical features, especially in complex mixtures, that may be weak or distorted in Orbitrap but still prominent in TOF.

To address this, I implemented a strategy to embed missing TOF peaks into the final clustered peak list, ensuring a more complete molecular representation, especially important in non-targeted or mixture-rich analyses.

Below is an example where Orbitrap-resolved ions (blue bars) partially explain a broader TOF feature, while a peak near m/z 190.90 in which clearly visible in TOF trace were not captured by Orbitrap. it is likely a chemically real ion that should be integrated into the final peak list.

EESI-Orbitrap vs TOF

2. Code Strategy

The following logic checks for high-prominence TOF peaks that were not matched in Orbitrap clustering results, and adds them back into the final peak list as "Note": 'Missing Peak'.

The procedures can be described as follows with code also attached:

  1. Peak detection from TOF
    Using find_peaks functions from scipy library to search for the local maixma values. Consider that there are some peaks on the shoulder only larger than one side, to avoid overfitting, we fitltered to keep those with strong prominice on both left and rigth side.

  2. Tolerance Matching with Orbitrap Peaks
    For each detected TOF peak apex, we check whether any Orbitrap-derived peak falls within a defined ppm tolerance window. If no match is found, the TOF peak is considered “missing in Orbitrap” and marked accordingly.

In the final step, we combine the Orbitrap-processed peak list with these unmatched TOF peaks, resulting in a more comprehensive ion profile.

3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def filter_asymmetric_peaks(peaks, props, y_data, left_thre=500, right_thre=500):
"""
Remove weak or one-sided peaks to avoid false positives.
"""
filtered_peaks = []
for i, pk in enumerate(peaks):
y_pk = y_data[pk]
left_valley = y_data[props['left_bases'][i]]
right_valley = y_data[props['right_bases'][i]]

left_prom = y_pk - left_valley
right_prom = y_pk - right_valley

if left_prom >= left_thre or right_prom >= right_thre:
filtered_peaks.append(pk)

return np.array(filtered_peaks)

# Step 1. Detect TOF peaks
peaks, props = find_peaks(y_tof, prominence=200, distance=5, width=2)
filtered_peaks = filter_asymmetric_peaks(peaks, props, y_tof)

# Step 2. Extract peak m/z and compare with Orbitrap clusters
peak_mz = x_tof[filtered_peaks]
ppm_threshold = tof_add_ion_threshold
missing_records = []

for peak in peak_mz:
allowed_diff = peak * ppm_threshold / 1e6
matched_in_cluster = any(np.abs(cluster_df['m.z'] - peak) <= allowed_diff) if not cluster_df.empty else False

if not matched_in_cluster:
int_orbi = np.nan
int_tof = y_tof.iloc[(x_tof - peak).abs().idxmin()]
missing_records.append({
'm.z': peak,
'int_in_orbi': int_orbi,
'int_in_tof': int_tof,
'Note': 'Missing Peak'
})

# Step 3. Combine original clusters with missing TOF peaks
missing_df = pd.DataFrame(missing_records)
combined_df = pd.concat([cluster_df, missing_df], ignore_index=True)
if len(combined_df) < 2:
return pd.DataFrame(), pd.DataFrame()
combined_df = combined_df.sort_values(by='m.z').reset_index(drop=True)
OrbiTrack Dev Log 5: Chemical Formula Assignment OrbiTrack Dev Log 3: Ion Filtering and Clustering

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×