【问题标题】:How to interpolate between numerial data table values in Dart ( actually in any language )如何在 Dart 中的数值数据表值之间进行插值(实际上是任何语言)
【发布时间】:2021-07-07 21:59:13
【问题描述】:

我有一个带有 x 和 y 值的简单数据表,如下所示:

x     y
-10  -0.505
-9   -0.422
-8   -0.335
-7   -0.243
-6   -0.148
-5   -0.051
-4    0.046
-3    0.144
-2    0.242
-1    0.34
0     0.539
1     0.658
2     0.773
3     0.716
4     0.8
5     0.88
6     0.952
7     1.016
8     1.071
9     1.116
10    1.15

x 步长以及最小值和最大值可能不同。

我正在寻找一种在这些值之间进行插值的内置功能。 所以我需要一个函数来获取 x 值并返回相应的 y 值。当没有完全匹配时,我需要函数在两个最接近的值之间进行线性插值。

我当然可以编写自己的函数,但我觉得可能有一个简单的解决方案,甚至可能内置在 Dart 中。

感谢您的帮助。

感谢和欢呼

托比

【问题讨论】:

    标签: function dart interpolation


    【解决方案1】:

    pub.dev 上可能有一些包已经这样做了,但我会使用SplayTreeMap 及其lastKeyBeforefirstKeyAfter 方法来查找周围的点,然后在它们之间进行插值。例如:

    import 'dart:collection' show SplayTreeMap;
    import 'dart:math' show Point;
    
    /// Returns the y-coordinate for the specified x-coordinate on the line defined
    /// by two given points.
    double _interpolate(Point<double> p0, Point<double> p1, double x) {
      // y - y0 = m * (x - x0)
      var m = (p1.y - p0.y) / (p1.x - p0.x);
      return m * (x - p0.x) + p0.y;
    }
    
    class InterpolatingMap {
      final SplayTreeMap<double, double> _data;
    
      InterpolatingMap(Map<double, double> data)
          : _data = SplayTreeMap<double, double>.of(data);
    
      double operator [](double x) {
        var value = _data[x];
        if (value != null) {
          return value;
        }
        
        if (_data.isEmpty) {
          throw StateError('InterpolatingMap is empty');
        }
    
        double? lower = _data.lastKeyBefore(x);
        double? upper = _data.firstKeyAfter(x);
        assert(lower != null || upper != null);
    
        double x0;
        double x1;
        if (lower == null) {
          // `x` is to the left of the left-most data point.  Extrapolate from the
          // first two entries.
          x0 = upper!;
          x1 = _data.firstKeyAfter(upper) ?? x0;
        } else if (upper == null) {
          // `x` is to the right of the right-most data point.  Extrapolate from the
          // last two entries.
          x1 = lower;
          x0 = _data.lastKeyBefore(lower) ?? x1;
        } else {
          x0 = lower;
          x1 = upper;
        }
    
        return _interpolate(
          Point<double>(x0, _data[x0]!),
          Point<double>(x1, _data[x1]!),
          x,
        );
      }
    }
    
    void main() {
      var interpolatingMap = InterpolatingMap({
        0: 1,
        1: 2,
        2: 1,
      });
    
      print(interpolatingMap[-1]);   // Prints: 0
      print(interpolatingMap[0]);    // Prints: 1
      print(interpolatingMap[0.25]); // Prints: 1.25
      print(interpolatingMap[0.5]);  // Prints: 1.5
      print(interpolatingMap[0.75]); // Prints: 1.75
      print(interpolatingMap[1]);    // Prints: 2
      print(interpolatingMap[1.5]);  // Prints: 1.5
      print(interpolatingMap[3]);    // Prints: 0
    }
    

    请注意,上述实现中的InterpolatingMap 有点用词不当,因为它还会推断数据范围之外的值。 (但是,如果你想禁用外推,让它抛出异常应该很简单。)它也没有实现 Map 接口(留给关心它的读者作为练习)。

    【讨论】:

      【解决方案2】:

      我可能会使用二进制搜索来查找匹配范围,然后从中进行插值。 您可以使用package:collection 中的lowerBound 方法查找小于或等于您搜索的元素的最大元素。

      类似:

      import"package:collection/collection.dart";
      double interpolate(List<num> keyPoints, List<num> values, num x) {
        if (keyPoints.length < 2) {
          throw ArgumentError.value(keyPoints, "keyPoints", 
              "Needs at least two points to interpolate");
        }
        if (keyPoints.length != values.length) {
          throw ArgumentError.value(values, "values", 
              "Must have the same number of elements as the key points");
        }
        var p = keyPoints.lowerBound(x);
        if (p > keyPoints.length - 2) p = keyPoints.length - 2;
        var startPosition = keyPoints[p];
        var endPosition = keyPoints[p + 1];
        var startValue = values[p];
        var endValue = values[p + 1];
        return (x - startPosition) / (endPosition - startPosition) * (endValue - startValue);
      }
      

      这将在x 在两个关键点之间时插入值,如果x 值在关键点范围之外,则推断第一个或最后一个范围。

      【讨论】:

        【解决方案3】:

        您可以使用 SplayTreeMap 来存储您的值。键表示要搜索的范围。然后使用插值函数,如下所示:

        var kEfficiencyMotorsFullLoad = SplayTreeMap<double, double>.from({
            0.75: .825,
            1.1: .841,
            1.5: .853,
            2.2: .867,
            3.0: .877,
            4.0: .886,
            5.5: .896,
            7.5: .904,
            11.0: .914,
            15.0: .921,
            18.5: .926,
            22.0: .930,
            30.0: .936,
          });
        
        
        double linearInterpolate(double target, SplayTreeMap<double, double> values) {
          if (values.containsKey(target)) return values[target]!;
        
          double? xa = values.lastKeyBefore(target);
          double? xb = values.firstKeyAfter(target);
        
          //very small key
          if (xa != null && xb == null) return values[xa]!;
          //very large key
          if (xa == null && xb != null) return values[xb]!;
          //strange error
          if (xa == null && xb == null)
            throw Exception(
                "number was not found in the SplayTreeMap, check if it is not empty");
        
          double ya = values[xa] ?? 0;
          double yb = values[xb] ?? 0;
        
          return ya + ((yb - ya) * ((target - xa!) / (xb! - xa)));
        }
        

        【讨论】:

          猜你喜欢
          • 2023-04-11
          • 1970-01-01
          • 2016-10-04
          • 1970-01-01
          • 1970-01-01
          • 2020-07-05
          • 1970-01-01
          • 1970-01-01
          • 2017-10-27
          相关资源
          最近更新 更多