Если вы хотите передать функцию, вам нужно Seq.averageBy
, а не Seq.average
.Seq.average
принимает последовательность чисел, тогда как Seq.averageBy
принимает функцию и последовательность вещей типа T (и функция должна быть такой, которая принимает что-то типа T и возвращает число).
Кроме того, если вы сначала используете Seq.groupBy
, имейте в виду, что он возвращает последовательность кортежей, где первый элемент кортежа - это ключ, а второй - последовательность значений, имеющих этот ключ.,(В сигнатуре типа это представлено типом seq<'Key * seq<'T>>
).Итак, то, что вы хотите, немного сложнее, и я проведу вас через это:
- Во-первых, если вы хотите получить среднее значение всей вашей последовательности, то это будет
rows |> Seq.averageBy(fun row -> row.Income)
. Но сначала вы звоните 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 })
}
В конце вы хотите получить:
seq {
(TX, 12345.0)
(CA, 34567.0)
(NY, 23456.0)
}
Следовательно, вам нужно взять последовательность, созданную с помощью Seq.groupBy
и , преобразовать таким образом, чтобы сохранить ключи, но преобразовать последовательность значений.Всякий раз, когда вы думаете: «Я хочу сохранить эту последовательность, но превращаю ее содержимое во что-то другое», вы хотите, чтобы Seq.map
.
Seq.map
брал функцию, которая занимает одинэлемент типа T (каким бы ни был T), но мы можем использовать деструктурирование в параметрах функции (посмотрите пример addOneToTuple
на этой странице), чтобы упростить его: так как мы знаем, что "внешний"последовательность, которую мы отображаем, является кортежем (key, values)
, мы можем написать функцию для приема кортежа (key, values)
: fun (key, values) -> key, (values |> Seq.averageBy ...)
будет тем, что вы хотите. Следовательно, конвейерчто вы хотите использовать, чтобы сначала сгруппировать, а затем усреднить значения в каждой группе (при этом сохраняя групповые ключи), будет выглядеть так:
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
здесь).