0

我想计算一个值(双精度)数组中元素的平均值或总和或其他方法,条件是具有相同长度的第二个数组。

例如,我有一个带有日期 (double[]dts) 的排序数组,现在我想计算值数组 (double[]vals) 上的开始日期 (std) 和结束日期 (edd) 之间的平均值(即简单,我下面的代码做了什么)。

但是,如果我想计算一年中所有星期一的平均值,下面的代码将不再起作用。有任何想法吗?谢谢!

public static double aggr(double[] dts, double[] vals, double std, double edd)
    {
        int stdindex = 0;
        int eddindex = dts.Length;

        for (int k = 0; k < dts.Length; k++)
        {
            if (dts[k] == std)
            {
                stdindex = k;
            }
            if (dts[k] == edd)
            {
                eddindex = k;
            }
        }


        return vals.Skip(stdindex).Take(eddindex-stdindex).Average();
    }

谢谢大家。我认为所有答案都有效。这是我的新代码:

 public static double aggr(double[] dts, double[] vals, double std, double edd, string peakoffpeakbase, double sumavg)
    {
        double result;
        if (sumavg == 1)
        {

            if (peakoffpeakbase=="Peak")
            {
                    result = dts.Zip(vals, (d, v) => new { d, v })
                                        .Where(dv =>  (dv.d >= std & dv.d < edd & DateTime.FromOADate(dv.d).DayOfWeek != DayOfWeek.Saturday & DateTime.FromOADate(dv.d).DayOfWeek != DayOfWeek.Sunday & DateTime.FromOADate(dv.d).Hour > 7 & DateTime.FromOADate(dv.d).Hour < 20))
                                        .Select(dv => dv.v).Sum();
            }
             else if (peakoffpeakbase=="Offpeak")
            {
                    result = dts.Zip(vals, (d, v) => new { d, v })
                                        .Where(dv => (dv.d >= std & dv.d < edd & DateTime.FromOADate(dv.d).DayOfWeek == DayOfWeek.Saturday | DateTime.FromOADate(dv.d).DayOfWeek == DayOfWeek.Sunday | DateTime.FromOADate(dv.d).Hour < 8 | DateTime.FromOADate(dv.d).Hour > 19))
                                        .Select(dv => dv.v).Sum();
             }
             else
             {
                    result = dts.Zip(vals, (d, v) => new { d, v })
                                       .Where(dv => (dv.d >= std && dv.d < edd))
                                       .Select(dv => dv.v).Sum();
            }
        }

        else
        {

            if (peakoffpeakbase == "Peak")
            {
                result = dts.Zip(vals, (d, v) => new { d, v })
                                    .Where(dv => (dv.d >= std & dv.d < edd & DateTime.FromOADate(dv.d).DayOfWeek != DayOfWeek.Saturday & DateTime.FromOADate(dv.d).DayOfWeek != DayOfWeek.Sunday & DateTime.FromOADate(dv.d).Hour > 7 & DateTime.FromOADate(dv.d).Hour < 20))
                                    .Select(dv => dv.v).Average();
            }
            else if (peakoffpeakbase == "Offpeak")
            {
                result = dts.Zip(vals, (d, v) => new { d, v })
                                    .Where(dv => (dv.d >= std & dv.d < edd & (DateTime.FromOADate(dv.d).DayOfWeek == DayOfWeek.Saturday | DateTime.FromOADate(dv.d).DayOfWeek == DayOfWeek.Sunday | DateTime.FromOADate(dv.d).Hour < 8 | DateTime.FromOADate(dv.d).Hour > 19)))
                                    .Select(dv => dv.v).Average();
            }
            else
            {
                result = dts.Zip(vals, (d, v) => new { d, v })
                                   .Where(dv => (dv.d >= std && dv.d < edd))
                                   .Select(dv => dv.v).Average();
            }
        }
            return result;

    }

显然这很糟糕而且非常冗长。我真正想做的是结合下面的答案并写下:

result = dts.Zip(vals, (d, v) => new { d, v })
                                        .Where(dv =>  (dv.d.InTimeRange(std,edd).IsBusinessHour(peakoffpeakbase))
                                        .Select(dv => dv.v).CustomX(indicator);

其中 InTimeRange 和 IsBusinessHour 是如下所述的扩展方法,customX 将接受一个参数,然后进行平均、求和或其他操作。但是我不能让它工作。再次感谢!

4

3 回答 3

2

您可以使用Zip组合两个数组:

double result = dts.Zip(vals, (d,v) => new { d, v})
    .Where( dv => SomeCondition(dv.d))
    .Select( dv => dv.v).Average();

这将计算所有vals值的平均值,谓词SomeCondition返回true相应dts值。

于 2013-04-05T14:47:25.260 回答
0

我不完全确定您在寻找什么,但您是否尝试过将双精度转换为 DateTime 以便您可以检查它?

DateTime.FromOADate(dts[k]).DayOfWeek

也许是这样的:

public static double aggr(double[] dts, double[] vals, double std, double edd, DayOfWeek dayOfWeek)
    {
         int stdindex = 0;
        int eddindex = dts.Length;

        List<double> newDts = new List<double>();
        List<double> newVals = new List<double>();

        // Use only values that meet criteria
        for (int k = 0; k < dts.Length; k++)
        {
            if (DateTime.FromOADate(dts[k]).DayOfWeek == dayOfWeek)
            {
                newDts.Add(dts[k]);
                newVals.Add(vals[k]);
            }
        }

        // Loop through dates that met the criteria
        for (int k = 0; k < newDts.Count; k++)
        {
            if (newDts[k] == std)
            {
                stdindex = k;
            }
            if (newDts[k] == edd)
            {
                eddindex = k;
            }
        }

        return newVals.Skip(stdindex).Take(eddindex - stdindex).Average();
    }
于 2013-04-05T14:25:05.483 回答
0

上面的答案(Henrik's)是最短的一个,但是如果您发现需要经常使用特定的过滤器范围,您可以编写扩展方法以使其更具可读性。

您可以将数据转换为更易于使用的形式。例如,您可以将double日期转换为DateTime. 然后,您可以将数据放入以下集合中:

public class Datum
{
    public DateTime DateTime;
    public double Value;
}

这将简化日期检查。

然后,您可以将范围选择实现为两个正交概念:

  1. 开始和结束日期的概念。
  2. 按一周中的特定日期过滤的概念。

您可以实现这些过滤器 - 以及平均值的计算 - 作为 IEnumerable 上的扩展方法,如下所示:

public static class EnumerableDatumExt
{
    public static double Average(this IEnumerable<Datum> @this)
    {
        return @this.Average(datum => datum.Value);
    }

    public static IEnumerable<Datum> InTimeRange(this IEnumerable<Datum> @this, DateTime start, DateTime end)
    {
        return from datum in @this
               where (start <= datum.DateTime && datum.DateTime <= end)
               select datum;
    }

    public static IEnumerable<Datum> ForEach(this IEnumerable<Datum> @this, DayOfWeek targetDayOfWeek)
    {
        return from datum in @this
               where datum.DateTime.DayOfWeek == targetDayOfWeek
               select datum;
    }
}

然后,您可以组合过滤器,为您提供一周中特定日期指定时间范围内所有数据的平均值,如下所示:

double average = data.ForEach(DayOfWeek.Monday).InTimeRange(start, end).Average();
// Or
double average = data.InTimeRange(start, end).ForEach(DayOfWeek.Monday).Average();

将所有这些放在一个可编译的控制台应用程序中:

using System;
using System.Linq;
using System.Collections.Generic;

namespace Demo
{
    public class Datum
    {
        public DateTime DateTime;
        public double Value;
    }

    public static class EnumerableDatumExt
    {
        public static double Average(this IEnumerable<Datum> @this)
        {
            return @this.Average(datum => datum.Value);
        }

        public static IEnumerable<Datum> InTimeRange(this IEnumerable<Datum> @this, DateTime start, DateTime end)
        {
            return from datum in @this
                   where (start <= datum.DateTime && datum.DateTime <= end)
                   select datum;
        }

        public static IEnumerable<Datum> ForEach(this IEnumerable<Datum> @this, DayOfWeek targetDayOfWeek)
        {
            return from datum in @this
                   where datum.DateTime.DayOfWeek == targetDayOfWeek
                   select datum;
        }
    }

    static class Program
    {
        static void Main()
        {
            var start  = new DateTime(2012, 01, 01);
            var end    = new DateTime(2012, 01, 31);
            var data   = createTestData(start);

            double average = data.ForEach(DayOfWeek.Monday).InTimeRange(start, end).Average();

            Console.WriteLine(average);
        }

        private static IEnumerable<Datum> createTestData(DateTime start)
        {
            var result = new List<Datum>();
            var oneDay = TimeSpan.FromDays(1);

            for (int i = 0; i < 20; ++i)
            {
                start += oneDay;
                result.Add(new Datum {DateTime = start, Value = i});
            }

            return result;
        }
    }
}
于 2013-04-05T14:52:25.823 回答