diff --git a/SigmaSwiftStatistics/GeometricMean.swift b/SigmaSwiftStatistics/GeometricMean.swift new file mode 100644 index 0000000..26ffbd8 --- /dev/null +++ b/SigmaSwiftStatistics/GeometricMean.swift @@ -0,0 +1,26 @@ +// +// GeometricMean.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 30/12/2016. +// Copyright © 2016 Evgenii Neumerzhitckii. All rights reserved. +// +import Foundation + +public extension Sigma { + + public static func geometricMean(data: [Double]) -> Double? { + let count = data.count + if count == 0 { + return nil + } + var data_log: [Double] = [] + var log_val: Double + for item in data { + log_val = log(item) + data_log.append(log_val) + } + let return_val = exp(average(data_log)!) + return return_val + } +} diff --git a/SigmaSwiftStatistics/HarmonicMean.swift b/SigmaSwiftStatistics/HarmonicMean.swift new file mode 100644 index 0000000..147b8ce --- /dev/null +++ b/SigmaSwiftStatistics/HarmonicMean.swift @@ -0,0 +1,28 @@ + +// +// HarmonicMean.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 30/12/2016. +// Copyright © 2016 Evgenii Neumerzhitckii. All rights reserved. +// +import Foundation + +public extension Sigma { + + public static func harmonicMean(data: [Double]) -> Double? { + let count = data.count + if count == 0 { + return nil + } + var data_inv: [Double] = [] + var inv_val: Double + for item in data { + inv_val = 1.0 / item + data_inv.append(inv_val) + } + let m1 = average(data_inv) + let hm = 1.0 / m1! + return hm + } +} diff --git a/SigmaSwiftStatistics/Mode.swift b/SigmaSwiftStatistics/Mode.swift new file mode 100644 index 0000000..28fcadc --- /dev/null +++ b/SigmaSwiftStatistics/Mode.swift @@ -0,0 +1,48 @@ +// +// Mode.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 19/01/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import Foundation + +public extension Sigma { + /** + + Returns the mode(s) from the array after it is sorted and the indices where it occurs + + https://en.wikipedia.org/wiki/Mode_(statistics) + + - parameter values: Array of decimal numbers. + - returns: The mode value itself and an array of the indices where the mode occurs + + Example: + + Sigma.mode([1, 12, 9.5, 3, -5, 12]) // (12, [1,5]) + + */ + public static func mode(_ values: [Double]) -> (Double, [Int])? { + let count = values.count + if count == 0 { return nil } + else if count == 1 { + return (values[0], [0]) + } + var mode_value = values[0] + var mode_indices: [Int] = [0] + + for index in 1...(count - 1) { + if values[index] > mode_value { + mode_value = values[index] + mode_indices = [index] + } + else if values[index] == mode_value { + mode_indices.append(index) + } + } + return (mode_value, mode_indices) + } +} + + diff --git a/SigmaSwiftStatistics/Probabilities.swift b/SigmaSwiftStatistics/Probabilities.swift new file mode 100644 index 0000000..c2d66b3 --- /dev/null +++ b/SigmaSwiftStatistics/Probabilities.swift @@ -0,0 +1,21 @@ +import Foundation + +public extension Sigma { + + public static func gammln(xx: Double) -> Double? { + let coeffs = [76.18009173, -86.50532033, 24.01409822, -1.231739516, 0.120858003e-2, -0.536382e-5] + var x = xx - 1.0 + var tmp = x + 5.5 + tmp = tmp - (x + 0.5) * log(tmp) + var ser = 1.0 + for coeff in coeffs { + x = x + 1.0 + ser = ser + (coeff / x) + } + return -tmp + log(2.50662827465*ser) + } + + + +} + diff --git a/SigmaSwiftStatistics/Ttest.swift b/SigmaSwiftStatistics/Ttest.swift new file mode 100644 index 0000000..846279c --- /dev/null +++ b/SigmaSwiftStatistics/Ttest.swift @@ -0,0 +1,152 @@ +// +// Ttest.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 19/01/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import Foundation + +public extension Sigma { + /** + + Performs one of 3 t-tests: Single sample, unpaired samples, paired samples. + + Paired is also known as related, within or dependent + Unpaired is also known as unrelated, between or independent + + https://en.wikipedia.org/wiki/Student's_t-test + + */ + + /* + + One sample t-test + + This performs a one-sample t-test and returns the t-statistic, the degrees of freedom, the probability (p-value) + and the Cohen's d statistic + + - parameter values: Array of double numbers. + - returns: t (the t statistic), df (degrees of freedom) and p (probability), d (Cohen's d) + + Example: + + Sigma.tTest_one( + + */ + public static func tTest_one(_ values: [Double], userMean: Double) -> Dictionary? { + let count = Double(values.count) + if count == 0 { return nil } + else if count == 1 { + return (nil) + } + + let df = count - 1.0 + + let numerator = average(values)! - userMean + let denominator = standardDeviationSample(values)! / sqrt(Double(count)) + + if denominator != 0.0 { + let t = numerator / denominator + + let probability = tProbability(DF: df, T: t)! + + // compile results into dictionary + var results = [String: Double]() + + // store sums of squares + results["t"] = t + results["df"] = df + results["probability"] = probability + results["D"] = 0.0 + + return results + } + else { + return nil + } + } + + public static func tTest(_ values1: [Double], _ values2: [Double], testTails: Int = 2, testType: Int = 1) -> Dictionary? { + let count1 = Double(values1.count) + let count2 = Double(values2.count) + if count1 < 2 { return nil } + if count2 < 2 { return nil } + var df: Double + var t: Double // ignore warning about 't' not being used + var numerator: Double + var denominator: Double + + if (testType < 1 || testType > 3) { + return nil + } + else if testType == 1 { // Paired t-test + if count1 != count2 { return nil } + df = Double(count1 - 1) + + var deltaSum: Double = 0.0 + var deltaSquared: Double = 0.0 + var delta: Double = 0.0 + + for idx in 0...Int(count1 - 1.0) { + delta = values1[idx] - values2[idx] + deltaSum += delta + deltaSquared += (delta * delta) + } + + numerator = deltaSum / count1 + denominator = sqrt((deltaSquared - ((deltaSum * deltaSum) / count1)) / ((count1 - 1.0) * count1)) + + } + else { // Unpaired t-test + let m1 = average(values1)! + let m2 = average(values2)! + let s1 = varianceSample(values1)! + let s2 = varianceSample(values2)! + df = Double(count1 + count2 - 2.0) + + let sd = ((s1 * (count1 - 1)) + (s2 * (count2 - 1))) / (count1 + count2 - 2.0) + + // Alternative DF formula for unequal variances (heteroscedastic) + if testType == 3 { + let s12 = s1 / count1 + let s22 = s2 / count2 + let dfNumerator = (s12 + s22) * (s12 + s22) + let dfDenominator1 = (s12 * s12) / (count1 - 1.0) + let dfDenominator2 = (s22 * s22) / (count2 - 1.0) + + print (dfNumerator, dfDenominator1, dfDenominator2) + let dfDenominator = dfDenominator1 + dfDenominator2 + df = dfNumerator / dfDenominator + } + + numerator = m1 - m2 + denominator = sqrt((sd / count1) + (sd / count2)) + } + + if denominator == 0.0 { + print ("Divide by zero") + return nil + } + else { + let t = numerator / denominator + + // Calculate the probability + let probability = tProbability(DF: df, T: t)! + + // compile results into dictionary + var results = [String: Double]() + + // store sums of squares + results["t"] = t + results["df"] = df + results["probability"] = probability + + return results + } + } + +} + + diff --git a/SigmaSwiftStatistics/multipleRegression.swift b/SigmaSwiftStatistics/multipleRegression.swift new file mode 100644 index 0000000..3ef4a08 --- /dev/null +++ b/SigmaSwiftStatistics/multipleRegression.swift @@ -0,0 +1,9 @@ +// +// multipleRegression.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 22/02/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import Foundation diff --git a/SigmaSwiftStatistics/univariateANOVA.swift b/SigmaSwiftStatistics/univariateANOVA.swift new file mode 100644 index 0000000..6242bbe --- /dev/null +++ b/SigmaSwiftStatistics/univariateANOVA.swift @@ -0,0 +1,9 @@ +// +// univariateANOVA.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 16/02/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import Foundation diff --git a/SigmaSwiftStatisticsTests/GeometricMeanTests.swift b/SigmaSwiftStatisticsTests/GeometricMeanTests.swift new file mode 100644 index 0000000..5aa8de5 --- /dev/null +++ b/SigmaSwiftStatisticsTests/GeometricMeanTests.swift @@ -0,0 +1,45 @@ +import XCTest +import SigmaSwiftStatistics + +class GeometricMeanTests: XCTestCase { + + func testGeometricMean_Normal() { + let data_array = [0.52662978, 0.77362142, 0.57550701, 0.04158415, 0.03447811, + 0.08848505, 0.5236469 , 0.25523548, 0.89229563, 0.71272614, + 0.17107995, 0.26764894, 0.27308645, 0.38404429, 0.12755542, + 0.9856573 , 0.91394384, 0.50584635, 0.31623642, 0.13751698, + 0.68101821, 0.71853529, 0.66112074, 0.71656707, 0.35927775, + 0.76151524, 0.94317209, 0.01808385, 0.36550638, 0.9901121 , + 0.60259119, 0.62285146, 0.61310069, 0.55510847, 0.15929895, + 0.80369179, 0.26319102, 0.49952759, 0.34527164, 0.08919652, + 0.61979169, 0.43286263, 0.42874006, 0.1784381 , 0.51625026, + 0.74231264, 0.34506245, 0.70310094, 0.09531878, 0.02909812] + if let result = Sigma.geometricMean(data: data_array) { + XCTAssertEqualWithAccuracy(0.34079044910152717, result, accuracy: 0.000000000000001) + } + else { + XCTAssertNil(nil) + } + } + + func testGeometricMean_EmptyArray() { + let data_array: [Double] = [] + if let _ = Sigma.geometricMean(data: data_array) { + XCTFail() + } + else { + XCTAssertNil(nil) + } + } + + func testGeometricMean_SingleElement() { + let data_array = [0.52662978] + if let result = Sigma.geometricMean(data: data_array) { + XCTAssertEqualWithAccuracy(0.52662978000000005, result, accuracy: 0.000000000000001) + } + else { + XCTAssertNil(nil) + } + } + +} diff --git a/SigmaSwiftStatisticsTests/HarmonicMeanTests.swift b/SigmaSwiftStatisticsTests/HarmonicMeanTests.swift new file mode 100644 index 0000000..d95df50 --- /dev/null +++ b/SigmaSwiftStatisticsTests/HarmonicMeanTests.swift @@ -0,0 +1,45 @@ +import XCTest +import SigmaSwiftStatistics + +class HarmonicMeanTests: XCTestCase { + + func testHarmonicMean_Normal() { + let data_array = [0.52662978, 0.77362142, 0.57550701, 0.04158415, 0.03447811, + 0.08848505, 0.5236469 , 0.25523548, 0.89229563, 0.71272614, + 0.17107995, 0.26764894, 0.27308645, 0.38404429, 0.12755542, + 0.9856573 , 0.91394384, 0.50584635, 0.31623642, 0.13751698, + 0.68101821, 0.71853529, 0.66112074, 0.71656707, 0.35927775, + 0.76151524, 0.94317209, 0.01808385, 0.36550638, 0.9901121 , + 0.60259119, 0.62285146, 0.61310069, 0.55510847, 0.15929895, + 0.80369179, 0.26319102, 0.49952759, 0.34527164, 0.08919652, + 0.61979169, 0.43286263, 0.42874006, 0.1784381 , 0.51625026, + 0.74231264, 0.34506245, 0.70310094, 0.09531878, 0.02909812] + if let result = Sigma.harmonicMean(data: data_array) { + XCTAssertEqualWithAccuracy(0.17589575818127001, result, accuracy: 0.000000000000001) + } + else { + XCTAssertNil(nil) + } + } + + func testGeometricMean_EmptyArray() { + let data_array: [Double] = [] + if let _ = Sigma.harmonicMean(data: data_array) { + XCTFail() + } + else { + XCTAssertNil(nil) + } + } + + func testGeometricMean_SingleElement() { + let data_array = [0.52662978] + if let result = Sigma.harmonicMean(data: data_array) { + XCTAssertEqualWithAccuracy(0.52662978000000005, result, accuracy: 0.000000000000001) + } + else { + XCTAssertNil(nil) + } + } + +} diff --git a/SigmaSwiftStatisticsTests/Helpers/TtestTests.swift b/SigmaSwiftStatisticsTests/Helpers/TtestTests.swift new file mode 100644 index 0000000..176c759 --- /dev/null +++ b/SigmaSwiftStatisticsTests/Helpers/TtestTests.swift @@ -0,0 +1,136 @@ +import XCTest +import SigmaSwiftStatistics + +class TtestsTests: XCTestCase { + // MARK: - Sample variance + + let testAccuracy = 0.001 + + func testtTest_one() { + let data: [Double] = [62.0, 92, 75, 68, 83, 95] + let hyp_mean = 70.0 + let result = Sigma.tTest_one(data, userMean: hyp_mean) + // t = 1.7053, df = 5, p-value = 0.1489 (from R) + + // test 't' + if let t = result?["t"] { + XCTAssertEqualWithAccuracy(1.7053, t, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'probability' + if let probability = result?["probability"] { + XCTAssertEqualWithAccuracy(0.1489, probability, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'df' + if let df = result?["df"] { + XCTAssertEqualWithAccuracy(5, df, accuracy: testAccuracy) + } + else { + XCTFail() + } + } + + func testtTest_Unpaired_Unequal() { + let data1: [Double] = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99] + let data2: [Double] = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98] + var result = Sigma.tTest(data1, data2, testType: 3) + // t = 1.959, df = 7.0306, p-value = 0.09077 (unequal variances from Wikipedia) + + // Test 't' + if let t = (result?["t"]) { + XCTAssertEqualWithAccuracy(1.959, t, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'probability' + if let probability = result?["probability"] { + XCTAssertEqualWithAccuracy(0.09077, probability, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'df' + if let df = result?["df"] { + XCTAssertEqualWithAccuracy(7.0306, df, accuracy: testAccuracy) // 7.4018 + } + else { + XCTFail() + } + } + + func testtTest_Unpaired_Equal() { + let data1: [Double] = [30.02, 29.99, 30.11, 29.97, 30.01, 29.99] + let data2: [Double] = [29.89, 29.93, 29.72, 29.98, 30.02, 29.98] + var result = Sigma.tTest(data1, data2, testType: 2) + // t = 1.959, df = 10, p-value = 0.07857 (equal variances, from R) + + // test 't' + if let t = result?["t"] { + XCTAssertEqualWithAccuracy(1.959, t, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'probability' + if let probability = result?["probability"] { + XCTAssertEqualWithAccuracy(0.07857, probability, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'df' + if let df = result?["df"] { + XCTAssertEqualWithAccuracy(10.0, df, accuracy: testAccuracy) + } + else { + XCTFail() + } + } + + func testtTest_Paired() { + let data1: [Double] = [ 3, 3, 3,12,15,16,17,19,23,24,32] + let data2: [Double] = [20,13,13,20,29,32,23,20,25,15,30] + let result = Sigma.tTest(data1, data2, testType: 1) + // t = -2.7373, df = 10, p-value = 0.02093 (from R) + + // test 't' + if let t = result?["t"] { + XCTAssertEqualWithAccuracy(-2.7373, t, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'probability' + if let probability = result?["probability"] { + XCTAssertEqualWithAccuracy(0.02093, probability, accuracy: testAccuracy) + } + else { + XCTFail() + } + + // test 'df' + if let df = result?["df"] { + XCTAssertEqualWithAccuracy(10, df, accuracy: testAccuracy) + } + else { + XCTFail() + } + + } + + + +} diff --git a/SigmaSwiftStatisticsTests/ModeTests.swift b/SigmaSwiftStatisticsTests/ModeTests.swift new file mode 100644 index 0000000..b56672a --- /dev/null +++ b/SigmaSwiftStatisticsTests/ModeTests.swift @@ -0,0 +1,119 @@ +// +// ModeTests.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 19/01/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import XCTest +import SigmaSwiftStatistics + +class ModeTests: XCTestCase { + + func testMode_normalList() { + if let result = Sigma.mode([1, 12, 9.5, 3, -5, 12]) { + let (mode_value, mode_indices) = result + // (12, [1,5]) + XCTAssertEqual(12, mode_value) + XCTAssertEqual([1,5], mode_indices) + } + else { + XCTFail() + } + } + + func testMode_negativeList() { + if let result = Sigma.mode([-0.000001, -4, -5, -10, -1.43242, -0.0001]) { + let (mode_value, mode_indices) = result + // (-0.000001, [0]) + XCTAssertEqual(-0.000001, mode_value) + XCTAssertEqual([0], mode_indices) + } + else { + XCTFail() + } + } + + func testMode_singleItemList() { + if let result = Sigma.mode([1]) { + let (mode_value, mode_indices) = result + // (1, [0]) + XCTAssertEqual(1, mode_value) + XCTAssertEqual([0], mode_indices) + } + else { + XCTFail() + } + } + + func testMode_singleZeroItem() { + if let result = Sigma.mode([0]) { + let (mode_value, mode_indices) = result + // (0, [0]) + XCTAssertEqual(0, mode_value) + XCTAssertEqual([0], mode_indices) + } + else { + XCTFail() + } + } + + func testMode_emptyList() { + let test_data: [Double] = [] + if let _ = Sigma.mode(test_data) { + XCTFail() + } + else { + XCTAssertNil(nil) + } + } + + func testMode_positiveNegativeList() { + if let result = Sigma.mode([-1, 12, -9.5, 3, -5, -12]) { + let (mode_value, mode_indices) = result + // (12, [1]) + XCTAssertEqual(12, mode_value) + XCTAssertEqual([1], mode_indices) + } + else { + XCTAssertNil(nil) + } + } + + func testMode_allModes() { + if let result = Sigma.mode([12, 12, 12, 12, 12, 12, 12, 12, 12]) { + let (mode_value, mode_indices) = result + // (12, [0,1,2,3,4,5,6,7,8]) + XCTAssertEqual(12, mode_value) + XCTAssertEqual([0,1,2,3,4,5,6,7,8], mode_indices) + } + else { + XCTAssertNil(nil) + } + } + + func testMode_allZeroModes() { + if let result = Sigma.mode([0, 0, 0, 0, 0, 0, 0, 0]) { + let (mode_value, mode_indices) = result + // (0, [0,0,0,0,0,0,0,0]) + XCTAssertEqual(0, mode_value) + XCTAssertEqual([0,1,2,3,4,5,6,7], mode_indices) + } + else { + XCTAssertNil(nil) + } + } + + func testMode_slightDifference() { + if let result = Sigma.mode([4, 7.7777777, 5, 6, -33, 7.777777, 0, 7.7777777]) { + let (mode_value, mode_indices) = result + // (7.7777777, [1,7]) + XCTAssertEqual(7.7777777, mode_value) + XCTAssertEqual([1,7], mode_indices) + } + else { + XCTAssertNil(nil) + } + } +} diff --git a/SigmaSwiftStatisticsTests/ProbabilitiesTests.swift b/SigmaSwiftStatisticsTests/ProbabilitiesTests.swift new file mode 100644 index 0000000..d0f47d7 --- /dev/null +++ b/SigmaSwiftStatisticsTests/ProbabilitiesTests.swift @@ -0,0 +1,9 @@ +// +// probabilitiesTests.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 19/02/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import Foundation diff --git a/SigmaSwiftStatisticsTests/univariateANOVATests.swift b/SigmaSwiftStatisticsTests/univariateANOVATests.swift new file mode 100644 index 0000000..a8038e8 --- /dev/null +++ b/SigmaSwiftStatisticsTests/univariateANOVATests.swift @@ -0,0 +1,9 @@ +// +// univariateANOVATests.swift +// SigmaSwiftStatistics +// +// Created by Alan James Salmoni on 18/02/2017. +// Copyright © 2017 Evgenii Neumerzhitckii. All rights reserved. +// + +import Foundation