Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

Unit 2 — Histograms

Lesson 20 of 46 in the free Introduction to Data Analytics notes on Siksha Sarovar, written by Rohit Jangra.

Histograms

A histogram displays the frequency distribution of a single continuous numeric variable by dividing its range into consecutive, non-overlapping intervals (bins) and drawing a bar whose height is the count of values falling in each bin.

Marks:  45, 52, 55, 58, 61, 63, 65, 67, 68, 70, 72, 75, 78, 82, 88

Bin        Frequency   Bar
40-50          1       █
50-60          3       ███
60-70          6       ██████
70-80          3       ███
80-90          2       ██

Histogram vs Bar Chart — The Classic Exam Question

BasisHistogramBar Chart
Data typeContinuous / numericCategorical / discrete
X-axisNumeric intervals (bins)Distinct categories
BarsTouch each other (no gaps)Separated by gaps
Bar orderCannot be reordered — the scale is continuousCan be reordered (e.g. sorted by height)
Bar widthRepresents the bin interval; meaningfulArbitrary; no meaning
ShowsDistribution/shape of one variableComparison across categories
ExampleDistribution of student marksNumber of students per department

What a Histogram Reveals

  1. Central tendency — where the bulk of the data sits
  2. Spread — how wide the distribution is
  3. Shape/skewness — symmetric, left- or right-skewed
  4. Modality — one peak (unimodal), two (bimodal), many
  5. Outliers — isolated bars far from the main body
  6. Gaps — ranges with no data at all
A bimodal histogram is a signal, not a nuisance — it usually means two distinct populations have been mixed together (e.g. exam marks of a coached batch and an uncoached batch). Split and analyse them separately.

Choosing the Number of Bins

The bin count changes what you see, so it matters:

Sturges' Rule:        k = 1 + 3.322 × log₁₀(n)
Square-root rule:     k = √n
Rice rule:            k = 2 × n^(1/3)

Freedman-Diaconis (bin WIDTH, robust to outliers):
                                2 × IQR
                      width = ───────────
                                 n^(1/3)

Worked example. For n = 100 observations:

Sturges:      k = 1 + 3.322 × log₁₀(100) = 1 + 3.322(2) = 7.64  ≈  8 bins
Square-root:  k = √100 = 10 bins
Rice:         k = 2 × 100^(1/3) = 2 × 4.64 = 9.28  ≈  9 bins
Too few binsToo many bins
Over-smoothed; hides multi-modality and gapsNoisy, spiky; every random fluctuation looks like a feature

Python

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

np.random.seed(42)
marks = np.random.normal(loc=65, scale=12, size=300).clip(0, 100)

fig, axes = plt.subplots(1, 3, figsize=(16, 4))

# Effect of bin count
for ax, bins in zip(axes, [5, 20, 60]):
    ax.hist(marks, bins=bins, color="#168B99", edgecolor="white")
    ax.set_title(f"{bins} bins")
    ax.set_xlabel("Marks")
axes[0].set_ylabel("Frequency")
plt.tight_layout()
plt.show()
# Full-featured histogram with statistical reference lines
fig, ax = plt.subplots(figsize=(9, 5))

n, bins, patches = ax.hist(marks, bins=20, color="#168B99",
                           edgecolor="white", alpha=0.85)

ax.axvline(marks.mean(), color="#ef4444", linestyle="--", linewidth=2,
           label=f"Mean = {marks.mean():.1f}")
ax.axvline(np.median(marks), color="#10b981", linestyle="-", linewidth=2,
           label=f"Median = {np.median(marks):.1f}")

ax.set_title("Distribution of Student Marks (n = 300)", fontsize=13, fontweight="bold")
ax.set_xlabel("Marks")
ax.set_ylabel("Number of Students")
ax.legend()
ax.spines[["top", "right"]].set_visible(False)
plt.tight_layout()
plt.show()

print(f"Mean {marks.mean():.2f} | Median {np.median(marks):.2f} | Skew {pd.Series(marks).skew():.3f}")
# Mean ≈ Median and skew ≈ 0  ->  symmetric distribution
# Histogram + KDE (smoothed density curve) with Seaborn
plt.figure(figsize=(9, 5))
sns.histplot(marks, bins=20, kde=True, color="#168B99", edgecolor="white")
plt.title("Marks Distribution with KDE Overlay")
plt.xlabel("Marks"); plt.ylabel("Count")
plt.show()

# Comparing distributions across a group
tips = sns.load_dataset("tips")
plt.figure(figsize=(9, 5))
sns.histplot(data=tips, x="total_bill", hue="time", bins=25,
             kde=True, alpha=0.6, element="step")
plt.title("Total Bill Distribution — Lunch vs Dinner")
plt.show()
# Skewed data — histogram before and after a log transform
income = np.random.lognormal(mean=10.5, sigma=0.8, size=1000)

fig, axes = plt.subplots(1, 2, figsize=(13, 4))

axes[0].hist(income, bins=40, color="#f59e0b", edgecolor="white")
axes[0].set_title(f"Raw Income — skew = {pd.Series(income).skew():.2f}")

axes[1].hist(np.log(income), bins=40, color="#10b981", edgecolor="white")
axes[1].set_title(f"Log(Income) — skew = {pd.Series(np.log(income)).skew():.2f}")

plt.tight_layout(); plt.show()
# The log transform turns a heavily right-skewed distribution into a near-normal one

Frequency Table — The Histogram in Numbers

freq_table = pd.cut(pd.Series(marks), bins=[0, 40, 50, 60, 70, 80, 90, 100]).value_counts().sort_index()
freq_df = pd.DataFrame({
    "Frequency": freq_table,
    "Relative Freq %": (freq_table / len(marks) * 100).round(2),
    "Cumulative Freq": freq_table.cumsum(),
})
print(freq_df)
#            Frequency  Relative Freq %  Cumulative Freq
# (0, 40]           10             3.33               10
# (40, 50]          28             9.33               38
# (50, 60]          64            21.33              102
# (60, 70]          98            32.67              200
# (70, 80]          70            23.33              270
# (80, 90]          27             9.00              297
# (90, 100]          3             1.00              300

Related Chart Types

ChartDifference from a histogram
Frequency polygonLine joining the mid-points of the bar tops; good for comparing several distributions
Ogive (cumulative frequency curve)Plots cumulative frequency; used to read medians and quartiles graphically
KDE / density plotSmooth continuous curve instead of discrete bars; no bin-width artefacts
Stem-and-leaf plotText-based; preserves the actual data values
Violin plotMirrored KDE, usually compared across groups

The histogram shows the full shape of a distribution. The next chart — the box plot — sacrifices shape detail in exchange for a compact five-number summary that makes group comparison and outlier detection trivial.