C# GroupBy и Average во вложенном словаре - PullRequest
2 голосов
/ 05 мая 2020

У меня есть следующие данные.

new Dictionary<DateTime, Dictionary<string, int>>() 
{
    { 
        new DateTime(2020,05,05,10,10,10) , 
        new Dictionary<string, int>() 
        {
            { "Value1", 2 },
            { "Value2", 4 },
            { "ValueXY", 6 },
        }
    },
    { 
        new DateTime(2020,05,05,10,10,12) , 
        new Dictionary<string, int>() 
        {
            { "Value1", 4 },
            { "Value2", 6 },
            { "ValueABC", 12 }
        }
    }
};

То, что я хочу сделать с LINQ, - это Группировать по DateTime без секунд, чтобы получать пакет в минуту. Дополнительное среднее значение для отдельных ключей.

Итак, в приведенном выше примере я бы получил это

new Dictionary<DateTime, Dictionary<string, int>>()
{
    { 
        new DateTime(2020,05,05,10,10,0) , 
        new Dictionary<string, int>()
        {
            { "Value1", 3 },
            { "Value2", 5 },
            { "ValueABC", 12 },
            { "ValueXY", 6 }
        }
    }
};

Мне удалось выполнить группировку, но я не получил среднего часть работает.

categorie.Value.GroupBy(row => row.Key.AddSeconds(-row.Key.Second));

Есть идеи, как это сделать?

Ответы [ 3 ]

2 голосов
/ 05 мая 2020

В вашем коде 3 проблемы.

  1. AddSeconds не будет работать с DateTime содержит миллисекунды, поэтому вы можете использовать дополнительные c методы для округления .
  2. Вы группируете только по значению DateTime, а не по ключам внутренних словарей.
  3. Среднее значение для 2 целых значений не является целым числом (например, avg для 2 и 3 равно 2.5), поэтому вам следует округлить его или преобразовать в целое число, если вы уверены, что результат целочисленный. или используйте вместо него double.

Вот полный код вашей проблемы:

DateTime RoundDown(DateTime dt, TimeSpan d)
{
    var delta = dt.Ticks % d.Ticks;
    return new DateTime(dt.Ticks - delta, dt.Kind);
}

Dictionary<DateTime, Dictionary<string, double>> result = categorie
    .GroupBy(kvp => RoundDown(kvp.Key, new TimeSpan(0, 0, 1, 0)), kvp => kvp.Value) // group by DateTime
    .ToDictionary( // retrieve result dictionary
        grouping => grouping.Key, // use DateTime from grouping as the key
        grouping => grouping.SelectMany(g => g) // flatten inner dictionaries to the IEnumerable<KeyValuePair<string, int>>
            .GroupBy(g => g.Key, g => g.Value) // group by string keys
            .ToDictionary(g => g.Key, g => g.Average())); // convert to the inner result dictionary with avg values
2 голосов
/ 05 мая 2020

Вы выполнили группировку, но вам не хватает более сложного комбинирования и усреднения значений во вложенных словарях. Вы можете сделать это следующим образом.

var x =new Dictionary<DateTime, Dictionary<string, int>>() 
{
    { 
        new DateTime(2020,05,05,10,10,10) , 
        new Dictionary<string, int>() 
        {
            { "Value1", 2 },
            { "Value2", 4 },
            { "ValueXY", 6 },
        }
    },
    { 
        new DateTime(2020,05,05,10,10,12) , 
        new Dictionary<string, int>() 
        {
            { "Value1", 4 },
            { "Value2", 6 },
            { "ValueABC", 12 }
        }
    }
};

var res = x.GroupBy(kvp => 
        kvp.Key.AddSeconds(-kvp.Key.Second).AddMilliseconds(-kvp.Key.Millisecond))
    .ToDictionary(
        grp => grp.Key,
        grp => grp.SelectMany(kvp => kvp.Value)
            .GroupBy(kvp => kvp.Key)
            .ToDictionary(
                grpInner => grpInner.Key, 
                grpInner => grpInner.Average(kvp => kvp.Value)));

Это выполнит вашу группировку, а затем создаст словарь на основе DateTime без секунд, а затем сгладит все пары значений ключа во внутренних словарях затем сгруппируйте по этим ключевым значениям и сгенерируйте среднее значение.

Изменить: Как отметил Вадим Мартынов, вам также следует позаботиться об усечении миллисекунд.

0 голосов
/ 05 мая 2020

Можно также использовать Enumerable.ToLookup для группировки части Date ключа datetime, затем добавить часы с DateTime.AddHours и минуты с DateTime.AddMinutes :

var result = categorie
    .ToLookup(
        kvp => kvp.Key.Date
            .AddHours(kvp.Key.Hour)
            .AddMinutes(kvp.Key.Minute),
        kvp => kvp.Value)
    .ToDictionary(
        group => group.Key,
        group => group
            .SelectMany(group => group)
            .ToLookup(
                kvp => kvp.Key, 
                kvp => kvp.Value)
            .ToDictionary(
                group => group.Key, 
                group => group.Average()));

Что первым преобразует группы в первые внешние Dictionary<DateTime...> с помощью Enumerable.ToDictionary, выравнивает внутренние значения с помощью Enumerable.SelectMany , создает поиск ключей и значений с использованием ToLookup, затем создает внутренний Dictionary<string, int> и получает средние значения с помощью Enumerable.Average. Конечный результат будет Dictionary<DateTime, Dictionary<string, double>>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...