2013-04-05 2 views
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; 

    } 

분명히 이것은 매우 끔찍하고 매우 장황합니다. 내가 정말하고 싶은 것은 좋아 아래의 답변을 결합 STH 쓰기입니다 :

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가 인수를 다음 중 평균, 합계 또는 STH 다른 할 것 같은 확장 방법이 있습니다

. 그러나 나는 그것을 작동시킬 수 없습니다. 다시 한 번 감사드립니다!

+0

그래서 ... 무엇을 찾고 계십니까? – Nevyn

+0

http://upload.wikimedia.org/math/7/e/e/7eea8d495207d82b31b927502464ed6a.png 날짜를 계산할 때 산술 평균을 사용할 수 없습니다. DateTime에 DateTime을 추가 할 수 없으며 DateTime을 n으로 나눌 수 없기 때문입니다. –

+0

평균 방법의 결과로 얻으려는 것이 무엇입니까? –

답변

2

당신은 두 개의 배열 결합 Zip을 사용할 수 있습니다 :이 모든 vals 값의 평균을 계산

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

을하는 술어 SomeCondition 해당 dts 값에 대해 true을 반환합니다.

0

나는 당신이 찾고있는 것이 무엇인지 잘 모르겠지만 그것을 확인하기 위해 Double을 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(); 
    } 
0

위의 대답 (Henrik 's)은 ​​가장 짧은 답변이지만, 특정 필터 범위를 상당히 자주 사용해야하는 경우 확장 메서드를 작성하여보다 쉽게 ​​읽을 수 있습니다.

데이터를보다 쉽게 ​​사용할 수있는 형식으로 변환 할 수 있습니다. 예를 들어 double 날짜를 DateTime으로 변환 할 수 있습니다.

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

날짜 검사를 단순화 : 그럼 당신은 이들의 컬렉션에 데이터를 넣을 수 있습니다.

그럼 당신은 두 개의 직교 개념로 범위 선택을 구현할 수 있습니다

  1. 시작 날짜와 종료 날짜의 개념입니다.
  2. 특정 요일별 필터링 개념.
  3. 것은
당신은이 필터를 구현할 수

- 너무 평균의 계산 - 그래서 같은를 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; 
     } 
    } 
} 
관련 문제