
Programming languages are tools, which we can use to write code to do things to make our lives easier. Sometimes we think that there is only one tool to do a particular job, when in reality there may be many. This happens in coding, too.
Take, for example, calculating a dataset’s descriptive statistics. If you are a statistician or data professional, you may default to Python or R, thinking that is the only possible tool to do the job. This isn’t the case, and there are plenty of programmers out there using a vast plethora of programming languages to perform this and other related statistics tasks.
So for the sake of argument, and a little comparative fun, let’s see what it takes to compute a dataset’s descriptive statistics in 5 different programming languages. See if this is how you would do it in yours, and get some idea of how others are up approaching the task.
I’m going to keep the editorial to a minimum, but will comment code well enough to be followed by most, even if you aren’t familiar with the language (hopefully!).
1. Bash
Let’s start with a widely known language: Bash.
#!/bin/bash
file="data.txt"
# Mean
mean=$(awk '{ total += $1; count++ } END { print total/count }' $file)
# Median
median=$(sort -n $file | awk '{
a[i++] = $1;
} END {
if (i % 2 == 0) {
print (a[i/2] + a[i/2 - 1]) / 2;
} else {
print a[int(i/2)];
}
}')
# Mode
mode=$(awk '{a[$1]++} END {for (i in a) {if (a[i] > max) {max = a[i]; mode = i}}; print mode}' $file)
# Range
min=$(sort -n $file | head -1)
max=$(sort -n $file | tail -1)
range=$(echo "$max - $min" | bc)
# Standard Deviation
stddev=$(awk '{ total += $1; count++; array[NR] = $1 }
END {
mean = total / count;
for(i=1; i<=count; i++){
sumsq += (array[i] - mean)^2;
}
variance = sumsq / count;
print sqrt(variance);
}' $file)
echo "Mean: $mean"
echo "Median: $median"
echo "Mode: $mode"
echo "Range: $range"
echo "Standard Deviation: $stddev"
You can find this code here with a discussion of its development.
2. R
Next up is a favorite of (some) data practitioners: R.
# Load necessary libraries
library(dplyr)
file <- "data.txt"
# Read the data
data <- scan(file)
# Mean
mean_value <- mean(data)
# Median
median_value <- median(data)
# Mode
get_mode <- function(v) {
unique_v <- unique(v)
unique_v[which.max(tabulate(match(v, unique_v)))]
}
mode_value <- get_mode(data)
# Range
range_value <- range(data)
range_result <- diff(range_value)
# Standard Deviation
stddev_value <- sd(data)
# Print results
cat(sprintf("Mean: %f\n", mean_value))
cat(sprintf("Median: %f\n", median_value))
cat(sprintf("Mode: %f\n", mode_value))
cat(sprintf("Range: %f\n", range_result))
cat(sprintf("Standard Deviation: %f\n", stddev_value))
3. Python
And now it's time for Python, the other preferred language of many data pros.
import numpy as np
from scipy import stats
file = "data.txt"
# Read the data
data = np.loadtxt(file)
# Mean
mean_value = np.mean(data)
# Median
median_value = np.median(data)
# Mode
mode_value = stats.mode(data).mode[0]
# Range
range_value = np.ptp(data)
# Standard Deviation
stddev_value = np.std(data)
# Print results
print(f"Mean: {mean_value}")
print(f"Median: {median_value}")
print(f"Mode: {mode_value}")
print(f"Range: {range_value}")
print(f"Standard Deviation: {stddev_value}")
4. Go
Fourth on our list is Go, a language that shouldn't be ignored any longer (if you have been doing so up to now).
package main
import (
"fmt"
"math"
"sort"
"os"
"io/ioutil"
"strconv"
"strings"
)
func main() {
// Data Acquisition Phase
file := "data.txt"
data, _ := ioutil.ReadFile(file)
numStr := strings.Fields(string(data))
var numbers []float64
for _, s := range numStr {
num, _ := strconv.ParseFloat(s, 64)
numbers = append(numbers, num)
}
// Central Tendency Metrics
mean := mean(numbers)
median := median(numbers)
mode := mode(numbers)
// Dispersion Metrics
r := rangeData(numbers)
stddev := stdDev(numbers)
// Results Output
fmt.Printf("Mean: %f\n", mean)
fmt.Printf("Median: %f\n", median)
fmt.Printf("Mode: %f\n", mode)
fmt.Printf("Range: %f\n", r)
fmt.Printf("Standard Deviation: %f\n", stddev)
}
// Arithmetic mean computation
func mean(data []float64) float64 {
total := 0.0
for _, v := range data {
total += v
}
return total / float64(len(data))
}
// Median computation
func median(data []float64) float64 {
sort.Float64s(data)
n := len(data)
if n%2 == 0 {
return (data[n/2-1] + data[n/2]) / 2
}
return data[n/2]
}
// Mode calculation
func mode(data []float64) float64 {
counts := make(map[float64]int)
for _, v := range data {
counts[v]++
}
var modeVal float64
maxCount := 0
for k, v := range counts {
if v > maxCount {
maxCount = v
modeVal = k
}
}
return modeVal
}
// Range computation
func rangeData(data []float64) float64 {
min, max := data[0], data[0]
for _, v := range data {
if v < min {
min = v
}
if v > max {
max = v
}
}
return max - min
}
// Standard deviation calculation
func stdDev(data []float64) float64 {
m := mean(data)
var variance float64
for _, v := range data {
variance += (v - m) * (v - m)
}
variance /= float64(len(data))
return math.Sqrt(variance)
}
5. Rust
Our final implementation comes in Rust. Rust is an immensely popular language often employed in systems programming, though it is definitely a general purpose lanugage.
use std::fs; use std::error::Error; use std::collections::HashMap; fn main() -> Result<(), Box> { // Data Acquisition Phase // Reads numeric data from file, parsing strings into f64 values let file = "data.txt"; let data: Vec = fs::read_to_string(file)? .lines() .filter_map(|line| line.parse().ok()) .collect(); // Central Tendency Metrics let mean_value = mean(&data); let median_value = median(&data); let mode_value = mode(&data); // Dispersion Metrics let range_value = range(&data); let stddev_value = std_dev(&data); // Results Output println!("Mean: {}", mean_value); println!("Median: {}", median_value); println!("Mode: {}", mode_value); println!("Range: {}", range_value); println!("Standard Deviation: {}", stddev_value); Ok(()) } // Arithmetic mean computation fn mean(data: &[f64]) -> f64 { let total: f64 = data.iter().sum(); total / data.len() as f64 } // Median computation via sorted position fn median(data: &[f64]) -> f64 { let mut sorted_data = data.to_vec(); sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap()); let mid = sorted_data.len() / 2; if sorted_data.len() % 2 == 0 { (sorted_data[mid - 1] + sorted_data[mid]) / 2.0 } else { sorted_data[mid] } } // Mode calculation via frequency counting fn mode(data: &[f64]) -> f64 { let mut counts = HashMap::new(); for &value in data { *counts.entry(value).or_insert(0) += 1; } *counts.iter().max_by_key(|entry| entry.1).unwrap().0 } // Range computation via min/max difference fn range(data: &[f64]) -> f64 { let min = data.iter().cloned().fold(f64::INFINITY, f64::min); let max = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max); max - min } // Standard deviation implementation fn std_dev(data: &[f64]) -> f64 { let m = mean(data); let variance: f64 = data.iter().map(|&x| (x - m).powi(2)).sum:: () / (data.len() as f64); variance.sqrt() }
Wrapping Up
I'm quite an experienced programmer, but even I admit that I don't know the best way to do everything when coding. I learn something new almost everyday. Hopefully this helps you do the same.
I hesitate to draw any conclusions from the above, since this is only how I have implemented these tasks, and there are other ways. Also, comparing apples to oranges generally isn't fruitful (pun intended).
Keep in mind that software engineering principles weren't followed implementing these; they could all be improved by adding error handling, robust file handling, input validation, and formatted output, at the very least (as well as memory management in some cases).
If there is a better way to any of the above, feel free to share your code!
