Iris¶
This example uses the iris dataset to predict the species of various flowers based on their physical features.
PetalLengthCm: Petal Length in cm
PetalWidthCm: Petal Width in cm
SepalLengthCm: Sepal Length in cm
SepalWidthCm: Sepal Width in cm
Species: The Flower Species (Setosa, Virginica, Versicolor)
We will follow the data science cycle (Data Exploration - Data Preparation - Data Modeling - Model Evaluation - Model Deployment) to solve this problem.
Initialization¶
This example uses the following version of vastorbit:
import vastorbit as vo
vo.__version__
Connect to VAST. This example uses an existing connection called VASTDSN .
For details on how to create a connection, see the Connection tutorial.
You can skip the below cell if you already have an established connection.
vo.connect("VASTDSN")
Let’s create a VastFrame of the dataset.
from vastorbit.datasets import load_iris
iris = load_iris()
iris.head(5)
123 sepallengthcmDecimal(5,2) | 123 sepalwidthcmDecimal(5,2) | 123 petallengthcmDecimal(5,2) | 123 petalwidthcmDecimal(5,2) | Abc speciesVarchar(30) | |
|---|---|---|---|---|---|
| 1 | 5.1 | 3.5 | 1.4 | 0.2 | Iris-setosa |
| 2 | 4.9 | 3.0 | 1.4 | 0.2 | Iris-setosa |
| 3 | 4.7 | 3.2 | 1.3 | 0.2 | Iris-setosa |
| 4 | 4.6 | 3.1 | 1.5 | 0.2 | Iris-setosa |
| 5 | 5.0 | 3.6 | 1.4 | 0.2 | Iris-setosa |
Data Exploration and Preparation¶
Let’s explore the data by displaying descriptive statistics of all the columns.
iris.describe(method = "categorical", unique = True)
| dtype | count | top | top_percent | unique | |
|---|---|---|---|---|---|
| "sepallengthcm" | decimal(5,2) | 150 | 5.0 | 6.667 | 35.0 |
| "sepalwidthcm" | decimal(5,2) | 150 | 3.0 | 17.333 | 23.0 |
| "petallengthcm" | decimal(5,2) | 150 | 1.5 | 9.333 | 43.0 |
| "petalwidthcm" | decimal(5,2) | 150 | 0.2 | 18.667 | 22.0 |
| "species" | varchar(30) | 150 | Iris-virginica | 33.333 | 3.0 |
We don’t have much data here, but that’s okay; since different flower species have different proportions and ratios between those proportions, we can start by making ratios between each feature.
We’ll need to use the One-Hot Encoder on the Species to get information about each species.
iris["Species"].one_hot_encode(drop_first = False)
iris["ratio_pwl"] = iris["PetalWidthCm"] / iris["PetalLengthCm"]
iris["ratio_swl"] = iris["SepalWidthCm"] / iris["SepalLengthCm"]
We can draw the correlation matrix (Pearson correlation coefficient) of the new features to see if there are some linear links.
iris.corr()
The Iris setosa is highly linearly correlated with the petal length and the sepal ratio. We can see a perfect separation using the two features (though we can also see this separation the petal length alone).
iris.scatter(
columns = ["PetalLengthCm", "ratio_swl"],
by = "Species",
)
We can we a clear linear separation between the Iris setosa and the other species, but we’ll need more features to identify the differences between Iris virginica and Iris versicolor.
iris.scatter(
columns = [
"PetalLengthCm",
"PetalWidthCm",
"SepalLengthCm",
],
by = "Species",
)
Our strategy is simple: we’ll use two Linear Support Vector Classification (SVC): one to classify the Iris setosa and another to classify the Iris versicolor.
Machine Learning¶
Let’s build the first LinearSVC to predict if a flower is an Iris setosa.
from vastorbit.machine_learning.vast import LinearSVC
from vastorbit.machine_learning.model_selection import cross_validate
predictors = ["PetalLengthCm", "ratio_swl"]
response = "Species_Iris-setosa"
model = LinearSVC("svc_setosa_iris", max_iter = 1000)
cross_validate(model, iris, predictors, response)
| auc | prc_auc | accuracy | log_loss | precision | recall | f1_score | mcc | informedness | markedness | csi | time | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1-fold | 1.0 | 0.17571428571428582 | 1.0 | 0.1948176137681635 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.39058709144592285 |
| 2-fold | 1.0 | 0.17387820512820507 | 1.0 | 0.17402997622758762 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.5655460357666016 |
| 3-fold | 1.0 | 0.1881127450980392 | 1.0 | 0.19978879457548462 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.3199501037597656 |
| avg | 1.0 | 0.17923507864684338 | 1.0 | 0.18954546152374527 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.42536107699076336 |
| std | 0.0 | 0.006322052366843215 | 0.0 | 0.01115723599691346 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.10323521042609826 |
Our model is excellent. Let’s build it using the entire dataset.
model.fit(iris, predictors, response)
Let’s plot the model to see the perfect separation.
model.plot()
We can add this probability to the VastFrame.
model.predict_proba(iris, name = "setosa", pos_label = 1)
123 sepallengthcmDecimal(5,2) | 123 sepalwidthcmDecimal(5,2) | 123 petallengthcmDecimal(5,2) | 123 petalwidthcmDecimal(5,2) | Abc speciesVarchar(30) | 123 species_Iris-setosaBool | 123 species_Iris-versicolorBool | 123 species_Iris-virginicaBool | 123 ratio_pwlDecimal(13,8) | 123 ratio_swlDecimal(13,8) | 123 setosaDouble | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5.1 | 3.5 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.68627451 | 0.7533862310328937 |
| 2 | 4.9 | 3.0 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.6122449 | 0.7347100983001176 |
| 3 | 4.7 | 3.2 | 1.3 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.15384615 | 0.68085106 | 0.7673806330944822 |
| 4 | 4.6 | 3.1 | 1.5 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.13333333 | 0.67391304 | 0.7342632994790236 |
| 5 | 5.0 | 3.6 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.72 | 0.761595468695358 |
| 6 | 5.4 | 3.9 | 1.7 | 0.4 | Iris-setosa | 1 | 0 | 0 | 0.23529412 | 0.72222222 | 0.7134820101455942 |
| 7 | 4.6 | 3.4 | 1.4 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.21428571 | 0.73913043 | 0.7661678386279932 |
| 8 | 5.0 | 3.4 | 1.5 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.13333333 | 0.68 | 0.7358342158526154 |
| 9 | 4.4 | 2.9 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.65909091 | 0.7466324281606391 |
| 10 | 4.9 | 3.1 | 1.5 | 0.1 | Iris-setosa | 1 | 0 | 0 | 0.06666667 | 0.63265306 | 0.7234590908839088 |
| 11 | 5.4 | 3.7 | 1.5 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.13333333 | 0.68518519 | 0.737167699846424 |
| 12 | 4.8 | 3.4 | 1.6 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.125 | 0.70833333 | 0.7267037644243937 |
| 13 | 4.8 | 3.0 | 1.4 | 0.1 | Iris-setosa | 1 | 0 | 0 | 0.07142857 | 0.625 | 0.7379915215420295 |
| 14 | 4.3 | 3.0 | 1.1 | 0.1 | Iris-setosa | 1 | 0 | 0 | 0.09090909 | 0.69767442 | 0.7996161340009732 |
| 15 | 5.8 | 4.0 | 1.2 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.16666667 | 0.68965517 | 0.7840216084848081 |
| 16 | 5.7 | 4.4 | 1.5 | 0.4 | Iris-setosa | 1 | 0 | 0 | 0.26666667 | 0.77192982 | 0.7588254032478668 |
| 17 | 5.4 | 3.9 | 1.3 | 0.4 | Iris-setosa | 1 | 0 | 0 | 0.30769231 | 0.72222222 | 0.7770233333481225 |
| 18 | 5.1 | 3.5 | 1.4 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.21428571 | 0.68627451 | 0.7533862310328937 |
| 19 | 5.7 | 3.8 | 1.7 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.17647059 | 0.66666667 | 0.6981988028179743 |
| 20 | 5.1 | 3.8 | 1.5 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.2 | 0.74509804 | 0.7522584560908878 |
Let’s create a model to classify the Iris virginica.
predictors = [
"PetalLengthCm",
"SepalLengthCm",
"SepalWidthCm",
"PetalWidthCm",
"ratio_pwl",
"ratio_swl",
]
response = "Species_Iris-virginica"
model = LinearSVC("svc_virginica_iris", max_iter = 1000)
cross_validate(model, iris, predictors, response)
| auc | prc_auc | accuracy | log_loss | precision | recall | f1_score | mcc | informedness | markedness | csi | time | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1-fold | 1.0 | 0.24272727272727268 | 1.0 | 0.20065743001538366 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 0.3792917728424072 |
| 2-fold | 1.0 | 0.2242324561403508 | 0.9583333333333334 | 0.239925695296195 | 1.0 | 0.8947368421052632 | 0.9444444444444444 | 0.914883536058052 | 0.8947368421052633 | 0.935483870967742 | 0.8947368421052632 | 0.14366984367370605 |
| 3-fold | 0.9966329966329966 | 0.21043076099835645 | 0.9411764705882353 | 0.23769605483224418 | 0.9411764705882353 | 0.8888888888888888 | 0.9142857142857143 | 0.8703882797784892 | 0.8585858585858586 | 0.8823529411764706 | 0.8421052631578947 | 0.30555009841918945 |
| avg | 0.9988776655443322 | 0.22579682995532666 | 0.9665032679738562 | 0.22609306004794094 | 0.9803921568627452 | 0.9278752436647174 | 0.9529100529100529 | 0.9284239386121804 | 0.9177742335637072 | 0.9392789373814042 | 0.912280701754386 | 0.2761705716451009 |
| std | 0.0015872206087240053 | 0.013231316803673081 | 0.024699700094495056 | 0.018008725302187445 | 0.027729677693590107 | 0.051055753815628845 | 0.03550102898881748 | 0.05377301913717624 | 0.059986281219858315 | 0.04810411977692991 | 0.06564311204866566 | 0.09840999549977007 |
We have another excellent model. Let’s add it to the VastFrame.
model.fit(iris, predictors, response)
model.predict_proba(iris, name = "virginica", pos_label = 1)
123 sepallengthcmDecimal(5,2) | 123 sepalwidthcmDecimal(5,2) | 123 petallengthcmDecimal(5,2) | 123 petalwidthcmDecimal(5,2) | Abc speciesVarchar(30) | 123 species_Iris-setosaBool | 123 species_Iris-versicolorBool | 123 species_Iris-virginicaBool | 123 ratio_pwlDecimal(13,8) | 123 ratio_swlDecimal(13,8) | 123 setosaDouble | 123 virginicaDouble | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5.1 | 3.5 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.68627451 | 0.7533862310328937 | 0.0007296201703394868 |
| 2 | 4.9 | 3.0 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.6122449 | 0.7347100983001176 | 0.0014058511173605517 |
| 3 | 4.7 | 3.2 | 1.3 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.15384615 | 0.68085106 | 0.7673806330944822 | 0.0011623041879743321 |
| 4 | 4.6 | 3.1 | 1.5 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.13333333 | 0.67391304 | 0.7342632994790236 | 0.0018303349093362288 |
| 5 | 5.0 | 3.6 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.72 | 0.761595468695358 | 0.0007114409374903144 |
| 6 | 5.4 | 3.9 | 1.7 | 0.4 | Iris-setosa | 1 | 0 | 0 | 0.23529412 | 0.72222222 | 0.7134820101455942 | 0.0008412646621078331 |
| 7 | 4.6 | 3.4 | 1.4 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.21428571 | 0.73913043 | 0.7661678386279932 | 0.001396973628006954 |
| 8 | 5.0 | 3.4 | 1.5 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.13333333 | 0.68 | 0.7358342158526154 | 0.0009996800043388435 |
| 9 | 4.4 | 2.9 | 1.4 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.14285714 | 0.65909091 | 0.7466324281606391 | 0.002270472963791192 |
| 10 | 4.9 | 3.1 | 1.5 | 0.1 | Iris-setosa | 1 | 0 | 0 | 0.06666667 | 0.63265306 | 0.7234590908839088 | 0.0012246022779389021 |
| 11 | 5.4 | 3.7 | 1.5 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.13333333 | 0.68518519 | 0.737167699846424 | 0.0005461931729756654 |
| 12 | 4.8 | 3.4 | 1.6 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.125 | 0.70833333 | 0.7267037644243937 | 0.0013341977091354206 |
| 13 | 4.8 | 3.0 | 1.4 | 0.1 | Iris-setosa | 1 | 0 | 0 | 0.07142857 | 0.625 | 0.7379915215420295 | 0.0012741245386073093 |
| 14 | 4.3 | 3.0 | 1.1 | 0.1 | Iris-setosa | 1 | 0 | 0 | 0.09090909 | 0.69767442 | 0.7996161340009732 | 0.0012243303583175569 |
| 15 | 5.8 | 4.0 | 1.2 | 0.2 | Iris-setosa | 1 | 0 | 0 | 0.16666667 | 0.68965517 | 0.7840216084848081 | 0.00019672232223855336 |
| 16 | 5.7 | 4.4 | 1.5 | 0.4 | Iris-setosa | 1 | 0 | 0 | 0.26666667 | 0.77192982 | 0.7588254032478668 | 0.0003086020837643312 |
| 17 | 5.4 | 3.9 | 1.3 | 0.4 | Iris-setosa | 1 | 0 | 0 | 0.30769231 | 0.72222222 | 0.7770233333481225 | 0.00047993869640503033 |
| 18 | 5.1 | 3.5 | 1.4 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.21428571 | 0.68627451 | 0.7533862310328937 | 0.000868941342595363 |
| 19 | 5.7 | 3.8 | 1.7 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.17647059 | 0.66666667 | 0.6981988028179743 | 0.0006185188604478436 |
| 20 | 5.1 | 3.8 | 1.5 | 0.3 | Iris-setosa | 1 | 0 | 0 | 0.2 | 0.74509804 | 0.7522584560908878 | 0.000739172518255419 |
Let’s evaluate our final model (the combination of two LinearSVC).
iris.case_when(
"prediction",
iris["setosa"] > 0.5, "Iris-setosa",
iris["virginica"] > 0.5, "Iris-virginica",
"Iris-versicolor",
)
iris["score"] = (iris["Species"] == iris["prediction"])
iris["score"].avg()
We have a great model with an accuracy of 96% on an entirely balanced dataset.
Conclusion¶
We’ve solved our problem in a pandas-like way, all without ever loading data into memory!