Iris¶
This example uses the iris dataset to predict the species of various flowers based on their physical features. You can download the Jupyter Notebook of the study here.
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 VerticaPy:
import verticapy as vp
vp.__version__
Out[2]: '1.1.0'
Connect to Vertica. This example uses an existing connection called VerticaDSN .
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.
vp.connect("VerticaDSN")
Let’s create a Virtual DataFrame of the dataset.
from verticapy.datasets import load_iris
iris = load_iris()
iris.head(5)
123 SepalLengthCm100% | ... | 123 PetalWidthCm100% | Abc Species100% | |
| 1 | 4.6 | ... | 0.2 | Iris-setosa |
| 2 | 4.7 | ... | 0.2 | Iris-setosa |
| 3 | 4.7 | ... | 0.2 | Iris-setosa |
| 4 | 4.8 | ... | 0.1 | Iris-setosa |
| 5 | 4.8 | ... | 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)
| ... | top_percent | unique | |
| "SepalLengthCm" | ... | 20.4 | 36.0 |
| "SepalWidthCm" | ... | 20.0 | 25.0 |
| "PetalLengthCm" | ... | 22.4 | 44.0 |
| "PetalWidthCm" | ... | 24.8 | 23.0 |
| "Species" | ... | 40.0 | 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 verticapy.machine_learning.vertica import LinearSVC
from verticapy.machine_learning.model_selection import cross_validate
predictors = ["PetalLengthCm", "ratio_swl"]
response = "Species_Iris-setosa"
model = LinearSVC("svc_setosa_iris")
cross_validate(model, iris, predictors, response)
| ... | csi | time | |
| 1-fold | ... | 1.0 | 17.181851625442505 |
| 2-fold | ... | 1.0 | 15.774991989135742 |
| 3-fold | ... | 1.0 | 13.243692636489868 |
| avg | ... | 1.0 | 15.400178750356039 |
| std | ... | 0.0 | 1.629445259086767 |
Our model is excellent. Let’s build it using the entire dataset.
model.fit(iris, predictors, response)
=======
details
=======
predictor |coefficient
-------------+-----------
Intercept | -0.23704
petallengthcm| -0.54910
ratio_swl | 3.13800
===========
call_string
===========
SELECT svm_classifier('public.svc_setosa_iris', '"public"."_verticapy_tmp_view_v_mldb_c772779097b711efa8720242ac120002_"', '"species_iris-setosa"', '"PetalLengthCm", "ratio_swl"'
USING PARAMETERS class_weights='1,1', C=1, max_iterations=100, intercept_mode='regularized', intercept_scaling=1, epsilon=0.0001);
===============
Additional Info
===============
Name |Value
------------------+-----
accepted_row_count| 250
rejected_row_count| 0
iteration_count | 8
Let’s plot the model to see the perfect separation.
model.plot()
We can add this probability to the vDataFrame.
model.predict_proba(iris, name = "setosa", pos_label = 1)
123 SepalLengthCm100% | ... | 123 ratio_swl100% | 123 setosa100% | |
| 1 | 4.6 | ... | 0.782608695652174 | 0.841533100572887 |
| 2 | 4.7 | ... | 0.680851063829787 | 0.76595900739171 |
| 3 | 4.7 | ... | 0.680851063829787 | 0.73514805993585 |
| 4 | 4.8 | ... | 0.625 | 0.722212112179584 |
| 5 | 4.8 | ... | 0.645833333333333 | 0.713207858194324 |
| 6 | 4.8 | ... | 0.708333333333333 | 0.719586013677281 |
| 7 | 4.9 | ... | 0.612244897959184 | 0.714111143515526 |
| 8 | 4.9 | ... | 0.63265306122449 | 0.715971528918452 |
| 9 | 4.9 | ... | 0.63265306122449 | 0.715971528918452 |
| 10 | 4.9 | ... | 0.63265306122449 | 0.715971528918452 |
| 11 | 5.0 | ... | 0.46 | 0.353061615330462 |
| 12 | 5.0 | ... | 0.68 | 0.745193749016457 |
| 13 | 5.1 | ... | 0.686274509803922 | 0.759098004217551 |
| 14 | 5.4 | ... | 0.555555555555556 | 0.275942083881882 |
| 15 | 5.4 | ... | 0.62962962962963 | 0.714038231013369 |
| 16 | 5.4 | ... | 0.722222222222222 | 0.788424580584295 |
| 17 | 5.5 | ... | 0.436363636363636 | 0.289169426943208 |
| 18 | 5.5 | ... | 0.436363636363636 | 0.278014495678082 |
| 19 | 5.6 | ... | 0.482142857142857 | 0.263022398357408 |
| 20 | 5.7 | ... | 0.526315789473684 | 0.290758414906492 |
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")
cross_validate(model, iris, predictors, response)
| ... | csi | time | |
| 1-fold | ... | 0.9655172413793104 | 22.611406087875366 |
| 2-fold | ... | 0.9705882352941176 | 19.794111967086792 |
| 3-fold | ... | 0.972972972972973 | 19.11236333847046 |
| avg | ... | 0.9696928165488004 | 20.50596046447754 |
| std | ... | 0.003108945635450922 | 1.5145673206054286 |
We have another excellent model. Let’s add it to the vDataFrame.
model.fit(iris, predictors, response)
model.predict_proba(iris, name = "virginica", pos_label = 1)
123 SepalLengthCm100% | ... | 123 SepalWidthCm100% | 123 virginica100% | |
| 1 | 4.6 | ... | 3.6 | 0.000455747290886906 |
| 2 | 4.7 | ... | 3.2 | 0.00139140638971681 |
| 3 | 4.7 | ... | 3.2 | 0.00251955766982754 |
| 4 | 4.8 | ... | 3.0 | 0.00223640521351681 |
| 5 | 4.8 | ... | 3.1 | 0.00279150100167775 |
| 6 | 4.8 | ... | 3.4 | 0.00336592475065291 |
| 7 | 4.9 | ... | 3.0 | 0.00207780291089906 |
| 8 | 4.9 | ... | 3.1 | 0.00230024485969189 |
| 9 | 4.9 | ... | 3.1 | 0.00230024485969189 |
| 10 | 4.9 | ... | 3.1 | 0.00230024485969189 |
| 11 | 5.0 | ... | 2.3 | 0.151843075577554 |
| 12 | 5.0 | ... | 3.4 | 0.00143941431306228 |
| 13 | 5.1 | ... | 3.5 | 0.00100133346021508 |
| 14 | 5.4 | ... | 3.0 | 0.373732220700207 |
| 15 | 5.4 | ... | 3.4 | 0.00116375600766135 |
| 16 | 5.4 | ... | 3.9 | 0.000405239187610279 |
| 17 | 5.5 | ... | 2.4 | 0.218651520531901 |
| 18 | 5.5 | ... | 2.4 | 0.250781925000777 |
| 19 | 5.6 | ... | 2.7 | 0.316586394528472 |
| 20 | 5.7 | ... | 3.0 | 0.235240010162963 |
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()
Out[4]: 0.968
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!