Gibs
Gibs

Reputation: 43

Editing the labels and position of the axis ticks on a seaborn heatmap results in an empty plot

I am trying to plot a seaborn heatmap with custom locations and labels on both axes. The dataframe looks like this:

Dataframe

I can plot this normally with seaborn.heatmap:

fig, ax = plt.subplots(figsize=(8, 8))
sns.heatmap(genome_freq.applymap(lambda x: np.log10(x+1)),
            ax=ax)
plt.show()

Normal heatmap

I have a list of positions I'd like to set as the xticks (binned_chrom_genome_pos):

[1000000, 248000000, 491000000, 690000000, 881000000, 1062000000, 1233000000, 1392000000, 1538000000, 1679000000, 1814000000, 1948000000, 2081000000, 2195000000, 2301000000, 2402000000, 2490000000, 2569000000, 2645000000, 2709000000, 2772000000, 2819000000, 2868000000, 3023000000]

However, when I try to modify the xticks, the plot becomes empty:

plt.xticks(binned_chrom_genome_pos)

Modified heatmap

I also noticed that the x-axis labels do not correspond to the ticks specified.

Could someone assist me in plotting this properly?

Upvotes: 1

Views: 6782

Answers (1)

warped
warped

Reputation: 9481

why the code does what it does

ax.get_xticks() returns the positions of the ticks. You can see that they are between 0.5 and 3000. These values refer to the index of your data. Large values, set by plt.xticks, or ax.set_xticks are still interpreted as data indices. So, if you have 10 rows of data, and set xticks to [0, 1000], the data in your figure will only occupy 1% of the x-range, hence disappearing. I am not sure if I am making myself clear, so I will give an example with synthetic data:

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

#generating data
dic = {a:np.random.randint(0,1000,100) for a in range(0,1000000, 10000)}
genome_freq = pd.DataFrame(dic, index=range(0,1000000, 10000))

#plotting heatmaps
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))

sns.heatmap(genome_freq.applymap(lambda x: np.log10(x+1)),
            ax=ax1)

sns.heatmap(genome_freq.applymap(lambda x: np.log10(x+1)),
            ax=ax2)


old_ticks = ax2.get_xticks()
print(np.min(old_ticks), np.max(old_ticks), len(old_ticks)) # prints 0.5 99.5 34

ax2.set_xticks([0,300]) # setting xticks with values way larger than your index squishes your data

plt.show()

enter image description here

what can be done to fix it

So, what you want to do, is to change the xticks based on the size of your data, and then overwrite xticklabels:

Given the new labels from your question:

new_labels = [1000000, 248000000, 491000000, 690000000, 881000000, 1062000000, 1233000000, 1392000000, 1538000000, 1679000000, 1814000000, 1948000000, 2081000000, 2195000000, 2301000000, 2402000000, 2490000000, 2569000000, 2645000000, 2709000000, 2772000000, 2819000000, 2868000000, 3023000000]

len(new_labels) # returns 24

fig, ax = plt.subplots(figsize=(4, 4))
sns.heatmap(genome_freq.applymap(lambda x: np.log10(x+1)),
            ax=ax)

So, now we want 24 evenly spaced xticks between the former minimum and the former maximum. We can use np.linspace to achieve that:

old_ticks = ax.get_xticks()
new_ticks = np.linspace(np.min(old_ticks), np.max(old_ticks), len(new_labels))
ax.set_xticks(new_ticks)
ax.set_xticklabels(new_labels)
plt.show()

enter image description here

Upvotes: 3

Related Questions