
Based on a find from Mendel Mendelovits.

Based on a find from Mendel Mendelovits.

Thanks to Chloe Condon for the find, and Sophie Mallinson for the original tweet!

Every week, I compile a list of events for developers, technologists, tech entrepreneurs, and nerds in and around the Tampa Bay area. We’ve got a lot of events going on this week, and here they are!


With data science and machine learning a hot topic these days (and possibly a path to the hottest job at the moment), you may be experimenting with statistics, and in doing so, you may be rolling your own statistics methods. Don’t!
You wouldn’t chop down trees for lumber for a home renovation project; you’d go to Home Depot or a lumber store and get standard cuts of wood. In the same vein, you should make use of ready-made statistics libraries, which are proven, road-tested, and let you focus on what your application actually does.
These are the ones I use:
If you’re doing stats in JavaScript, you want jStat, which provides not just the basic statistical functions, but all manner of distributions, including Weibull (β), Cauchy, Poisson, hypergeometric, and beta distributions, with probability density functions (pdf), cumulative density functions (cdf), inverse, mean, mode, variance, and a sample function, allowing for more complex calculations.
jStat is contained in a single file: jstat.js; there’s also the minified version, jstat.min.js.
You can also get the most up-to-date version from jsdelivr’s content delivery netowork at http://cdn.jsdelivr.net/npm/jstat@latest/dist/jstat.min.js
To install it via npm, just do this on the command line…
npm install --save jStat
…and if you’re loading it while in Node, reference the child object. Here’s a session in Node:
var jStat = require('jStat').jStat
// Now we can use jStat!
let data = [20, 30, 30, 40, 40, 40]
jStat.mean(data) // 33.333333333333336
jStat.median(data) // 35
jStat.mode(data) // 40
jStat.stdev(data) // 7.453559924999299
For more in-depth statistics functions, you’ll want to go with Scipy, but for the basics — namely averages and measures of central location (mean, mode, median, and so on) and calculating spread (variance and standard deviation) — you might just want to use Python’s statistics library, which was introduced with Python 3.4.
To use it, import it first, and then you’re good to go! Here’s a session in the Python REPL:
import statistics // Now we can use statistics! data = [20, 30, 30, 40, 40, 40] statistics.mean(data) # 33.333333333333336 statistics.median(data) # 35.0 statistics.mode(data) # 40 statistics.stdev(data) # 8.16496580927726 # Wait, why’s this different from the JavaScript result? # That’s because in Python’s statistics library, # stdev() is the *sample* standard deviation, # while jStat’s stdev() is the *population* standard deviation. # To get the population standard deviation in Python’s statistics, # use pstdev(). statistics.pstdev(data) # 7.453559924999299
You can add it to your project in a number of ways:
Here it is in action, in a Swift playground:
let data: [Double] = [20, 30, 30, 40, 40, 40] Sigma.average(data) // 33.333333333333336 Sigma.median(data) // 35 // Oddly enough, there’s no mode function. Sigma.standardDeviationPopulation(data) // 7.453559924999299 Sigma.standardDeviationSample(data) // 8.164965809277259
If Kotlin’s your jam and you want to do stats, you want Kotlin Statistics.
I use Kotlin primarily in Android Studio, so I use Gradle to include it:
dependencies {
compile 'org.nield:kotlin-statistics:1.0.0'
}
Here it is, inside an Android app written in Kotlin:
val data = sequenceOf(20, 30, 30, 40, 40, 40) val average = data.average() // 33.333333333333336 val median = data.median() // 35.0 val mode = data.mode() // 40 val standardDeviation = data.standardDeviation() // 7.453559924999299
In the last installment, we laid out the foundation for a simple “notepad”-style app where the user can type in some text, save it with the press of a “Save” button, and then retrieve the saved text of by pressing the “Load” button.
Here’s what we did in the previous article: we built a simple app with only three controls…
We applied constraints to the control and wired them up with an outlet and actions as shown below:
Click the diagram to see it at full size.
At the end of the last article, the Save and Load buttons didn’t save or load — they simply changed the contents of the text view. Clicking the Save button changes the contents of the text view to this:

Here’s the code for the Save button action:
@IBAction func saveButtonPressed(_ sender: UIButton) {
userText.text = "You pressed the 'Save' button!"
}
Clicking the Load button changes the contents of the text view to this…

…and here’s the code for its action:
@IBAction func loadButtonPressed(_ sender: UIButton) {
userText.text = "Ah, the 'Load' button. Nice."
}
In this article, we’ll make the Save and Load buttons do what they’re supposed to do: save and load the user’s text.
But before we do that, we need to look at accessing files in iOS.
An iOS app’s access to its iDevice’s filesystem isn’t like the access a desktop application have to its computer’s filesystem. Every iOS app is cordoned off to its own space in the filesystem that only it can access: its sandbox. For two apps — we’ll call them App A and App B — App A’s filesystem access is limited to the App A sandbox, and App B’s is limited to the App B sandbox.
I’ve taken the filesystem sandbox diagram from Apple’s File System Programming Guide and added a few details to it:

For our notepad app, we’re going to store the data that the user types inside a directory in the Data container, which is the part of the sandbox specifically for storing user- and application-related data.
The data container contains a number of subdirectories, one of which is the Documents directory, the designated directory for files that the user will create, import, delete or edit. As an added bonus, unless you specify otherwise, files in this directory are backed up by default.

We want to change the code in our app’s saveButtonPressed and loadButtonPressed methods so that they do the following:
saveButtonPressed: Take the contents of the userText text view and save them in the Documents directory in a file named savefile.txt.loadButtonPressed: Take the contents of the file named savefile.txt in the Documents directory and put them into the userText text view.We’ll store and retrieve our notepad app’s data to and from a file in the Documents directory, which we’ll access with the help of…
FileManager.default object, which is a one-stop object for working with the file system that should work for most common file system operations, including reading and writing files, andFileManager’s url method, which we use to access specific directories in the sandbox. If we pass it a parameter that we want the user’s Documents directory for the app, we get an URL struct representing the location of that directory.Here’s code that gets the Documents directory for the current app and stores it in a constant named documentsDirectoryURL (don’t bother entering this right now):
let documentsDirectoryURL = try! FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
What powers this code is the url(for:in:appropriateFor:create:) method, which can locates and returns the URL for a directory in a given domain. It can even create a directory. It has these parameters:
for: The directory to search for, which we specify with a value of the enum FileManager.SearchPathDirectory. In this case, we’re looking for the Documents directory, so we’ll use the value FileManager.SearchPathDirectory.documentDirectory, or .documentDirectory for short.in: Where to look for the directory, which we specify with a value of the enum FileManager.SearchPathDomainMask. In this case, we’re searching the user’s filespace, so we’ll use the value FileManager.SearchPathDomainMask.userDomainMask, or .userDomainMask for short.appropriateFor: This is often used to specify a temporary directory to be created, which we’re not doing here. As a result, we’re simply setting this to nil, which says “No, we’re not creating a temporary directory”.create: A boolean value that specifies if the directory should be created if it doesn’t already exist. In this case, we don’t want to do that, so we’ll set this value to false.Now that we have the URL for the documents directory, we now need an URL for the file where we’ll save and retrieve our notepad data from.
Once we have the Documents directory, we can create an URL that refers to a file in that directory by using the URL struct’s URL(fileURLWithPath:relativeTo:)method and store it in saveFileURL (again, don’t bother entering this right now; we’ll do some coding very soon):
let saveFileURL = URL(fileURLWithPath: "savefile.txt",
relativeTo: documentsDirectoryURL)
The method has these parameters:
fileURLWithPath: The name of the file path.relativeTo: The base URL for the file path. Note that we’re using the documentsDirectoryURL constant that we created in the previous section.Now let’s enter some code — a computed property that gives us the URL for the file where we want to save data, saveFileURL. Put this inside the ViewController class, outside any methods:
private var saveURL: URL {
let documentsDirectoryURL = try! FileManager.default.url(for: FileManager.SearchPathDirectory.documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
return URL(fileURLWithPath: "savefile.txt",
relativeTo: documentsDirectoryURL)
}
With this property, we can now make the Save and Load buttons do what they’re supposed to.
Change the saveButtonClicked(_:) method so that it looks like this:
@IBAction func saveButtonClicked(_ sender: Any) {
try! userText.text.write(to: saveURL,
atomically: true,
encoding: .utf8)
}
In our reformulated method, we take the text property of the userText text area — an instance of String that contains the text inside the text area — and use String’s write(to:atomically:encoding:) method to write the text property’s contents to file referenced by our saveURL computer property. Here’s what the other parameters are for:
atomically: A boolean, that if set to true, has the write(to:atomically:encoding:) method first write the string to a temporary file, and then once done, rename the temporary file to its final name. This ensures that if the iPhone or iPad crashes during the write process, you won’t end up with a corrupted file.encoding: The way in which the string we’re saving should be encoded, which we specify with a value of the enum String.Encoding. We want to save the string in the preferred string format of the web, UTF-8, so we’ll use the value String.Encoding.utf8, or .utf8 for short.The Save button now does what it’s supposed to do: save the contents of the text area to a save file.
Change the loadButtonClicked(_:) method so that it looks like this:
@IBAction func loadButtonClicked(_ sender: Any) {
do {
try userText.text = String(contentsOf: saveURL)
} catch {
userText.text = "Encountered an error while trying to load saved data."
}
}
In our reformulated method, we take the text property of the userText text area — an instance of String that contains the text inside the text area — and use String’s init(contentsOf:) method to fill it with the contents of the file referenced by our saveURL computer property. The Load button now does what it’s supposed to do: load the contents of the save file into a text area.
The code in ViewController.swift should look like this now:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var userText: UITextView!
// View controller events
// ======================
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Save and load button events
// ===========================
@IBAction func saveButtonClicked(_ sender: Any) {
try! userText.text.write(to: saveURL,
atomically: true,
encoding: String.Encoding.utf8)
}
@IBAction func loadButtonClicked(_ sender: Any) {
do {
try userText.text = String(contentsOf: saveURL)
} catch {
userText.text = "Encountered an error while trying to load saved data."
}
}
private var saveURL: URL {
let documentsDirectoryURL = try! FileManager.default.url(
for: FileManager.SearchPathDirectory.documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
return URL(fileURLWithPath: "savefile.txt",
relativeTo: documentsDirectoryURL)
}
}
If your code looks like this, it’s ready to run.

If you like, you can download the finished project here [37K .zip].
If you’ve followed the steps in this article and the previous one, the app should work as expected. You should be able to:
The project that we just covered was the one that I used in the last Tampa iOS Meetup. At the end of the session, one of the attendees asked an interesting question:
“What if we changed the text view to something else? Maybe a slider?”
“Why don’t we give it a try right now?” I replied. “Let’s change the app so that the Save button saves the current position of the slider, and the Load button restores the slider to the saved position.”

I opened Main.storyboard, deleted the text view, and then dragged a slider into its place.

With the slider still selected, I set its constraints as shown above.

Then, I created an outlet for the slider. Using the Assistant Editor, I:
It’s now time to change the code behind the Save and Load buttons.
Here’s the revised saveButtonClicked(_:) method:
@IBAction func saveButtonClicked(_ sender: Any) {
let sliderValue = String(userValue.value)
print("Saving slider value: \(sliderValue)")
try! sliderValue.write(to: saveURL,
atomically: true,
encoding: String.Encoding.utf8)
}
Here’s what’s happening inside this method:
value property — by default, a Float whose value can range from 0 (all the way to the left) to 1 (all the way to the right) — and convert it to into a String.String-ified slider value.String-ified slider value and use String’s write(to:atomically:encoding:) method to write its value to savefile.txt.Here’s the revised loadButtonClicked(_:) method:
@IBAction func loadButtonClicked(_ sender: Any) {
do {
let sliderValue = try String(contentsOf: saveURL)
print("Loading slider value: \(sliderValue)")
userValue.value = Float(sliderValue)!
} catch {
print("Encountered an error while trying to load saved data.")
}
}
Here’s what’s happening inside this method:
saveURL computer property and put it inside the constant sliderValue.sliderValue.sliderValue into a Float and use that value to set the position of the slider.The code in ViewController.swift should look like this now:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var userValue: UISlider!
// View controller events
// ======================
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Save and load button events
// ===========================
@IBAction func saveButtonClicked(_ sender: Any) {
let sliderValue = String(userValue.value)
print("Saving slider value: \(sliderValue)")
try! sliderValue.write(to: saveURL,
atomically: true,
encoding: String.Encoding.utf8)
}
@IBAction func loadButtonClicked(_ sender: Any) {
do {
let sliderValue = try String(contentsOf: saveURL)
print("Loading slider value: \(sliderValue)")
userValue.value = Float(sliderValue)!
} catch {
print("Encountered an error while trying to load saved data.")
}
}
private var saveURL: URL {
let documentsDirectoryURL = try! FileManager.default.url(
for: FileManager.SearchPathDirectory.documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
return URL(fileURLWithPath: "savefile.txt",
relativeTo: documentsDirectoryURL)
}
}
If your code looks like this, it’s ready to run.
If you like, you can download the finished project here [37K .zip].
In this exercise, we have, over two articles:
If you’re in the Tampa Bay area, come to Tampa iOS Meetup next Tuesday, May 22nd, when I’ll walk you through the process of building a “to-do list” app, and in the process cover:
After the meetup, I’ll write it up here. Stay tuned!

Every week, I compile a list of events for developers, technologists, tech entrepreneurs, and nerds in and around the Tampa Bay area. We’ve got a lot of events going on this week, and here they are!
“Facebook reportedly plans to launch its own cryptocurrency,” reads The Verge’s headline, followed by the subhead “With an eye towards payments on the social media platform”. My immediate reaction is best summed up with the picture below:
