如何在 Python 中从 GC-MS 数据创建包含保留时间、质量值和强度的 DataFrame,并正确对齐?

问题描述 投票:0回答:1

我正在用 Python 处理存储在 NetCDF 文件中的气相色谱-质谱 (GC-MS) 数据,但在正确对齐数据以创建结构化 DataFrame 时遇到问题。我的目标是建立一个矩阵,其中:

行:保留时间(来自 scan_acquisition_time)。 列:四舍五入的质量值(来自mass_values)。 细胞:对应于保留时间和质量的强度值(来自强度值)。

我现在面临的问题是,在我的一生中,我无法理解为什么某些强度值没有在数据框中以正确的顺序对齐/定位。我知道它们的位置不正确,因为我有相同数据帧的引用(来自另一个编程软件的输出,已被验证是正确的)。

数据框中强度值的放置对于我想做的事情非常重要;我尝试了不同的方法,例如我们制作数据透视表或根据质量值的大小对强度值进行分块,但到目前为止所有方法都失败了。

  1. 如何确保强度与保留时间和四舍五入的质量值正确一致?
  2. 是否有更好的方法在 Python 中构建或处理这些数据以避免这些对齐问题?

这就是我提取原始数据的方式:

` 将 xarray 导入为 xr 将 pandas 导入为 pd 将 numpy 导入为 np

 # Extract necessary data
 retention_time = data['scan_acquisition_time'].values.squeeze()
 intensity_values = data['intensity_values'].values.squeeze()
 mass_values = data['mass_values'].values.squeeze()
 scan_index = data['scan_index'].values.squeeze()
 rounded_mass = np.round(mass_values).astype(int)  # Round mass values to integers
 mass_min = np.round(min(mass_values)).astype(int)
 mass_max = np.round(max(mass_values)).astype(int)
 ideal_mass_values = list(range(mass_min, mass_max + 1))

`

以下是我的数据结构的示例:

scan_acquisition_time(形状(4825,)):每次扫描的保留时间。 scan_index(形状(4825,)):mass_values和intensity_values中每次扫描数据的起始索引。 mass_values(形状(2903174,)):所有扫描的质荷比。 强度值(形状(2903174,)):每个质量值对应的强度值。

然后为了构建数据框,我这样做了: ` # 确保存在“point_count”以将扫描映射到强度数据 如果 data.variables 中有“point_count”: point_count = data['point_count'].values.squeeze() 别的: raise ValueError(“数据集没有‘point_count’变量,这是将扫描映射到强度数据所必需的”)

 # Repeat retention times for each point in each scan
 retention_time_repeated = np.repeat(retention_time, point_count)

 # Ensure lengths match
 assert len(retention_time_repeated) == len(intensity_values), "Mismatch in  retention_time and intensity_values length"

 # Get unique retention times and define matrix dimensions
 unique_retention_times, inverse_indices = np.unique(retention_time_repeated, return_inverse=True)
 unique_masses = ideal_mass_values

 # Initialize a zero-filled intensity matrix
 intensity_matrix = np.zeros((len(unique_retention_times), len(unique_masses)))

 # Create mass index mapping
 mass_to_index = {mass: j for j, mass in enumerate(unique_masses)}

 # Get indices for the intensity matrix
 mass_indices = np.array([mass_to_index[mass] for mass in rounded_mass])

 # Populate the intensity matrix in a vectorized manner
 intensity_matrix[inverse_indices, mass_indices] += intensity_values

 # Convert the matrix to a DataFrame for easier inspection
 matrix_df = pd.DataFrame(
     intensity_matrix,
     index=unique_retention_times,
     columns=unique_masses
 )

 # Display part of the matrix for verification
 print(matrix_df.head())

`

我的问题:

  1. 如何确保强度与保留时间和四舍五入的质量值正确一致?
  2. 我应该合并 scan_index 来帮助对齐吗?如果是这样,我该如何处理?
  3. 是否有其他更好的方法在 Python 中构建或处理这些数据以避免对齐问题?

还有什么我可以做的吗?请帮忙。谢谢你:')

作为参考,这就是我想要得到的:

样本的强度矩阵.CDF: enter image description here

我设法把它弄出来,但正如前面提到的,有些值的结果有所不同。

python pandas dataframe numpy netcdf
1个回答
0
投票

在您的方法中,

retention_time_repeated
是使用
np.repeat
创建的。这意味着您假设保留时间和强度值之间完美匹配。但事实并非如此。实际映射取决于
scan_index
,它确定每次扫描数据的开始和结束位置。因此,您需要使用
rounded_mass
计算
np.round
并确保一致的舍入方法并具有显式映射
mass_to_index
。创建此值是为了将每个舍入质量值与强度矩阵中的特定列对齐。填充矩阵时,使用
mass_to_index
将每个强度值放入正确的列中,确保与圆形质量对齐。

这可以通过这种方式完成(这里我创建了示例数据,因为您没有提供任何数据):

import numpy as np
import pandas as pd

sample_data = {
    'scan_acquisition_time': np.array([1.0, 1.1, 1.2, 1.3, 1.4]), 
    'scan_index': np.array([0, 4, 9, 14, 18, 21]), 
    'mass_values': np.array([50.1, 51.2, 52.0, 53.3, 50.0, 51.1, 52.2, 54.1, 55.0, 50.2, 
                             51.5, 52.3, 53.8, 54.6, 50.3, 51.0, 52.1, 53.7, 50.5, 52.0, 53.0]),
    'intensity_values': np.array([100, 200, 150, 50, 120, 180, 160, 90, 80, 110, 
                                  210, 140, 70, 60, 130, 190, 170, 100, 125, 175, 155])
}

retention_time = sample_data['scan_acquisition_time']
scan_index = sample_data['scan_index']

rounded_mass = np.round(sample_data['mass_values']).astype(int)
intensity_values = sample_data['intensity_values']

mass_min = np.min(rounded_mass)
mass_max = np.max(rounded_mass)
ideal_mass_values = list(range(mass_min, mass_max + 1))

intensity_matrix = np.zeros((len(retention_time), len(ideal_mass_values)))

mass_to_index = {mass: i for i, mass in enumerate(ideal_mass_values)}

for i in range(len(scan_index) - 1):  
    start_idx = scan_index[i]
    end_idx = scan_index[i + 1]
    current_masses = rounded_mass[start_idx:end_idx]
    current_intensities = intensity_values[start_idx:end_idx]
    
    for m, intensity in zip(current_masses, current_intensities):
        col_idx = mass_to_index.get(m, None)
        if col_idx is not None:  
            intensity_matrix[i, col_idx] += intensity

last_start_idx = scan_index[-1]
current_masses = rounded_mass[last_start_idx:]
current_intensities = intensity_values[last_start_idx:]
for m, intensity in zip(current_masses, current_intensities):
    col_idx = mass_to_index.get(m, None)
    if col_idx is not None:
        intensity_matrix[-1, col_idx] += intensity

matrix_df = pd.DataFrame(
    intensity_matrix,
    index=retention_time,
    columns=ideal_mass_values
)

print(matrix_df.head())

我相信它会返回您对实际数据的期望:

        50     51     52     53     54    55
1.0  100.0  200.0  150.0   50.0    0.0   0.0
1.1  120.0  180.0  160.0    0.0   90.0  80.0
1.2  110.0    0.0  350.0    0.0   70.0  60.0
1.3  130.0  190.0  170.0    0.0  100.0   0.0
1.4  125.0    0.0  175.0  155.0    0.0   0.0
© www.soinside.com 2019 - 2024. All rights reserved.