Search code examples
matplotlibseaborn

Setting xtick formatter on seaborn catplot changes the value of the x ticks to the index


I'm trying to visualize a simple order book dataframe with a seaborn catplot. However, when I set the format of the x tick labels of the ax of the catplot, it changes the values of the labels to being the index (0-10 instead of 9.95-10.05). I've tried using a .barplot() directly and the same result happens. Is this something in how seaborn creates bar charts? I have never had this issue with line charts.

book = pd.DataFrame({'price': np.arange(10.05, 9.95, -0.01), 'type': '?', 'qty': 0})
book.iloc[1, 2] = 1800
book.iloc[2, 2] = 500
book.iloc[3, 2] = 500
book.iloc[4, 2] = 100
book.iloc[0:6, 1] = 'Ask'

book.iloc[6, 2] = 100
book.iloc[7, 2] = 200
book.iloc[8, 2] = 600
book.iloc[9, 2] = 2500
book.iloc[6:11, 1] = 'Bid'

cat = sns.catplot(x='price', y='qty', data=book, hue='type', kind='bar') 
cat.tick_params(axis='x', rotation=45)

plot


cat.ax.xaxis.set_major_formatter(ticker.FuncFormatter(lambda x, pos: f'{x:.2f}'))

enter image description here


Solution

  • Python seaborn categorical barplot with custom formatted x-axis tick labels

    You need to convert from the integer x-axis indices back to the corresponding price values in the dataframe.

    For example, like so:

    import matplotlib.pyplot as plt
    import matplotlib.ticker as ticker
    import numpy as np
    import pandas as pd
    import seaborn as sns
    
    
    book = pd.DataFrame(
        {"price": np.arange(9.95, 10.05, 0.01), "type": "?", "qty": 0}
    )
    book.iloc[1, 2] = 1800
    book.iloc[2, 2] = 500
    book.iloc[3, 2] = 500
    book.iloc[4, 2] = 100
    book.iloc[0:6, 1] = "Ask"
    
    book.iloc[6, 2] = 100
    book.iloc[7, 2] = 200
    book.iloc[8, 2] = 600
    book.iloc[9, 2] = 2500
    book.iloc[6:11, 1] = "Bid"
    
    
    fig, ax = plt.subplots(figsize=(6, 4), dpi=150)
    sns.barplot(
        x="price",
        y="qty",
        data=book,
        hue="type",
        ax=ax,
        order=book["price"][::-1],
        dodge=False,
    )
    
    ax.set_xticks(range(len(book["price"])))
    ax.set_xticklabels(book["price"].round(2), rotation=45)
    
    ax.xaxis.set_major_formatter(
        ticker.FuncFormatter(lambda x, pos: f'${book["price"].iloc[x]:.2f}')
    )
    
    plt.tight_layout()
    plt.show()
    

    Python seaborn categorical barplot with custom formatted x-axis tick labels

    Note: I'm not 100% sure if there is an error in the sample code you provide or not, but, oddly, it was necessary to change the prices range (defined with np.arange) and yet then reverse the order of the plotted data to obtain a plot that matched the expected output provided and shown in question.