Мне часто приходится вычислять среднее и стандартное отклонение для числовых массивов. Поэтому я написал небольшой протокол и расширения для числовых типов, которые, похоже, работают. Я просто хотел бы получить обратную связь, если что-то не так с тем, как я это сделал. В частности, мне интересно, есть ли лучший способ проверить, может ли тип быть отличным как Double, чтобы избежать необходимости переменной asDouble и конструктором init(_:Double)
.Расширение Swift Array для стандартного отклонения
Я знаю, что есть проблемы с протоколами, которые допускают арифметику, но это, похоже, работает нормально и избавляет меня от установки стандартной функции отклонения в классы, которые в ней нуждаются.
protocol Numeric {
var asDouble: Double { get }
init(_: Double)
}
extension Int: Numeric {var asDouble: Double { get {return Double(self)}}}
extension Float: Numeric {var asDouble: Double { get {return Double(self)}}}
extension Double: Numeric {var asDouble: Double { get {return Double(self)}}}
extension CGFloat: Numeric {var asDouble: Double { get {return Double(self)}}}
extension Array where Element: Numeric {
var mean : Element { get { return Element(self.reduce(0, combine: {$0.asDouble + $1.asDouble})/Double(self.count))}}
var sd : Element { get {
let mu = self.reduce(0, combine: {$0.asDouble + $1.asDouble})/Double(self.count)
let variances = self.map{pow(($0.asDouble - mu), 2)}
return Element(sqrt(variances.mean))
}}
}
редактировать: Я знаю, что это своего рода бессмысленно получить [Int].mean
и sd
, но я мог бы использовать числовое в другом месте, так что это для последовательности ..
редактировать: в @Severin Pappadeux отметил, дисперсия может выражаться таким образом, чтобы избежать тройного прохода по массиву - значит, тогда значение карты означает среднее значение. Вот окончательное расширение стандартного отклонения
extension Array where Element: Numeric {
var sd : Element { get {
let sss = self.reduce((0.0, 0.0)){ return ($0.0 + $1.asDouble, $0.1 + ($1.asDouble * $1.asDouble))}
let n = Double(self.count)
return Element(sqrt(sss.1/n - (sss.0/n * sss.0/n)))
}}
}
'Int', как правило, такой же размер, как' Int64' на новых устройствах ('> =' iPhone 5S, которая представила процессор 64-битный), поэтому, если вы не работаете с действительно большими номерами, это не должно быть проблемой: но просто знайте, что 'init (_: Double)' может привести к переполнению целых чисел (исключение во время выполнения) в тех случаях, когда 'Element = Int' не может хранить целочисленное представление заданного (огромного) значения 'Double'. Возможно, это не проблема, если вы просто используете свои приложения Swift самостоятельно, но если вы отправляете их клиентам, это может быть полезно иметь в виду. – dfri
Хорошо, спасибо. Вряд ли я буду использовать его с целыми числами, а значения, с которыми я работаю, физиологически ограничены до <500 с этим приложением. Так должно быть хорошо. –
@dfri очень полезный комментарий! Я полагаю, что нет возможности «поймать» этот переток? – matt