处理我的测量数据的最佳库和实现

Best library and implementation to handle my measurement data

我在 Python 中有很多测量数据要分析。每个数据集都包含一个参数集(带有数字、日期和字符串的标量)和两条曲线。

目标是能够过滤(select 基于标准)、分组、聚类、分析(例如,一组中所有曲线和参数的平均值)和可视化数据集或它们的组。

我开始使用 Pandas 来实现它,并创建了一个 Dataframe,每个参数都有一列,测量 ID 作为索引。然后我为曲线添加了一列,这样该列中的每个字段都包含两条曲线作为两个 numpy 数组的字典。

这是一个示例实现(真实数据框有数千个数据集和数十个参数列)

import numpy as np
import pandas as pd

example_dataset_nr = 5
# Column titles
columns = ['DateTime', 'PositionX', 'Filter', 'Curves']

# Generate arbitrary parameter data to fill example Dataframe
dates = [pd.Timestamp(i*10000000) for i in range(example_dataset_nr)]
positions = np.random.rand(example_dataset_nr)
filters = ['green']*example_dataset_nr

# Generate curves, such that each field in the Dataframes "Curves"-column contains
# a dict with two curves, each as a array of points:
curves = [{'curve_voltage': np.random.randint(0, 100, size=(100,2)), 'curve_current': np.random.randint(0, 100, size=(100))} for i in range(example_dataset_nr)]

# Create Dataframe
df = pd.DataFrame(data=np.array([dates, positions, filters, curves]).T, columns=columns)
df['PositionX'] = df['PositionX'].astype(np.float)
df.index.rename('MeasurementID', inplace=True)
print(df.to_string())

现在,如果我用"df.mean()"这样的操作来分析数据,Pandas当然不知道如何处理曲线。我希望 pandas 像在其他数字字段上一样对曲线进行操作。例如,假设 df.mean(),Pandas 应该计算所有曲线的平均值,而不仅仅是 Dataframe 中的参数。

# Get the mean of all numeric types. Want to get the mean curves of all 'curve_voltage' and 'curve_current', too.
df.mean()

我想知道,在 Python 中实现这种行为的最佳方式是什么?

这里有一些建议:

  1. Pandas:对曲线使用单独的数据帧或系列,并通过外键将它们连接到纯 "parameter dataframe"。但接下来的问题是如何自动将所有方法从 "parameter dataframe" 转发到 "curves dataframe" 而无需重新实现它们?
  2. Pandas:子类数据框。或者任何其他扩展 Pandas 的方式。我读了 https://pandas.pydata.org/pandas-docs/stable/development/extending.html,但我不确定去这里哪个是正确的方法。同样的问题,转发方法是有意义的。
  3. Xarray:我从未使用过它,但是 Xarray 是满足我需求的更好工具吗,那么 pandas?
  4. 数据库:数据库是否更适合,比如SQL?
  5. 还有其他可行的选择吗?

我认为这将是 xarray 的一个很好的用例,因为它自然支持将表格(一维)数据与高维数据(您的曲线)相结合。

使用 xarray,您可以像这样构建数据集:

import xarray as xr

ds = xr.Dataset(
    {
        'DateTime': (['MeasurementID'], dates),
        'PositionX': (['MeasurementID'], positions),
        'Filter': (['MeasurementID'], filters),
        'curve_voltage': (['MeasurementID', 'curve_x', 'curve_y'], [row['curve_voltage'] for row in curves]),
        'curve_current': (['MeasurementID', 'curve_x'], [row['curve_current'] for row in curves]),
    },
    coords={
        'MeasurementID': np.arange(len(dates)),
        'curve_x': np.arange(100),
        'curve_y': np.arange(2)
    }
)

并像这样使用它:

>>> ds
<xarray.Dataset>
Dimensions:        (MeasurementID: 5, curve_x: 100, curve_y: 2)
Coordinates:
  * MeasurementID  (MeasurementID) int64 0 1 2 3 4
  * curve_x        (curve_x) int64 0 1 2 3 4 5 6 7 8 ... 92 93 94 95 96 97 98 99
  * curve_y        (curve_y) int64 0 1
Data variables:
    DateTime       (MeasurementID) datetime64[ns] 1970-01-01 ... 1970-01-01T00:00:00.040000
    PositionX      (MeasurementID) float64 0.7422 0.4789 0.7673 0.2552 0.8817
    Filter         (MeasurementID) <U5 'green' 'green' 'green' 'green' 'green'
    curve_voltage  (MeasurementID, curve_x, curve_y) int64 11 40 51 ... 38 26 64
    curve_current  (MeasurementID, curve_x) int64 88 24 57 32 75 ... 60 25 40 3

>>> ds['curve_voltage'].mean()  # global average over all voltage curves
<xarray.DataArray 'curve_voltage' ()>
array(49.26)

>>> ds['curve_voltage'].mean('curve_x')  # average only over curve_x dimension
ds['curve_voltage'].mean('curve_x')

<xarray.DataArray 'curve_voltage' (MeasurementID: 5, curve_y: 2)>
array([[47.06, 50.73],
       [53.1 , 45.41],
       [51.41, 50.33],
       [49.12, 46.26],
       [47.94, 51.24]])
Coordinates:
  * MeasurementID  (MeasurementID) int64 0 1 2 3 4
  * curve_y        (curve_y) int64 0 1

当然,您可以(并且应该)给您的附加维度起比 curve_xcurve_y 更有意义的名称。