Swiftを試してみた(簡単な記述統計関数)

新しく使う言語を試すときには音響プログラムを書いてみたり、統計プログラムを書いてみたりします。Pythonが簡単で面白かったので、Swiftはどんなだろうと試してみます。今回はSwiftで簡単な統計関数を作ってみました。と言っても平均や分散程度ですし、エラーチェックなども入っていないお遊びコードです。これを参考にはしないで下さいね。(実行環境:Xcode 8.2.1、Swift 3.0.2)

import Cocoa

// 和
func sum(_ X: [Double]) -> Double {
  var sum = 0.0
  for x in X {
    sum += x
  }
  return sum
}

// 平均
func mean(_ X: [Double]) -> Double {
  return sum(X) / Double(X.count)
}

// 中央値
func median(_ X: [Double]) -> Double {
  let Y = X.sorted()
  if Y.count % 2 == 1 {
    return Y[(Y.count-1)/2]
  } else {
    return (Y[Y.count/2-1] + Y[Y.count/2]) / 2.0
  }
}

// 平方和
func sumOfSquares(_ X: [Double]) -> Double {
  let mu = mean(X)
  var ss = 0.0
  for x in X {
    let deviation = x - mu
    ss += deviation * deviation
  }
  return ss
}

// 不偏分散(標本分散)
func vars(_ X: [Double]) -> Double {
  return sumOfSquares(X) / Double(X.count - 1)
}

// 分散(母分散)
func varp(_ X: [Double]) -> Double {
  return sumOfSquares(X) / Double(X.count)
}

// 標準偏差(標本標準偏差)
func stds(_ X: [Double]) -> Double {
  return sqrt(vars(X))
}

// 標準偏差(母標準偏差)
func stdp(_ X: [Double]) -> Double {
  return sqrt(varp(X))
}

Playgroundでしか試していませんが、以下のような感じで使います。

let X1: [Double] = [78, 70, 66, 76, 78, 76, 88, 76]   // データ列の定義
sum(X1)   // 和
mean(X1)   // 平均
median(X1)   // 中央値
vars(X1)   // 標本分散
stds(X1)   // 標本標準偏差
X1.max()   // 最大値
X1.min()   // 最小値

minmaxはもともと配列のためのメソッドとして用意されているものですので、今回作った関数と使用感が異なります。これは、Double配列型にメソッドを追加するとかで対応できそうです。

extension Array {
  func sum() -> Double {
    var sum = 0.0
    for x in self {
      sum += x as! Double
    }
    return sum
  }
}

X1.sum()

ただし、as! Doubleが気持ち悪く、このメソッドにDouble型以外の配列が来たときはどうなるんだろう、という心配があります。Doubleの配列のときだけ有効にしようとextension Array<Double>とやってみたけど「constrained extension must be declared on the unspecialized generic type ‘Array’ with constraints specified by a ‘where’ clause」というエラーメッセージが出ます。extension Array where Element: Doubleとかで動けばいいのに。

ウェブ検索するとExtension of constructed generic type in Swift - Stack Overflowという記事が見つかったので、参考にして以下のようにしたらうまくいった模様です。Arrayを拡張するのではなくSequenceを拡張するのですね。

extension Sequence where Iterator.Element == Double {
  func sum() -> Double {
    var sum = 0.0
    for x in self {
      sum += x
    }
    return sum
  }
}

Swiftは進化が速いので、最新版のXcodeではこれとは違う解決方法にな(ってい)るかもしれませんね。