Как вы используете Seq.среднее по функции в F # для усреднения данных в одной строке, сгруппированных по данным в другой строке? - PullRequest
0 голосов
/ 07 июня 2018

У меня есть два ряда, State и Income.Есть несколько строк с одним и тем же состоянием, но я хочу одну строку с каждым состоянием.Поэтому я хочу усреднить все данные о доходах по штатам, чтобы получить среднее значение по штатам и только одно значение / строку на штат.Это моя попытка усреднить значения в строке «Доход» по строке «Состояние».Я сгруппировал последовательность следующим образом:

Seq.groupBy (забавная строка -> row.State)

Но когда я пытаюсь усреднить данные в столбце дохода, который уже сгруппирован по состоянию(сверху):

Seq.average (забавная строка -> row.Income)

Это дает мне эту ошибку:

"Ошибка FS0001: Ожидание типа, поддерживающегооператор '+', но с заданным типом функции. Возможно, вам не хватает аргумента для функции. "

Что я делаю неправильно?

1 Ответ

0 голосов
/ 07 июня 2018

Если вы хотите передать функцию, вам нужно Seq.averageBy, а не Seq.average.Seq.average принимает последовательность чисел, тогда как Seq.averageBy принимает функцию и последовательность вещей типа T (и функция должна быть такой, которая принимает что-то типа T и возвращает число).

Кроме того, если вы сначала используете Seq.groupBy, имейте в виду, что он возвращает последовательность кортежей, где первый элемент кортежа - это ключ, а второй - последовательность значений, имеющих этот ключ.,(В сигнатуре типа это представлено типом seq<'Key * seq<'T>>).Итак, то, что вы хотите, немного сложнее, и я проведу вас через это:

  1. Во-первых, если вы хотите получить среднее значение всей вашей последовательности, то это будет rows |> Seq.averageBy(fun row -> row.Income).
  2. Но сначала вы звоните Seq.groupBy, который возвращает последовательность кортежей.Если вы сделали rows |> Seq.groupBy (fun row -> row.State) |> Seq.averageBy (fun row -> row.Income), вы получите сообщение о том, что у кортежа нет свойства с именем Income.Поскольку вызов Seq.groupBy превратил ваши данные во что-то вроде этого:

    seq {
        (TX, seq { row1, row4, row7 })
        (CA, seq { row2, row5, row8 })
        (NY, seq { row3, row6, row9 })
    }
    
  3. В конце вы хотите получить:

    seq {
        (TX, 12345.0)
        (CA, 34567.0)
        (NY, 23456.0)
    }
    
  4. Следовательно, вам нужно взять последовательность, созданную с помощью Seq.groupBy и , преобразовать таким образом, чтобы сохранить ключи, но преобразовать последовательность значений.Всякий раз, когда вы думаете: «Я хочу сохранить эту последовательность, но превращаю ее содержимое во что-то другое», вы хотите, чтобы Seq.map.

  5. Seq.map брал функцию, которая занимает одинэлемент типа T (каким бы ни был T), но мы можем использовать деструктурирование в параметрах функции (посмотрите пример addOneToTuple на этой странице), чтобы упростить его: так как мы знаем, что "внешний"последовательность, которую мы отображаем, является кортежем (key, values), мы можем написать функцию для приема кортежа (key, values): fun (key, values) -> key, (values |> Seq.averageBy ...) будет тем, что вы хотите.
  6. Следовательно, конвейерчто вы хотите использовать, чтобы сначала сгруппировать, а затем усреднить значения в каждой группе (при этом сохраняя групповые ключи), будет выглядеть так:

    rows
    |> Seq.groupBy (fun row -> row.State)
    |> Seq.map (fun (state, groupedRows) ->
        let averageIncome = groupedRows |> Seq.averageBy (fun row -> row.Income)
        (state, averageIncome))
    

И это должно сделатьЭто.Обратите внимание, что на последнем шаге Seq.map я должен был вернуть кортеж (state, averageIncome);если бы я только что возвратил результат groupedRows |> Seq.averageBy (fun row -> row.Income), то я бы сопоставил кортеж с одним значением, и вы получили бы ряд средних доходов, к которым больше не привязывалось бы государство.

Я надеюсь, что это поможет вам увидеть процесс решения проблемы, подобной этой, в F #.Существует множество различных функций, которые работают с коллекциями, такими как списки или последовательности , и поначалу это может быть немного запутанным.Но основной подход один и тот же, независимо от того, новичок вы или опытный разработчик F #: вы начинаете с того, что говорите: «Какие данные у меня есть, и какие данные я хочу получить, когда закончу?»И тогда вы ищете функцию, которая имеет правильную «форму», чтобы превратить данные типа A в данные типа B;если для этого нет единой функции, вы объединяете несколько функций, таких как строительные блоки, чтобы получить всю необходимую вам функцию.(Например, как мы объединили Seq.map и Seq.averageBy здесь).

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