我正在用 Python 处理存储在 NetCDF 文件中的气相色谱-质谱 (GC-MS) 数据,但在正确对齐数据以创建结构化 DataFrame 时遇到问题。我的目标是建立一个矩阵,其中:
行:保留时间(来自 scan_acquisition_time)。 列:四舍五入的质量值(来自mass_values)。 细胞:对应于保留时间和质量的强度值(来自强度值)。
我现在面临的问题是,在我的一生中,我无法理解为什么某些强度值没有在数据框中以正确的顺序对齐/定位。我知道它们的位置不正确,因为我有相同数据帧的引用(来自另一个编程软件的输出,已被验证是正确的)。
数据框中强度值的放置对于我想做的事情非常重要;我尝试了不同的方法,例如我们制作数据透视表或根据质量值的大小对强度值进行分块,但到目前为止所有方法都失败了。
这就是我提取原始数据的方式:
` 将 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())
`
我的问题:
还有什么我可以做的吗?请帮忙。谢谢你:')
作为参考,这就是我想要得到的:
我设法把它弄出来,但正如前面提到的,有些值的结果有所不同。
在您的方法中,
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