Iris Dataset Structure and Analysis
En esta página
- 1. Loading the Data (Default: NumPy Arrays)
- Understanding Targets and Features
- 2. NumPy vs Pandas: Understanding the Options
- Why Does Scikit-learn Default to NumPy?
- When to Use NumPy Arrays
- When to Use Pandas DataFrames
- The Typical ML Workflow
- Loading with Pandas for EDA
- Why Pandas Excels at EDA
- The Best Practice: Use Both
- Key Takeaways
- 3. Advanced Visualizations
- Statistical Summary
- Class Distribution
- Visualization
- Boxplots by Species
- Correlation Analysis
- 4. Key Insights from EDA
- Summary of Findings
- Implications for Modeling
This notebook provides a deep dive into the Iris dataset, a classic dataset in machine learning. We will explore its structure as provided by scikit-learn and perform Exploratory Data Analysis (EDA) to understand the data before modeling.
1. Loading the Data (Default: NumPy Arrays)
scikit-learn includes some toy datasets that we can use to test our models. In this case we are going to use the Iris flower dataset. This dataset contains 150 samples of 3 different species of flowers from the Iris genus (50 samples per species) that are classified. For each sample, measurements of sepal and petal length and width have been taken. We therefore have 4 characteristics or features (sepal and petal length and width) and the class (flower species).
from sklearn.datasets import load_iris
iris = load_iris() # Default loading (as_frame=False)
print("Type of iris object:", type(iris))
print("Type of data:", type(iris.data))Scikit-learn's load_iris() function returns a Bunch object, which is essentially a dictionary-like object. It contains keys for the data itself, the target labels, feature names, and a description. By default, the data is stored as NumPy arrays.
print(iris.keys())We can view the full description of the dataset using the DESCR attribute.
print(iris.DESCR)Understanding Targets and Features
The iris.target attribute contains the labels (0, 1, or 2) for each sample. We can map these numbers to names using target_names. Therefore, we have each sample labeled with the class to which it belongs (0 for setosa, 1 for versicolor and 2 for virginica).
print(type(iris.target)) # Flower classesprint(f"Target Names: {iris.target_names}")
print(f"Target Array Shape: {iris.target.shape}")
print("First 10 targets:", iris.target[:10])The data attribute contains the actual measurements. Each row is a sample, and each column is a feature. For each flower, we have:
Features (measurements in cm):
- Sepal length: Length of the sepal (protective leaf)
- Sepal width: Width of the sepal
- Petal length: Length of the petal (colorful part)
- Petal width: Width of the petal
print(f"Feature Names: {iris.feature_names}")
print(f"Data Array Shape: {iris.data.shape}")
print("First 5 data samples:\n", iris.data[:5])Recapping:
iris.datais a two-dimensional ndarray, where each row contains the 4 features of each sample in a 4-element vector. These elements are respectively sepal length, sepal width, petal length and petal width, all in centimeters, as indicated by thefeature_namesattribute.iris.targetis a one-dimensional array containing the class of each sample. Each class is an integer representing a flower species: 0 for setosa, 1 for versicolor and 2 for virginica, as indicated by thetarget_namesattribute.
Knowing this we can, for example, show how the data is classified according to petal length and width, which are the features that correlate best (as indicated in the dataset description).
import matplotlib.pyplot as plt
scatter = plt.scatter(
iris.data[:, 2], # Petal length on the X axis (array of elements from column 2)
iris.data[:, 3], # Petal width on the Y axis
c=iris.target) # Color based on flower classes
plt.xlabel(iris.feature_names[2]) # X axis name (Petal length)
plt.ylabel(iris.feature_names[3]) # Y axis name (Petal width)
plt.legend(scatter.legend_elements()[0], iris.target_names, title="Classes")
plt.title("Iris Type by Petal Width and Length")
plt.show()2. NumPy vs Pandas: Understanding the Options
Scikit-learn offers two ways to load datasets: as NumPy arrays (default) or as Pandas DataFrames (as_frame=True). Understanding when to use each is crucial for efficient machine learning workflows.
Why Does Scikit-learn Default to NumPy?
Scikit-learn uses NumPy arrays as its primary data structure for several important reasons:
-
Performance: NumPy arrays are stored in contiguous memory blocks, making operations significantly faster
- Mathematical operations are optimized at the C level
- Lower memory overhead compared to DataFrames
- Critical for large datasets and production systems
-
Simplicity: Arrays are simpler data structures
- No column names or indices to manage
- Direct numerical operations without overhead
- Easier to implement mathematical algorithms
-
Compatibility: NumPy is the universal standard
- All scientific computing libraries accept NumPy arrays
- Deep learning frameworks (TensorFlow, PyTorch) use array-like structures
- Ensures compatibility across the entire ML ecosystem
-
Training Speed: During model training, features are just numbers
- Column names don't matter during computation
- Models work with numerical matrices, not labeled data
- Removing metadata speeds up iterative algorithms
When to Use NumPy Arrays
✓ Model Training and Prediction:
model.fit(X_train, y_train) # Fast: NumPy arrays
predictions = model.predict(X_test) # Fast: NumPy arrays
✓ Production/Deployment: When performance is critical
✓ Large Datasets: When memory efficiency matters
✓ Deep Learning Pipelines: When integrating with TensorFlow/PyTorch
✓ Mathematical Operations: Matrix operations, linear algebra
When to Use Pandas DataFrames
✓ Exploratory Data Analysis (EDA):
df.describe() # Rich statistical summaries
df.groupby('species') # Easy grouping and aggregation
df['feature'].plot() # Built-in visualization
✓ Data Cleaning: Handling missing values, duplicates, data types
✓ Feature Engineering: Creating, transforming, and selecting features by name
✓ Data Wrangling: Merging, joining, pivoting datasets
✓ Human Readability: When you need to understand and communicate about data
The Typical ML Workflow
A professional workflow often uses both:
1. Load with Pandas → Exploration and understanding
2. Clean with Pandas → Handle missing values, outliers
3. Engineer with Pandas → Create new features by name
4. Convert to NumPy → Train models efficiently
5. Predict with NumPy → Deploy with optimal performanceLoading with Pandas for EDA
Now let's load the dataset as a DataFrame to perform exploratory data analysis. We can always convert back to NumPy arrays later for modeling.
# Load as DataFrame
iris_frame = load_iris(as_frame=True)
# The 'frame' attribute contains the full DataFrame (data + target)
df = iris_frame.frame
df.head()We will also add a target name column with the class names to make our analysis more intuitive.
# Map target integers to target name names
df['target name'] = [iris.target_names[i] for i in df['target']]
df.head()Why Pandas Excels at EDA
Let's compare the effort required to get insights from both data structures.
import numpy as np
print("Task: Calculate mean of each feature")
print("=" * 70)
# NumPy: Requires manual iteration and formatting
print("\nNumPy approach (verbose):")
print("-" * 70)
for i, feature in enumerate(iris.feature_names):
mean = np.mean(iris.data[:, i])
print(f"{feature}: {mean:.3f}")
print("\n" + "=" * 70)
print("\nPandas approach (one-liner):")
print("-" * 70)
print(df[iris.feature_names].mean())
print("\n" + "=" * 70)
print("\nFor comprehensive statistics:")
print("-" * 70)
df.describe().round(2)The Best Practice: Use Both
The most efficient workflow separates concerns:
Phase 1: Exploration (Use Pandas)
df = load_iris(as_frame=True).frame
df.describe() # Quick statistics
df.groupby('species').mean() # Group analysis
df.plot() # Easy visualization
Phase 2: Modeling (Convert to NumPy)
X = df[feature_columns].values # Convert to NumPy
y = df['target'].values
model.fit(X, y) # Train efficiently
Why this works:
- Pandas during exploration: Human-readable, feature-rich
- NumPy during training: Fast, memory-efficient
Key Takeaways
- NumPy is the default for good reasons: Speed, memory, compatibility
- Don't always use Pandas: Overhead matters in production and with large data
- Pandas shines in EDA: Feature names, grouping, statistics, visualization
- Convert when needed: Easy to go between formats (
.valuesoras_frame=True) - Separate concerns: Explore with Pandas, train with NumPy
3. Advanced Visualizations
We have already seen basic statistics. Now let's visualize distributions and relationships.
# We already have our DataFrame 'df' ready from the loading step.
df.head()Statistical Summary
The describe() method gives us count, mean, standard deviation, and quartiles for each feature. This helps us spot differences in scale.
df.describe().round(2)Class Distribution
We verify that the dataset is balanced, which is important for classification accuracy.
df['target name'].value_counts()Visualization
Histograms allow us to see the distribution of each feature. Notice how petal measurements seem to have bimodal distributions, suggesting they might be good at separating species.
import matplotlib.pyplot as plt
import seaborn as sns
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
fig.suptitle('Feature Distributions', fontsize=16)
for idx, feature in enumerate(iris.feature_names):
ax = axes[idx // 2, idx % 2]
sns.histplot(data=df, x=feature, hue='target name', multiple="stack", ax=ax)
ax.set_title(feature)
plt.tight_layout()
plt.show()Pairplots show pairwise relationships. This is excellent for spotting clusters. You can clearly see that Setosa (usually blue) is well-separated from the other two species, especially using Petal Length/Width.
plt.figure(figsize=(10, 8))
sns.pairplot(df, hue='target name', markers=["o", "s", "D"])
plt.show()Boxplots by Species
Boxplots help us see the distribution, median, and outliers for each feature grouped by species.
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
fig.suptitle('Feature Distributions by Species (Boxplots)', fontsize=16)
for idx, feature in enumerate(iris.feature_names):
ax = axes[idx // 2, idx % 2]
df.boxplot(column=feature, by='target name', ax=ax)
ax.set_title(feature)
ax.set_xlabel('')
plt.tight_layout()
plt.show()Key insights from boxplots:
- Setosa has distinctly smaller petal measurements
- Petal features show better separation between species
- Some overlap visible between Versicolor and Virginica
Correlation Analysis
Understanding feature correlations helps identify redundant features and relationships between measurements.
# Calculate correlation matrix
corr = df[iris.feature_names].corr()
plt.figure(figsize=(8, 6))
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0,
square=True, linewidths=1, fmt='.2f')
plt.title('Feature Correlation Matrix', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()Key observations:
- Petal length and width are highly correlated (0.96)
- Sepal width has weak correlation with other features
- High correlation between petal features suggests dimensionality reduction might work
4. Key Insights from EDA
Summary of Findings
From our exploratory analysis, we've discovered:
-
Dataset Characteristics:
- 150 samples, perfectly balanced (50 per class)
- 4 numerical features, all measured in centimeters
- No missing values
-
Feature Separability:
- Setosa is clearly separated from other species (especially by petal measurements)
- Versicolor and Virginica show some overlap
- Petal length and width are the most discriminative features
-
Feature Correlations:
- Petal measurements are highly correlated (0.96)
- Sepal features are less correlated with petal features
- This suggests petal features alone might be sufficient for good classification
-
Scale Differences:
- Features have different ranges (e.g., sepal length: 4.3-7.9, petal width: 0.1-2.5)
- Scaling will be important for distance-based algorithms
Implications for Modeling
Based on this analysis:
✓ Linear models should work well: Clear separation suggests linear boundaries
✓ Feature scaling is necessary: Different ranges require normalization
✓ All classes can be learned: Balanced dataset means no class imbalance issues
✓ Petal features are key: These provide the best discrimination
✓ High accuracy is achievable: Clear patterns suggest >90% accuracy is realistic