C++曲线数据统一:如何高效插值并处理多条曲线的x值

发布于:2025-04-13 ⋅ 阅读:(29) ⋅ 点赞:(0)

在数据处理和科学计算中,我们经常会遇到需要对多条曲线进行统一x值处理的情况。例如,在实验数据记录中,不同传感器可能以不同的采样率记录数据,导致曲线的x值不一致。为了后续的分析和可视化,我们需要将这些曲线的x值统一,并通过插值计算新的y值。本文将通过一个完整的C++代码示例,展示如何高效地完成这一任务。

问题描述

假设我们有以下数据结构:

std::vector<std::vector<double>> para = {
    {1.0, 2.0, 3.0, 4.0, 5.0},  // 第一条曲线的x值
    {10.0, 20.0, 30.0, 40.0, 50.0},  // 第一条曲线的y值
    {2.0, 3.5, 4.5},  // 第二条和第三条曲线的x值
    {15.0, 25.0, 35.0},  // 第二条曲线的y值
    {20.0, 30.0, 40.0}  // 第三条曲线的y值
};
  • 第1行和第2行存储第一条曲线的x和y值。
  • 第3行存储第二条和第三条曲线的x值(这两条曲线的x值相同)。
  • 第4行和第5行分别存储第二条和第三条曲线的y值。

我们的目标是:

  1. 合并第一条曲线和第二、三条曲线的x值,生成统一的x值集合。
  2. 对每条曲线进行插值计算,生成新的y值。
  3. 如果新的x值超出原始x范围,则返回固定值(例如0.0)。
  4. 将处理后的曲线存储到新的std::vector<std::vector<double>>中。

解决方案

1. 线性插值函数

我们首先实现一个线性插值函数,用于计算给定x值处的y值。如果x值超出原始x范围,则返回固定值。

double linearInterpolate(const std::vector<double>& x, const std::vector<double>& y, double targetX) {
    if (x.empty() || y.empty() || x.size() != y.size()) {
        return std::nan(""); // 返回NaN表示无效
    }

    // 如果目标x超出范围,返回固定值(这里假设固定值为0.0)
    if (targetX < x.front()) {
        return 0.0;
    }
    if (targetX > x.back()) {
        return 0.0;
    }

    // 找到目标x所在的区间
    auto it = std::lower_bound(x.begin(), x.end(), targetX);
    if (it == x.begin()) {
        return y.front();
    }
    if (it == x.end()) {
        return y.back();
    }

    // 计算插值
    size_t idx = std::distance(x.begin(), it) - 1;
    double x0 = x[idx];
    double x1 = x[idx + 1];
    double y0 = y[idx];
    double y1 = y[idx + 1];

    return y0 + (y1 - y0) * (targetX - x0) / (x1 - x0);
}

2. 合并x值并排序

我们使用std::set来合并并排序x值,确保x值唯一且有序。

std::set<double> xSet(x1.begin(), x1.end());
xSet.insert(x23.begin(), x23.end());
std::vector<double> unifiedX(xSet.begin(), xSet.end());

3. 插值计算新的y值

我们分别对三条曲线进行插值计算,生成新的y值。

// 计算第一条曲线的y值
std::vector<double> newY1;
for (double x : unifiedX) {
    newY1.push_back(linearInterpolate(x1, y1, x));
}

// 计算第二条曲线的y值
std::vector<double> newY2;
for (double x : unifiedX) {
    newY2.push_back(linearInterpolate(x23, y2, x));
}

// 计算第三条曲线的y值
std::vector<double> newY3;
for (double x : unifiedX) {
    newY3.push_back(linearInterpolate(x23, y3, x));
}

4. 存储结果

将统一后的x值和三条曲线的新y值存储到新的std::vector<std::vector<double>>中。

std::vector<std::vector<double>> result;
result.push_back(unifiedX); // 第一行是统一的x值
result.push_back(newY1);
result.push_back(newY2);
result.push_back(newY3);

完整代码

以下是完整的代码示例:

#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <cmath>

// 线性插值函数
double linearInterpolate(const std::vector<double>& x, const std::vector<double>& y, double targetX) {
    if (x.empty() || y.empty() || x.size() != y.size()) {
        return std::nan(""); // 返回NaN表示无效
    }

    // 如果目标x超出范围,返回固定值(这里假设固定值为0.0)
    if (targetX < x.front()) {
        return 0.0;
    }
    if (targetX > x.back()) {
        return 0.0;
    }

    // 找到目标x所在的区间
    auto it = std::lower_bound(x.begin(), x.end(), targetX);
    if (it == x.begin()) {
        return y.front();
    }
    if (it == x.end()) {
        return y.back();
    }

    // 计算插值
    size_t idx = std::distance(x.begin(), it) - 1;
    double x0 = x[idx];
    double x1 = x[idx + 1];
    double y0 = y[idx];
    double y1 = y[idx + 1];

    return y0 + (y1 - y0) * (targetX - x0) / (x1 - x0);
}

int main() {
    // 假设输入的para结构
    std::vector<std::vector<double>> para = {
        {1.0, 2.0, 3.0, 4.0, 5.0}, // 第一条曲线的x
        {10.0, 20.0, 30.0, 40.0, 50.0}, // 第一条曲线的y
        {2.0, 3.5, 4.5}, // 第二条和第三条曲线的x
        {15.0, 25.0, 35.0}, // 第二条曲线的y
        {20.0, 30.0, 40.0} // 第三条曲线的y
    };

    // 提取第一条曲线的x和y
    std::vector<double> x1 = para[0];
    std::vector<double> y1 = para[1];

    // 提取第二条和第三条曲线的x和y
    std::vector<double> x23 = para[2];
    std::vector<double> y2 = para[3];
    std::vector<double> y3 = para[4];

    // 合并x值并排序
    std::set<double> xSet(x1.begin(), x1.end());
    xSet.insert(x23.begin(), x23.end());
    std::vector<double> unifiedX(xSet.begin(), xSet.end());

    // 创建结果容器
    std::vector<std::vector<double>> result;
    result.push_back(unifiedX); // 第一行是统一的x值

    // 计算第一条曲线的y值
    std::vector<double> newY1;
    for (double x : unifiedX) {
        newY1.push_back(linearInterpolate(x1, y1, x));
    }
    result.push_back(newY1);

    // 计算第二条曲线的y值
    std::vector<double> newY2;
    for (double x : unifiedX) {
        newY2.push_back(linearInterpolate(x23, y2, x));
    }
    result.push_back(newY2);

    // 计算第三条曲线的y值
    std::vector<double> newY3;
    for (double x : unifiedX) {
        newY3.push_back(linearInterpolate(x23, y3, x));
    }
    result.push_back(newY3);

    // 输出结果
    std::cout << "Unified curves:" << std::endl;
    for (size_t i = 0; i < result.size(); ++i) {
        std::cout << "Row " << i + 1 << ": ";
        for (double val : result[i]) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

示例输出

假设输入的para如代码所示,输出结果将类似于:

Unified curves:
Row 1: 1 2 2.5 3 3.5 4 4.5 5 
Row 2: 10 20 22.5 30 35 40 45 50 
Row 3: 0 15 17.5 25 25 35 37.5 0 
Row 4: 0 20 25 30 30 40 42.5 0 

总结

通过上述方法,我们可以高效地统一多条曲线的x值,并通过插值计算新的y值。这种方法在数据处理和科学计算中非常实用,尤其是在需要对不同采样率的数据进行统一分析时。如果你有更复杂的需求,例如使用三次样条插值或其他高级插值方法,可以进一步扩展上述代码。