【问题标题】:Calculating steps from a min/max scenario, and steps should be nice numbers c#从最小/最大场景计算步骤,步骤应该是不错的数字 c#
【发布时间】:2017-01-13 01:00:07
【问题描述】:

我正在处理一张热图,我必须在地图上显示由一系列值计算出的颜色填充的区域。

我弄清楚了区域颜色部分,但我还必须显示 5 个步骤,每个步骤提供一个颜色和一个值,解释颜色的含义。

所以,假设我的最小值为 2654,最大值为 6947。 这为我提供了 5 个数字的范围,它们是:

2654
3727,25
4800,5
5873,75
6947

现在我想要将这些值显示为漂亮的间隔,如下所示:

0 - 3000

3000 - 4000

4000 - 5000

5000 - 6000

> 6000

这些值当然是动态的。 所以它可能是巨大的数字,也可能是小数字。 有没有办法做到这一点?

更新

@roberto-carlos 我在下面尝试了您的示例:

  var min = 900;
  var max = 12541;
  var steps = 5;

  var range = Enumerable.Range(0, steps).Select(i => min + (max - min) * ((double)i / (steps - 1)));

  foreach (var d in range)
  {
    var power = Math.Floor(Math.Log10(d));
    var inf = d - d % Math.Pow(10, power);
    var sup = d + (Math.Pow(10, power) - d % Math.Pow(10, power));

    if (d == min)
    {
      Console.WriteLine("0-" + sup);
    }
    else if (d == max)
    {
      Console.WriteLine(">" + sup);
    }
    else
    {
      Console.WriteLine(inf + "-" + sup);
    }
  }

但是,这为我提供了这样的结果:

  0-1000
  3000-4000
  6000-7000
  9000-10000
  >20000

有没有办法让间隔在数字上“粘在一起”?

【问题讨论】:

  • “好”到底是什么意思?你的意思是你首先决定你想要有 5 个间隔,并且应该以它们看起来“圆润”的方式确定边界?
  • 你没有任何意义。引用“最小值为 2654,最大值为 6947”,这与数字 5 没有任何关系。甚至在数字已知之前就选择了 5。那么为什么不直接使用 5 作为分段的除数。 x/5。或执行您在上面应用的逻辑。首先是最小值到第一个第十个最大值,然后是下一个的间隔除以 5。
  • 总会有 5 个间隔/步长,但值的数量和这些值的范围是动态的。所以到目前为止我所做的是我根据提供的最小/最大值计算了每个步骤。我正在寻找的是将这些分成基本四舍五入到最接近的 100 或 1000 或 10000 的类别,无论数字是多少。
  • @mp1990 你能举个例子来说明数字上“粘在一起”的间隔吗?
  • @roberto-carlos 这基本上是我想要达到的目标:imgur.com/a/BUFnZ 除了数字可以比这更宽。该图像是一个范围从 0 到 x 的值的示例,用于计算其中的组。

标签: c# .net math


【解决方案1】:

你可以试试这个:

var list = new List<double> { 2654, 3727.25, 4800.5, 5873.75, 6947 };

foreach (var d in list)
{
    var power = Math.Floor(Math.Log10(d));
    var min = d - d % Math.Pow(10, power); 
    var max = d + (Math.Pow(10, power) - d % Math.Pow(10, power));

    Console.WriteLine(min + "-" + max);
}
/*
Result:
2000 - 3000
3000 - 4000
4000 - 5000
5000 - 6000
6000 - 7000
/*  

版本 2:

var list = new List<double> { 2654, 3727.25, 4800.5, 5873.75, 6947 };
var min = list.Min();
var max = list.Max();

foreach (var d in list)
{
    var power = Math.Floor(Math.Log10(d));
    var inf = d - d % Math.Pow(10, power); 
    var sup = d + (Math.Pow(10, power) - d % Math.Pow(10, power));

    if (d == min)
    {
        Console.WriteLine("0-" + sup);
    }
    else if (d == max)
    {
        Console.WriteLine(">" + sup);
    }
    else
    {
        Console.WriteLine(inf + "-" + sup);
    }
}
/*
Result:
0-3000
3000-4000
4000-5000
5000-6000
>7000
/*  

【讨论】:

  • 这实际上非常接近,除了如果我摆弄数字并得到这个范围:951 3718,5 6486 9253,5 12021 结果是这样的:900-1000 3000-4000 6000-7000 9000 -10000 10000-20000 这会在开始时产生一个差距:900-1000 到 3000-4000。但也许我可以通过最终将值转换为 0 - 3000 和 > 10000 来解决这个问题。
【解决方案2】:

使用 linq 放入预先确定的组中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication42
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] intervals = {3000, 4000, 5000,6000};

            List<List<int?>> data = new List<List<int?>>() {
                new List<int?>() {2654},
                new List<int?>() {3727,25},
                new List<int?>() {3730,25},
                new List<int?>() {4800,5},
                new List<int?>() {5873,75},
                new List<int?>() {6947}
            };

            var results = data.Select(x => new { index = intervals.Select((y, i) => new { index = i, max = y }).Where(z => x[0] < z.max).FirstOrDefault(), data = x }).ToList();

            var groups = results.GroupBy(x => x.index != null ? x.index.index : intervals.Length).Select(x => new { key = x.Key, data = x.Select(y => y.data).ToList() }).ToList();

        }
    }
}

【讨论】:

  • 这很好,但我如何计算组呢?这基本上是我的问题。
  • 你想要线性、指数、对数还是固定数量的组?
【解决方案3】:

好的,我最初误解了你的问题。

以下实现应该这样做:

static IEnumerable<Interval> GetIntervals(IEnumerable<double> values, int intervalsCount)
{
    if (values == null)
        throw new ArgumentNullException(nameof(values));

    if (intervalsCount <= 1)
        yield break;

    var orderedValues = values.OrderBy(v => v);
    var frontiers = orderedValues.Select(
        v =>
        {
            var scale = Math.Pow(10, (int)Math.Log10(v));
            return Math.Floor(v / scale) * scale;
        }).ToArray();

    for (var i = 0; i < frontiers.Length - 1; i++)
    {
        yield return new Interval(frontiers[i], frontiers[i + 1]);
    }

    yield return new Interval(frontiers[frontiers.Length - 1], double.PositiveInfinity);
}

Interval 是一个简单的辅助结构:

struct Interval
{
    public Interval(double lower, double upper)
    {
        if (upper <= lower)
            throw new ArgumentException("The upper bound must be greater than the specified lower bound", nameof(upper));

        LowerBound = lower;
        UpperBound = upper;
    }

    public double LowerBound { get; }
    public double UpperBound { get; }

    public override string ToString()
    {
        if (UpperBound == double.PositiveInfinity)
            return $" > {LowerBound}";

        return $"{LowerBound} - {UpperBound}";
    }
}

2654.0, 3727.25, 4800.5, 5873.75, 6947 的输出:

2000 - 3000
3000 - 4000
4000 - 5000
5000 - 6000
> 6000

例如,在对数缩放输入2.0, 56, 321, 5674, 12764 的情况下,输出为:

2 - 50
50 - 300
300 - 5000
5000 - 10000
> 10000

请注意,此实现不会创建统一的间隔,它会创建间隔,这样每个间隔都包含一个具体的输入值。我不确定这是否是您想要的,您的要求并不那么清楚。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    • 2017-03-28
    • 2020-03-18
    相关资源
    最近更新 更多