Yet Another Iris EDA. An attempt to extract useful insights... | by Aditya Jetely | Towards Data Science

In this notebook, I have done the Exploratory Data Analysis of the famous Iris dataset and tried to gain useful insights from the data.
The features present in the dataset are:
Sepal Width
Sepal Length
Petal Width
Petal Length
import pandas as pdimport numpy as npimport matplotlibimport matplotlib.pyplot as pltimport seaborn as sns%matplotlib inlinesns.set_style('darkgrid')matplotlib.rcParams['font.size'] = 14matplotlib.rcParams['figure.figsize'] = (9, 5)matplotlib.rcParams['figure.facecolor'] = '#00000000'
iris_df = pd.read_csv("IRIS.csv")
iris_df.head()
iris_df.shape(150, 5)
We observe from this above line that our dataset has 150 rows and 5 columns
iris_df.columnsIndex(['sepal_length', 'sepal_width', 'petal_length', 'petal_width',       'species'],      dtype='object')# Further inspecting the dataframedef inspect_data(data):    return pd.DataFrame({"Data Type":data.dtypes,"No of Levels":data.apply(lambda x: x.nunique(),axis=0), "Levels":data.apply(lambda x: str(x.unique()),axis=0)})inspect_data(iris_df)
Let's gain some more information about our dataset
iris_df.info()<class 'pandas.core.frame.DataFrame'>RangeIndex: 150 entries, 0 to 149Data columns (total 5 columns): #   Column        Non-Null Count  Dtype  ---  ------        --------------  -----   0   sepal_length  150 non-null    float64 1   sepal_width   150 non-null    float64 2   petal_length  150 non-null    float64 3   petal_width   150 non-null    float64 4   species       150 non-null    object dtypes: float64(4), object(1)memory usage: 6.0+ KB
We see all the columns of our data set are free from null values and four of them are numeric while one is a categorical column which also is seen by the dtypes function.
iris_df.dtypessepal_length    float64sepal_width     float64petal_length    float64petal_width     float64species          objectdtype: object
iris_df.describe(include='all')
iris_df.describe() #Performing just for numeric columns
iris_df.duplicated().sum() # Total no of duplicates in the dataset3iris_df[iris_df.duplicated()] #This shows the actual duplicate rows
There are 3 duplicates in our dataset but removing them might break the balance of our dataset as we see that our dataset is balanced as in the describe function for the species column we saw the frequency of 50 which can be further proved by the following line of code.
iris_df['species'].value_counts()Iris-setosa        50Iris-versicolor    50Iris-virginica     50Name: species, dtype: int64
So we can let the duplicate values as they are and proceed.
We confirmed through the info function that our dataset is free from null values but let us further justify it.
iris_df.isnull().sum(axis=0)sepal_length    0sepal_width     0petal_length    0petal_width     0species         0dtype: int64
iris_df.skew()sepal_length    0.314911sepal_width     0.334053petal_length   -0.274464petal_width    -0.104997dtype: float64iris_df.kurt()sepal_length   -0.552064sepal_width     0.290781petal_length   -1.401921petal_width    -1.339754dtype: float64
Our target column is the species column because, in the end, we want to predict the correct species of flower.
plt.title('Species Count')sns.countplot(iris_df['species']);
This further tells that our dataset is balanced with equal records for all three species.
Let's compare the sepal length and sepal width columns.
plt.title('Comparison between sepal width and length')sns.scatterplot(iris_df['sepal_length'], iris_df['sepal_width']);
Let's compare them again but this time lets compare on the basis on species as well.
plt.figure(figsize=(16,9))plt.title('Comparison between sepal width and length on the basis of species')sns.scatterplot(iris_df['sepal_length'], iris_df['sepal_width'], hue = iris_df['species'], s= 50);
From the above visualization, we can tell that the iris-setosa species has smaller sepal length but higher width. While we see Versicolor lies in almost middle for length as well as width. While Virginica has larger sepal lengths and smaller sepal widths.
Let's compare the petal length and petal width columns.
plt.title('Comparison between petal width and length')sns.scatterplot(iris_df['petal_length'], iris_df['petal_width']);
We can see two separate clusters but not sure about the species so let's bring the species into the equation as well.
plt.figure(figsize=(16,9))plt.title('Comparison between petal width and length on the basis of species')sns.scatterplot(iris_df['petal_length'], iris_df['petal_width'], hue = iris_df['species'], s= 50);
We see that setosa has the smallest petal length as well as petal widths, while Versicolor has average petal length and petal width while the virginica species has the highest petal length as well as petal width.
Now let's visualize all the columns relationship using pair plots.
sns.pairplot(iris_df,hue="species",height=3);
Pair plot represents the relationship between our target and the variables. We can see that the setosa species has a large difference in its characteristics when compared to the other species, it has smaller petal width and length while its sepal width is high and its sepal length is low. Similar kind of conclusions can be drawn for the other species like the Versicolor species usually have average dimensions whether it is sepal or pedal. While virginica has high pedal width and length while it has small sepal width but large sepal length.
categorical_columns = iris_df.dtypes[iris_df.dtypes == 'object'].indexprint('No of categorical fetures:',len(categorical_columns),'\n')print(categorical_columns)print('*'*100)numerical_columns = iris_df.dtypes[iris_df.dtypes != 'object'].indexprint('No of numerical fetures:',len(numerical_columns),'\n')print(numerical_columns)No of categorical fetures: 1 Index(['species'], dtype='object')****************************************************************************************************No of numerical fetures: 4 Index(['sepal_length', 'sepal_width', 'petal_length', 'petal_width'], dtype='object')
This function just further supports our earlier observations.
iris_df[numerical_columns].describe()
We can see that the mean and median values don't have a large difference among their values so no data transformation is required.
iris_df.corr()
Further, we can visualise this correlation using a heatmap.
fig = plt.figure(figsize = (15,9))sns.heatmap(iris_df.corr(), cmap='Blues', annot = True);
From the above heatmap, we see that petal_length and petal_width have a high correlation, petal_width and sepal_length have good correlation as well as petal_length and sepal_length have good correlations.
cols = ['sepal_width', 'sepal_length', 'petal_width', 'petal_length']plt.figure(figsize=(20,4), dpi=100)i = 1for col in cols:    plt.subplot(1,11,i)    sns.distplot(iris_df[col])    i = i+1plt.tight_layout()plt.show()
We can observe the distribution of our columns through this distplot which shows that follow a normal distribution.
Let's see the distribution of data for the various columns of our data set.
fig, axes = plt.subplots(2, 2, figsize=(16,9))axes[0,0].set_title("Distribution of Sepal Width")axes[0,0].hist(iris_df['sepal_width'], bins=5);axes[0,1].set_title("Distribution of Sepal Length")axes[0,1].hist(iris_df['sepal_length'], bins=7);axes[1,0].set_title("Distribution of Petal Width")axes[1,0].hist(iris_df['petal_width'], bins=5);axes[1,1].set_title("Distribution of Petal Length")axes[1,1].hist(iris_df['petal_length'], bins=6);
The Highest frequency of sepal width is between 3.0 to 3.5 which is around 70.
The Highest frequency of sepal length is between 5.5 and 6.0 which is around 35.
The Highest frequency of petal width is between 0 to 0.5 which is around 50.
The Highest frequency of petal length is between 0 to 0.5 which is around 50.
sns.FacetGrid(iris_df,hue="species",height=5).map(sns.distplot,"petal_width").add_legend();
We see the setosa is easily separable while some portions of Versicolor and virginica are mixed.
sns.FacetGrid(iris_df,hue="species",height=5).map(sns.distplot,"petal_length").add_legend();
Again we see that on the basis of petal length setosa is separable while the other two are still overlapping.
sns.FacetGrid(iris_df,hue="species",height=5).map(sns.distplot,"sepal_length").add_legend();
We see it is quite tough to separate the species on the basis of sepal_length alone.
sns.FacetGrid(iris_df,hue="species",height=5).map(sns.distplot,"sepal_width").add_legend();
While the overlapping of species is more intense in the case of sepal_width.
fig, axes = plt.subplots(2, 2, figsize=(16,9))sns.boxplot(  y="petal_width", x= "species", data=iris_df,  orient='v' , ax=axes[0, 0])sns.boxplot(  y="petal_length", x= "species", data=iris_df,  orient='v' , ax=axes[0, 1])sns.boxplot(  y="sepal_length", x= "species", data=iris_df,  orient='v' , ax=axes[1, 0])sns.boxplot(  y="sepal_length", x= "species", data=iris_df,  orient='v' , ax=axes[1, 1])plt.show()
Further, we see that the box plots describe that the setosa usually has smaller features with few outliers.
The Versicolor species has average features
The virginica species has the longest features widths and lengths as compared to others.
We can further see the distributions using the violin plot on our dataset
fig, axes = plt.subplots(2, 2, figsize=(16,9))sns.violinplot(y="petal_width", x= "species", data=iris_df,  orient='v' , ax=axes[0, 0])sns.violinplot(y="petal_length", x= "species", data=iris_df,  orient='v' , ax=axes[0, 1])sns.violinplot(y="sepal_length", x= "species", data=iris_df,  orient='v' , ax=axes[1, 0])sns.violinplot(y="sepal_length", x= "species", data=iris_df,  orient='v' , ax=axes[1, 1])plt.show()
The kernel density of the data along with the full distribution of the data is shown through the violin plots. We can see the probability density of the various features.
The dataset is balanced i.e. equal records are present for all three species.
We have four numerical columns while just one categorical column which in turn is our target column.
A strong correlation is present between petal width and petal length.
The setosa species is the most easily distinguishable because of its small feature size.
The Versicolor and Virginica species are usually mixed and are sometimes hard to separate, while usually Versicolor has average feature sizes and virginica has larger feature sizes.