Search code examples
pythonpandasiterationhistogramsubplot

How to Iterate over multiple DataFrames and plot histograms for each feature with each data set's feature in the same graph


I have two DataFrames: df1 & df2

df1

Age BsHgt_M BsWgt_Kg    GOAT-MBOAT4_F_BM    TCF7L2_M_BM UCP2_M_BM
23.0    1.84    113.0   -1.623634   0.321379    0.199183
23.0    1.68    113.9   -1.073523   -0.957523   0.549469
24.0    1.60    86.4    -0.270883   -0.004106   1.479865
20.0    1.59    99.2    -0.218071   0.568458    -0.398410

df2
Age BsHgt_M BsWgt_Kg    GOAT-MBOAT4_F_BM    TCF7L2_M_BM UCP2_M_BM
29.0    1.94    123.0   -1.623676   0.321379    0.199183
30.0    1.61    113.9   -1.073523   -0.957523   0.549469
44.0    1.30    56.4    -0.270883   -0.004106   1.479865
30.0    1.19    91.2    -0.218071   0.568458    -0.398410

Here I'm trying to iterate over each column and plot a histogram for each column for df1, this I can do with the below code:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(len(df1.columns), figsize=(10,50))
for n, col in enumerate(df1.columns):
    df1[col].hist(ax=axs[n],legend=True)

But, I have to iterate over two DataFrames and plot histograms in such a way that to see histograms of each feature with each data frame's feature in the same graph, or side-by-side histograms with the same scale is also fine

Desired plot

histogram subplots :

df1['Age'] vs df2['Age']
df1['BsHgt_M'] vs df2['BsHgt_M']
.
.
.

Can anyone enlighten me on how to accomplish this!


Solution

  • IIUC, you could assign a new column named ID to both data frames that could be used for your legend to distinguish between your histograms. Then, you can concatenate your data frames row-wise using pd.concat. After that, you just need to define your axis and figure and iterate over all columns except of the new assigned one and plot a histogram using seaborn while distinguishing between your assigned variable. The implementation of such a distinction is straight-forward in seaborn, just use the argument hue.

    Possible Code:

    import matplotlib.pyplot as plt
    import pandas as pd
    import seaborn as sns
    
    # Note: next time when asking something on SO, please provide data as code like this,
    # it makes it easier for the community to replicate your problem and to help you
    df1 = pd.DataFrame({
        "Age": [23, 23, 24, 20],
        "BsHgt_M": [1.84, 1.68, 1.6, 1.59],
        "BsWgt_Kg": [113, 113.9, 86.4, 99.2],   
        "GOAT-MBOAT4_F_BM": [-1.623634, -1.073523, -0.270883, -0.218071],
        "TCF7L2_M_BM": [0.321379, -0.957523, -0.004106, 0.568458],
        "UCP2_M_BM": [0.199183, 0.549469, 1.479865, -0.398410]
    })
    
    df2 = pd.DataFrame({
        "Age": [29, 30, 44, 30],
        "BsHgt_M": [1.94, 1.61, 1.3, 1.19],
        "BsWgt_Kg": [123, 113.9, 56.4, 91.2],   
        "GOAT-MBOAT4_F_BM": [-1.623676, -1.073523, -0.270883, -0.218071],
        "TCF7L2_M_BM": [0.321379, -0.957523, -0.004106, 0.549469],
        "UCP2_M_BM": [0.199183, 0.5499, 1.479865, -0.398410]
    })
    
    df1["ID"] = "df1"
    df2["ID"] = "df2"
    
    df = pd.concat([df1, df2]).reset_index(drop=True)
    cols = df1.columns[:-1]
    
    assert (cols == df2.columns[:-1]).all()
    
    fig, ax = plt.subplots((len(cols)), figsize=(6, 14), sharex=False)
    for i, col in enumerate(cols):
        sns.histplot(data=df, x=col, hue="ID", ax=ax[i])
        if i > 0: ax[i].legend(list(), frameon=False)
        ax[i].set_ylabel(col)
    sns.move_legend(ax[0], "upper left", bbox_to_anchor=(1, 1))
    ax[-1].set_xlabel("")
    plt.show()
    

    This code plots histograms for all columns.

    For two columns, it would look somewhat like this:

    plot

    If needed, the style and form can easily be adjusted. This is just an example of a possible solution to your problem and should only serve as a guideline.