A Comprehensive Guide to Data Exploration in Python for Machine Learning
Data exploration is the foundation of any successful machine learning project. While it‘s tempting to jump straight into building models, taking the time to thoroughly understand your data upfront will save you time and frustration later on.
In fact, data scientists spend an estimated 60-80% of their time on data preparation tasks like exploration, cleaning and preprocessing [1]. That means if you‘re not investing heavily in data exploration, you‘re likely not setting yourself up for modeling success.
In this comprehensive guide, we‘ll dive deep into the key steps of data exploration in Python. We‘ll move beyond the basics to cover advanced techniques and important considerations for different types of data and machine learning tasks.
Whether you‘re a beginner looking to build your skills or an experienced practitioner looking to level up your exploration game, this guide has something for you. Let‘s get started!
Why Data Exploration is Essential
Data exploration is the process of understanding the structure, characteristics and relationships within your data before embarking on modeling. It helps you answer critical questions like:
- What does my data represent? What are the entities and attributes?
- How is each variable distributed? Are there outliers or missing values?
- How are variables related to each other? Which seem most predictive of the target?
- Are there data quality issues or inconsistencies that need to be addressed?
- How should I select and engineer features for modeling?
Thoroughly exploring your data upfront helps you avoid costly issues down the line, like realizing too late that you‘re missing a critical variable or that your model‘s poor performance is due to a data leak.
Data exploration is also essential for informing your modeling choices. The insights you glean will guide your feature selection, choice of algorithms, and setting of evaluation metrics. Without investing in exploration, you‘re flying blind.
Step 1: Import Libraries and Load Data
The first step to exploring your data is getting it into Python. We‘ll use the Pandas library to load our data into a DataFrame, the core data structure for data analysis in Python.
We‘ll also import Numpy for numerical computing, Matplotlib and Seaborn for data visualization, and a few other helpful libraries.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
df = pd.read_csv(‘data.csv‘)
Step 2: Understand Data Structure and Types
Once we‘ve loaded our data, we need to understand its structure and contents. We can use a few simple Pandas functions to summarize our DataFrame:
df.head() # preview the first 5 rows
df.info() # see column names, non-null counts and data types
df.describe() # see count, mean, min, max, quartiles for numeric columns
The head() method lets us preview our data, info() shows the column names, data types and missing values, and describe() provides summary statistics for numeric columns.
Pay close attention to the data types of each column. Are dates and times properly formatted? Are numeric variables stored as integers or floats? Are categorical variables stored as strings or numeric codes?
Understanding your data types is crucial because it informs how you analyze and visualize each variable. It also determines what preprocessing steps you‘ll need to take before modeling.
For example, if you have a datetime column stored as a string, you‘ll need to convert it to a proper datetime type to enable time-based analysis and feature engineering:
df[‘date‘] = pd.to_datetime(df[‘date‘])
df[‘day_of_week‘] = df[‘date‘].dt.day_name()
Step 3: Identify Missing Data
Real-world data is messy, and missing values are common. Missing data can lead to issues like bias in your analysis or models that silently fail, so it‘s essential to identify and address missing values early.
To check for missing values in each column:
df.isnull().sum()
This returns the number of missing values in each column. If variables have a high percentage of missing values (say >50%), you may consider excluding them from analysis.
For columns with a lower percentage of missing values, you have a few options:
- Remove observations with missing values (if only a few):
df = df.dropna()
- Impute missing values with a meaningful value like the mean or median:
imputer = SimpleImputer(strategy=‘mean‘)
df[[‘col_with_missings‘]] = imputer.fit_transform(df[[‘col_with_missings‘]])
- Impute with a constant that indicates missingness (e.g. ‘Unknown‘):
df[‘col‘] = df[‘col‘].fillna(‘Unknown‘)
The approach you choose depends on your tolerance for information loss and your understanding of why the data may be missing.
Step 4: Explore Distributions of Variables
Next, we want to understand how each variable in our dataset is distributed. This informs how we handle the variables in modeling and helps us identify issues like outliers.
For numeric variables, we can plot histograms to see the distribution of values:
fig, ax = plt.subplots(figsize=(10,6))
df[‘num_var‘].hist(ax=ax, bins=30)
ax.set_xlabel(‘Numeric Variable‘)
ax.set_ylabel(‘Frequency‘)
plt.show()
We can also calculate summary statistics to understand the central tendency and spread:
df[‘num_var‘].describe()
For categorical variables, we can use bar plots to see the frequency of each category:
fig, ax = plt.subplots(figsize=(10,6))
df[‘cat_var‘].value_counts().plot.bar(ax=ax)
ax.set_xlabel(‘Category‘)
ax.set_ylabel(‘Frequency‘)
plt.show()
The value_counts() method shows the number of occurrences of each unique value in a Series.
It‘s also a good idea to check for outliers – data points that are very different from the rest. Outliers can be errors in data collection or genuinely unusual observations. Either way, they can have a big impact on your analysis.
Box plots are a great way to visualize outliers:
fig, ax = plt.subplots(figsize=(10,6))
sns.boxplot(x=df[‘num_var‘], ax=ax)
ax.set_xlabel(‘Numeric Variable‘)
plt.show()
Outliers are points that fall outside the "whiskers" of the box plot. If you identify outliers, you can decide to remove them, cap them at a certain value, or leave them be, depending on your domain knowledge and modeling goals.
Step 5: Explore Relationships Between Variables
In addition to understanding each variable individually, we also want to know how variables relate to each other. This is especially important for feature selection – we want to identify the variables that are most predictive of our target.
For two numeric variables, scatter plots can show the relationship:
fig, ax = plt.subplots(figsize=(10,6))
sns.scatterplot(x=‘num_var1‘, y=‘num_var2‘, data=df, ax=ax)
ax.set_xlabel(‘Numeric Variable 1‘)
ax.set_ylabel(‘Numeric Variable 2‘)
plt.show()
We can also calculate the correlation coefficient to quantify the strength of the linear relationship:
df[[‘num_var1‘, ‘num_var2‘]].corr()
For a categorical and a numeric variable, box plots or violin plots can show the distribution of the numeric variable for each category:
fig, ax = plt.subplots(figsize=(10,6))
sns.boxplot(x=‘cat_var‘, y=‘num_var‘, data=df, ax=ax)
ax.set_xlabel(‘Categorical Variable‘)
ax.set_ylabel(‘Numeric Variable‘)
plt.show()
To explore relationships between many variables at once, we can use a pairs plot:
sns.pairplot(df[[‘num_var1‘, ‘num_var2‘, ‘num_var3‘, ‘target‘]], diag_kind=‘kde‘)
plt.show()
This creates a grid of scatter plots for each pair of variables, with univariate distributions on the diagonal.
As we explore relationships, we want to keep our modeling goal in mind. For supervised learning tasks, we‘re particularly interested in how predictors relate to the target variable. Predictors that show a clear relationship with the target are good candidates for inclusion in the model.
Step 6: Explore Different Data Types
So far, we‘ve focused on exploring numeric and categorical variables. But real-world datasets often contain other types of data, like text, images, and time series. Each type requires its own set of exploration techniques.
For text data, we might start by looking at the distribution of text lengths:
df[‘text_length‘] = df[‘text‘].apply(len)
df[‘text_length‘].hist(bins=50)
We can also look at word frequencies to get a sense of the content:
from collections import defaultdict
word_counts = defaultdict(int)
for text in df[‘text‘]:
for word in text.split():
word_counts[word] += 1
pd.Series(word_counts).sort_values(ascending=False).head(10)
For image data, we can display a sample of images to get a sense of the content and resolution:
fig, axes = plt.subplots(nrows=3, ncols=3, figsize=(10,10))
for i, ax in enumerate(axes.flatten()):
img = plt.imread(df[‘image_path‘][i])
ax.imshow(img)
ax.axis(‘off‘)
plt.tight_layout()
plt.show()
We might also want to check the distribution of image sizes and color channels.
For time series data, we can plot the variable of interest over time to check for trends, seasonality and outliers:
fig, ax = plt.subplots(figsize=(12,6))
df.plot(x=‘timestamp‘, y=‘value‘, ax=ax)
ax.set_xlabel(‘Time‘)
ax.set_ylabel(‘Value‘)
plt.show()
We can also calculate rolling statistics to smooth out noise and highlight trends:
df[‘rolling_mean‘] = df[‘value‘].rolling(window=30).mean()
fig, ax = plt.subplots(figsize=(12,6))
df[[‘value‘, ‘rolling_mean‘]].plot(x=‘timestamp‘, ax=ax)
ax.set_xlabel(‘Time‘)
plt.show()
The key with exploring different data types is to think about the unique properties of the data and what you‘re trying to learn from it. The exploration techniques will vary, but the goal is always to better understand your data.
Step 7: Explore for Different Learning Tasks
The way we explore our data will also depend on our machine learning task. Is it a supervised learning problem where we‘re trying to predict a target variable? Or an unsupervised problem where we‘re trying to discover inherent structure in the data?
For supervised learning, our exploration will focus heavily on understanding the relationship between predictors and the target. We‘ll look at how the distribution of the target varies across different predictor values, and we‘ll prioritize predictors that show a strong relationship with the target.
For unsupervised learning tasks like clustering or anomaly detection, our exploration will focus more on understanding the overall structure and variability of the data. We‘ll look for natural groupings in the data and variables that account for the most variance.
Dimensionality reduction techniques like Principal Component Analysis (PCA) can be helpful for visualizing structure in high-dimensional data:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
components = pca.fit_transform(df[[‘var1‘, ‘var2‘, ‘var3‘]])
fig, ax = plt.subplots(figsize=(10,6))
sns.scatterplot(x=components[:,0], y=components[:,1], ax=ax)
ax.set_xlabel(‘Principal Component 1‘)
ax.set_ylabel(‘Principal Component 2‘)
plt.show()
PCA projects the data onto a lower-dimensional space while preserving as much variability as possible. Plotting the first two principal components can reveal interesting structure or separability in the data.
Step 8: Bring in Domain Expertise
While the techniques we‘ve covered are a great starting point for exploring any dataset, it‘s important to remember that data exploration is not a purely mechanical process. Your domain knowledge and understanding of the business context are critical for guiding your exploration and interpreting your findings.
For example, consider a dataset of customer transactions for an e-commerce company. Your exploration might reveal that customers who buy product A are much more likely to also buy product B. But it takes domain expertise to recognize that A and B are frequently bought together because they‘re complementary (like a phone case and screen protector) and not because of an inherent customer preference.
As another example, suppose you‘re analyzing data from a medical study and you notice that patients over 65 have much higher rates of a certain disease. A naive interpretation might be that age causes the disease. But a domain expert would know to check for confounding factors like whether older patients are more likely to be screened for the disease in the first place.
The key is to let your exploration be guided by your understanding of the domain. As you spot interesting patterns, think critically about what they might mean and don‘t be afraid to dig deeper.
Conclusion
Data exploration is a critical but often underestimated step in the machine learning process. By taking the time to thoroughly understand your data, you set yourself up for success in modeling and avoid costly mistakes down the line.
In this guide, we‘ve covered a variety of techniques for exploring different types of data in Python, including:
- Understanding data structure and types
- Identifying and handling missing data
- Visualizing distributions of individual variables
- Exploring relationships between variables
- Handling different data types like text, images and time series
- Tailoring exploration to different machine learning tasks
- Incorporating domain expertise
But data exploration is as much an art as it is a science. As you build your skills, you‘ll develop your own workflow and intuition for uncovering insights in data.
The most important thing is to be curious and thorough in your exploration. Ask lots of questions, visualize everything, and don‘t hesitate to dive deep when you spot something interesting.
Happy exploring!
References
- Data Scientists Spend Most of Their Time Cleaning Data
- Python for Data Analysis by Wes McKinney
- Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow by Aurelien Geron
- Feature Engineering and Selection: A Practical Approach for Predictive Models by Max Kuhn and Kjell Johnson
- Exploratory Data Analysis from the NIST/SEMATECH e-Handbook of Statistical Methods