count missing values in each column
Introduction
Julia is a high-performance, dynamic programming language designed for numerical and scientific computing. In recent years, it has been gaining traction as a tool for data science due to its speed, ease of use, and powerful ecosystem of libraries.
Some of the advantages of Julia for data science include:
- Performance approaching that of C/C++
- General purpose language suitable for end-to-end data science workflows
- Simple and expressive syntax
- Growing ecosystem of quality libraries for data analysis, machine learning, visualization, and more
- Built-in package manager
- Seamless interoperability with Python, R, and C/C++ code
In this tutorial, we‘ll walk through the process of learning data science with Julia from the ground up. We‘ll cover the fundamentals of the language, setting up the data science environment, working with data, building models, and more. By the end, you‘ll be equipped with the knowledge and skills to tackle data science projects using Julia.
Installing Julia and Setting Up the Environment
First, download the latest stable release of Julia from the official website: https://julialang.org/downloads/. Follow the platform-specific instructions to install.
Next, we‘ll set up the Jupyter notebook environment. Jupyter notebooks provide an interactive coding environment well-suited for data analysis. To install Jupyter, launch the Julia REPL and enter the following command:
using Pkg
Pkg.add("IJulia")
Once the installation finishes, you can launch the notebook server with:
using IJulia
notebook()
This will open the Jupyter interface in your web browser, where you can create and edit notebooks.
Julia Basics for Data Analysis
Before diving into data science specifics, let‘s cover some fundamentals of the Julia language often used in data analysis.
Data Structures
The main data structures to be familiar with are:
- Arrays: Store multiple values of the same type. Can be multi-dimensional.
arr = [1, 2, 3]
matrix = [1 2; 3 4]
- Dictionaries: Store key-value pairs.
dict = Dict("a" => 1, "b" => 2)
- Tuples: Immutable, ordered collections of values.
tup = (1, 2, 3)
- DataFrames: 2-dimensional tabular data structure akin to R‘s data.frame or Python‘s pandas.DataFrame. More on this later.
Functions
Defining functions in Julia is straightforward:
function add(x, y)
return x + y
end
Anonymous functions can be defined using the arrow syntax:
add = (x, y) -> x + y
Many built-in functions are useful for data analysis, like sum(), mean(), maximum(), length(), etc.
Control Flow
Julia provides standard control flow constructs like loops and conditionals. Some examples:
for i in 1:10
println(i)
end
x = 5
if x > 3
println("x is greater than 3")
elseif x < 3
println("x is less than 3")
else
println("x is 3")
end
Exploring Data with DataFrames.jl
To get a feel for data analysis in Julia, let‘s explore a real dataset using the DataFrames.jl package – one of the most popular data manipulation libraries.
First, we need to install the package:
using Pkg
Pkg.add("DataFrames")
We‘ll work with the Iris dataset, which comes bundled with Julia by default. The dataset contains measurements for 150 iris flowers from 3 different species.
Load the required packages and dataset:
using DataFrames, RDatasets
iris = dataset("datasets", "iris")
We can check the size of the DataFrame with size(iris) and view the column names with names(iris).
To peek at the first few rows, use first(iris, 5). This is a good way to get a quick look at the data.
Some other useful functions for summarizing a DataFrame include:
describe(iris): Generate summary statisticscombine(groupby(iris, :Species), nrow => :count): Group by a column and aggregatedfilter(:SepalLength => >5.0, iris): Filter rows matching a criteria
With the split-apply-combine functions groupby() and combine(), we can perform SQL-like data aggregations efficiently.
Visualizing Data with Plots.jl
Visualization is an important part of the data science workflow. Julia‘s Plots.jl library provides a powerful interface for creating a variety of plots and charts.
First install Plots.jl along with a plotting backend like GR.
using Pkg
Pkg.add("Plots")
Pkg.add("GR")
Let‘s create a scatter plot to visualize the relationship between sepal length and width in the iris dataset:
using Plots
gr()
scatter(iris.SepalLength, iris.SepalWidth,
xlabel="Sepal Length (cm)",
ylabel="Sepal Width (cm)",
title="Sepal Length vs Width",
legend=false)
This will render an interactive scatter plot in your Jupyter notebook.
We can easily create other types of charts as well. For example, a histogram of sepal lengths:
histogram(iris.SepalLength,
bins=20,
xlabel="Sepal Length (cm)",
ylabel="Frequency",
title="Distribution of Sepal Lengths")
Or a bar plot comparing average petal sizes across species:
using Statistics
iris_stats = combine(groupby(iris, :Species),
:PetalLength => mean => :MeanPetalLength,
:PetalWidth => mean => :MeanPetalWidth)
bar(iris_stats.Species,
[iris_stats.MeanPetalLength iris_stats.MeanPetalWidth],
label=["Petal Length" "Petal Width"],
ylabel="Length (cm)",
title="Mean Petal Size by Species")
Plots.jl supports many different chart types and customization options. Consult the documentation to learn more.
Data Wrangling
Real-world datasets often require some cleaning and preparation before they‘re ready for analysis. Julia provides some nice tools for data wrangling.
Let‘s look at an example of handling missing values in a DataFrame:
using DataFrames, RDatasets
iris_missing = dataset("datasets", "iris")
iris_missing[1:10, 1] .= missing
iris_missing[1:20, 2] .= NaN
show(combine(colwise(x -> count(ismissing, x), iris_missing), :auto))
Here we artificially add some missing values to the dataset, represented as missing or NaN. To count the number of missing values in each column, we use combine() together with the colwise() function to apply the ismissing() check to each column.
One way to handle the missing data is to simply remove any rows containing missing values:
iris_dropped = dropmissing(iris_missing)
An alternative is to impute the missing values, perhaps with the column mean:
iris_imputed = copy(iris_missing)
for n in names(iris_imputed)
replace!(x -> ismissing(x) || isnan(x) ? mean(skipmissing(iris_imputed[:, n])) : x, iris_imputed[:, n])
end
Here we loop through the columns, replacing any missing or NaN values with the column mean calculated after skipping missing data.
Another common data wrangling task is converting between wide and long formats. For this we can leverage the DataFrames.jl stack() and unstack() functions:
iris_long = stack(iris, 1:4)
iris_wide = unstack(iris_long, :variable, :value)
Machine Learning with MLJ.jl
MLJ.jl is a machine learning framework for Julia aiming to provide a consistent interface for a wide variety of ML models. It supports data preprocessing, model evaluation, hyperparameter tuning, and more.
To demonstrate modeling with MLJ.jl, let‘s build a decision tree classifier for predicting iris species.
First, install the required packages:
using Pkg
Pkg.add("MLJ")
Pkg.add("DecisionTree")
Load the DecisionTree model and examine the hyperparameters:
using MLJ
X, y = @load_iris
DecisionTreeClassifier = @load DecisionTreeClassifier pkg=DecisionTree
dtree_model = DecisionTreeClassifier()
params(dtree_model)
Next, we‘ll train the model on the iris data. We first define the machine, then call fit!():
dtree = machine(dtree_model, X, y)
fit!(dtree)
To evaluate the trained model on a holdout set, we can use evaluate!():
train, test = partition(eachindex(y), 0.7, shuffle=true)
evaluate!(dtree, resampling=Holdout(fraction_train=0.7),
measure=[accuracy, cross_entropy], verbosity=0)
Hyperparameter optimization is straightforward with TunedModel():
tuning = Grid(resolution=8)
resampling = CV(nfolds=5)
tm = TunedModel(model=dtree_model, tuning=tuning,
resampling=resampling, measure=cross_entropy)
tuned_dtree = machine(tm, X, y)
fit!(tuned_dtree)
best_model = fitted_params(tuned_dtree).best_model
This tunes the decision tree hyperparameters using grid search and cross-validation, returning the best model.
Finally, we can generate predictions on new data with the optimized model:
predict(tuned_dtree, selectrows(X, test))
To learn more about the MLJ.jl ecosystem, I highly recommend checking out the official documentation.
Calling Python and R Libraries
One of the strengths of Julia is its ability to interface with existing Python and R libraries. This allows us to leverage the large ecosystems of these languages within Julia.
To call Python code, we use the PyCall.jl package. For example, to use the Python pandas library:
using Pkg
Pkg.add("PyCall")
using PyCall
pd = pyimport("pandas")
df = pd.DataFrame(Dict(:A => 1:3, :B => 4:6))
Similarly, to call R code we use RCall.jl. Here‘s an example plotting with R‘s ggplot2:
using Pkg
Pkg.add("RCall")
using RCall
R"""
library(ggplot2)
ggplot($iris, aes(x=Sepal.Length, y=Sepal.Width, color=Species)) +
geom_point() +
theme_bw()
"""
The ability to call foreign libraries gives Julia great flexibility. However, in most cases I‘d recommend looking for a native Julia solution first.
Conclusion and Next Steps
In this tutorial we covered the basics of data science with Julia, from setting up the environment to training machine learning models. Julia is a fun and expressive language well-suited for data science and scientific computing. Its speed, simplicity, and well-designed libraries make it a compelling choice for data-intensive applications.
To further your Julia data science journey, I recommend exploring the following resources:
- Intro to Julia for Data Science (YouTube)
- DataFrames.jl documentation
- MLJ.jl documentation
- Plots.jl documentation
- Julia AI ecosystem
- Official Julia documentation
I hope this tutorial helps you get up and running with Julia for data science. Enjoy exploring all that this exciting language has to offer!