Pandas is highly efficient at data analysis and manipulation tasks. It provides numerous functions and methods to operate on tabular data seamlessly. However, all we see is plain numbers in tabular form.
What if we integrate a few visual components into Pandas dataframes? I think it makes them look more appealing and informative in many cases.
We can achieve this by using Style property of pandas dataframes. Style property returns a styler object which provides many options for formatting and displaying dataframes. A styler object is basically a dataframe with some style. In this article, we will go through 10 examples to master how styling works.
We will use a customer churn dataset which is available on Kaggle and also create some sample dataframes.
churn = pd.read_csv(
"/home/soner/Downloads/datasets/BankChurners.csv",
usecols=np.arange(1,10))
churn.head()

The screenshot above shows only a part of the dataframe. The dataset contains relevant information about the customers of bank and whether they churned (i.e. left the bank).
Example 1
Consider a case where we want to see the average customer age for each category in the education level column. This task can be done using the group by function.
We can add some styling on what group by function returns. For instance, we can highlight the minimum value.
churn[['Education_Level','Months_on_book']].
groupby(['Education_Level'], as_index=False).mean().
style.highlight_min(color='red')

We apply the functions together with the style property of Pandas. The main syntax is as follows:
pandas.DataFrame.style
In this example, we have used one of the built-in styling functions which is highlight_min. There are other built-in functions as we will see in the following examples.
Example 2
We can apply multiple styling functions by chaining them together. For instance, it is possible to highlight both minimum and maximum values.
churn[['Education_Level','Customer_Age']].
groupby(['Education_Level'], as_index=False).mean().
style.highlight_min(color='red').highlight_max(color='blue')

Example 3
The functions in the first two examples highlight the maximum and minimum values of columns. We can also use to highlight values row-wise. It does not make sense for the previous cases because there is only one column.
Let’s create a sample dataframe with multiple columns and apply these styling functions.
df = pd.DataFrame(np.random.randint(100, size=(6,8)))
df.style.highlight_min(color='red',axis=1)
.highlight_max(color='green', axis=1)

The highlighted values are the maximum and minimum values of rows.
Example 4
Another built-in styling function is the bar function. It displays a colored bar in each cell whose length is proportional to the value in that cell.
churn[['Attrition_Flag','Gender','Customer_Age']].
groupby(['Attrition_Flag','Gender'], as_index=False).mean().
style.bar(color='green')

We have calculated the average customer age for each group in attrition flag and gender columns. The bar function provides us a visual overview of the values. We can easily realize the minimum and maximum values as well as the order of the values in between.
Example 4
In this example, we will see an extended use of the bar function. Consider a case where we have both positive and negative values in columns. We can set 0 as reference point and use bars with different colors for negative and positive values.
df = pd.DataFrame((np.random.randint(20, size=(6,3)) - 8) * 3.2)
df.style.bar(align='mid', color=['red','green'])

It makes it easy to visually differentiate positive and negative values.
Example 5
Up to this point, we have used the built-in styling functions. It is possible to use our own functions. Once we create our own styler, we can apply it using the apply or applymap functions of Pandas.
For instance, the function below highlights the values of a column that are higher than the column average.
def above_mean(col):
is_above = col > col.mean()
return ['background-color: green' if v else '' for v in is_above]
We use the apply function to do column-wise styling.
churn[['Marital_Status','Gender','Customer_Age',
'Dependent_count']].groupby(['Marital_Status','Gender']).mean().
style.apply(above_mean)

We have calculated the average value for each category in the marital status and gender columns. The above_mean function is applied to the results to highlight the values that are higher than the average value of the column.
Example 7
It is possible to apply the styling only for some of the columns. The subset parameter is used to select the desired columns.
For instance, the following code will only apply the above_mean function to the customer age column.
churn[['Marital_Status','Gender','Customer_Age',
'Dependent_count']].groupby(['Marital_Status','Gender']).mean().
style.apply(above_mean, subset=['Customer_Age'])

We pass the list of columns that we want to style to the subset parameter of the apply function.
Example 8
The apply function is used to do column-wise styling. If we want to do element-wise styling, the applymap function is used.
For instance, the above_zero function below colors positive and negative values in a dataframe differently.
def above_zero(val):
color = 'green' if val > 0 else 'red'
return 'color: %s' % color
We can use the applymap function to do element-wise styling with the above_zero function.
df.style.applymap(above_zero)

Example 9
The set_properties function of the Styler attribute allows for combining different styling operations.
For instance, we can choose specific colors for the background and the characters.
df = pd.DataFrame(np.random.randint(100, size=(6,8)))
df.style.set_properties(**{'background-color': 'yellow',
'color':'black'})

Example 10
The style functions we used here are pretty simple ones. However, we can also create more complex style functions that enhance the informative power.
We may want to use the same styling for multiple times. Pandas offers a way to transfer styles between dataframes.
A styler object is returned when we apply the style function. We can save this styler object in a variable and then use it to transfer the style.
df = pd.DataFrame(np.random.randint(100, size=(6,8)) - 50)
df.iloc[-1,-1] = np.nan
style1 = df.style.highlight_min(color='red')
.highlight_max(color='blue')
.highlight_null(null_color='green')
The variable style1 is a styler object which is basically a dataframe with style. Here is how it looks:

Let’s create another styler object based on a different dataframe.
df2 = pd.DataFrame(np.random.randint(50, size=(6,8)))
df2.iloc[-1,-1] = np.nan
style2 = df2.style
Style2 is a styler object that looks as below:

We can now transfer the style of the style1 object to the style2 object.
style2.use(style1.export())
Here is how the style2 object looks now:

Conclusion
We have seen how to use the built-in style function as well as creating a custom-made one.
We have also used the apply and applymap functions to actually apply the custom-made styles on the dataframes. We have also seen how to transfer styles from one styler object to another.
There are other styling and formatting options available that can be accessed on the styling section of pandas user guide.
Thank you for reading. Please let me know if you have any feedback.