Introduction to Matplotlib 🎨Matplotlib का परिचय 🎨Matplotlib 简介 🎨
Matplotlib is Python's base plotting library. seaborn and pandas .plot() are built on top of it. When you learn matplotlib directly, you can control every part of a chart: each line, label, tick and colour.
Matplotlib, Python की आधार plotting library है। seaborn और pandas .plot() इसी के ऊपर बने हैं। Matplotlib सीधे सीखने पर आप chart के हर हिस्से को नियंत्रित कर सकते हैं: हर line, label, tick और रंग।
Matplotlib 是 Python 的基础绘图库,seaborn 和 pandas .plot() 都构建在它之上。直接学习 matplotlib,你就能控制图表的每个部分:每条线、每个标签、每个刻度和每种颜色。
For a quick look during EDA, seaborn or plotly are often faster. Use matplotlib directly when you need exact control: custom tick formats, two y-axes, shared axes, inset charts, exact font sizes for a journal, or a layout the higher-level tools cannot make. Even when you use seaborn, you will still use matplotlib to fine-tune the result.
EDA के दौरान जल्दी देखने के लिए seaborn या plotly अक्सर तेज़ हैं। Matplotlib सीधे तब इस्तेमाल करें जब सटीक नियंत्रण चाहिए: custom tick formats, दो y-axes, shared axes, inset charts, journal के लिए सटीक font sizes, या ऐसा layout जो ऊँचे स्तर के tools नहीं बना सकते। Seaborn इस्तेमाल करते समय भी नतीजे को सुधारने के लिए matplotlib काम आता है।
在 EDA 中快速查看时,seaborn 或 plotly 往往更快。需要精确控制时直接用 matplotlib:自定义刻度格式、双 y 轴、共享坐标轴、嵌入小图、期刊要求的精确字号,或高层工具做不出的布局。即使用 seaborn,你仍要用 matplotlib 来微调结果。
The Object Tree: Figure → Axes → ArtistsObject Tree: Figure → Axes → Artists对象树:Figure → Axes → Artists
Everything you see is an object you can change. The Figure is the canvas. Each Axes is one plot inside it. Lines, bars, text and ticks are artists that live inside an Axes.
जो कुछ दिखता है वह एक object है जिसे आप बदल सकते हैं। Figure canvas है। हर Axes उसके अंदर एक plot है। Lines, bars, text और ticks artists हैं जो Axes के अंदर रहते हैं।
你看到的一切都是可以修改的对象。Figure 是画布,每个 Axes 是其中的一个图。线、柱、文字和刻度都是位于 Axes 内部的 artist。
Note the spelling: "Axes" is one plot area. It is different from an "Axis" (the x or y ruler inside it).
वर्तनी पर ध्यान दें: "Axes" एक plot area है। यह "Axis" (उसके अंदर का x या y पैमाना) से अलग है।
注意拼写:“Axes”是一个绘图区,与“Axis”(其中的 x 或 y 轴标尺)不同。
Click a part name. It is highlighted on the chart and the code that controls it appears below. Or press Tour.
किसी हिस्से के नाम पर click करें। वह chart पर highlight होगा और उसे नियंत्रित करने वाला code नीचे दिखेगा। या Tour दबाएँ।
点击一个部分的名字,它会在图上高亮,下方显示控制它的代码。也可以点“导览”。
Setup and ImportsSetup और Imports准备与导入
The code blocks in this tutorial run one after another in the same notebook, so later blocks reuse variables from earlier ones. They use the two practice files customers.csv and daily_sales.csv — Section 10 shows how to make them.
इस tutorial के code blocks एक ही notebook में एक के बाद एक चलते हैं, इसलिए बाद वाले blocks पहले के variables दोबारा इस्तेमाल करते हैं। ये दो practice files customers.csv और daily_sales.csv इस्तेमाल करते हैं — Section 10 में इन्हें बनाने का तरीक़ा है।
本教程的代码块在同一个笔记本中依次运行,后面的代码块会复用前面的变量。它们使用两个练习文件 customers.csv 和 daily_sales.csv —— 第 10 节介绍如何生成。
# 1) Setup: imports, a style sheet and global defaults (rcParams)
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np, pandas as pd
plt.style.use("seaborn-v0_8-whitegrid") # one line changes the whole look
plt.rcParams.update({
"font.family": "DejaVu Sans", # has the ₹ glyph; Arial/Liberation do not
"figure.figsize": (10, 5), "figure.dpi": 120,
"axes.titlesize": 14, "axes.labelsize": 12,
"xtick.labelsize": 10, "ytick.labelsize": 10,
"legend.fontsize": 10, "lines.linewidth": 2,
"axes.spines.top": False, "axes.spines.right": False,
})
df = pd.read_csv("customers.csv")
daily = pd.read_csv("daily_sales.csv", parse_dates=["date"])
print("matplotlib", plt.matplotlib.__version__, "| styles available:", len(plt.style.available))
print("customers:", df.shape, "| days:", len(daily))
Many style sheets (including the seaborn ones) ask for Arial or Liberation Sans first. These fonts have no ₹ glyph, so matplotlib warns "Glyph 8377 missing from font(s)" and draws an empty box. Setting "font.family": "DejaVu Sans" fixes it — that is why it is in the setup above.
कई style sheets (seaborn वाली भी) पहले Arial या Liberation Sans माँगती हैं। इन fonts में ₹ glyph नहीं है, इसलिए matplotlib चेतावनी देता है "Glyph 8377 missing from font(s)" और ख़ाली box बनाता है। "font.family": "DejaVu Sans" रखने से यह ठीक हो जाता है — इसीलिए यह ऊपर के setup में है।
许多样式表(包括 seaborn 系列)会优先使用 Arial 或 Liberation Sans。这些字体没有 ₹ 字形,所以 matplotlib 会警告 “Glyph 8377 missing from font(s)” 并画出空方框。设置 "font.family": "DejaVu Sans" 即可解决 —— 这就是上面设置里有它的原因。
plt.plot() draws on the "current" Axes that pyplot keeps track of. It is fine for a quick one-off chart. With fig, ax = plt.subplots() you hold the objects yourself and call ax.plot(), ax.set_title() and so on. This is clearer as soon as you have more than one panel, and it is what the matplotlib docs recommend for anything non-trivial.
plt.plot() उस "current" Axes पर बनाता है जिसका pyplot हिसाब रखता है। जल्दी के एक chart के लिए यह ठीक है। fig, ax = plt.subplots() से objects आपके हाथ में रहते हैं और आप ax.plot(), ax.set_title() आदि बुलाते हैं। एक से ज़्यादा panel होते ही यह ज़्यादा साफ़ है, और matplotlib docs भी ज़रा-से भी जटिल काम के लिए यही सुझाते हैं।
plt.plot() 会画在 pyplot 记录的“当前” Axes 上,适合快速画一张图。使用 fig, ax = plt.subplots() 时,对象握在你手里,你调用 ax.plot()、ax.set_title() 等。一旦有多个面板,这种方式更清晰,matplotlib 官方文档也推荐稍复杂的图都这样写。
Line Chart — Trends Over Time 📈Line Chart — समय के साथ Trends 📈折线图 —— 随时间的趋势 📈
A line chart joins points in order. Use it for time series and anything where order matters. Colour, width, style and markers can each be set on their own.
Line chart points को क्रम में जोड़ता है। Time series और जहाँ भी क्रम मायने रखे, वहाँ इसे इस्तेमाल करें। रंग, चौड़ाई, style और markers हर एक अलग से तय किए जा सकते हैं।
折线图按顺序连接各点,适用于时间序列以及任何顺序重要的数据。颜色、线宽、线型和标记都可以分别设置。
# 2) Line chart: monthly revenue vs target, shaded where revenue beats target
r25 = daily[daily["date"].dt.year == 2025]
months = r25["date"].dt.strftime("%b").unique()
revenue = r25.groupby(r25["date"].dt.month)["revenue"].median().to_numpy() # median day: robust to the bad record
target = np.linspace(52, 63, 12) # the sales plan
fig, ax = plt.subplots(figsize=(10, 5), layout="constrained")
ax.plot(months, revenue, color="#3b82f6", marker="o", markersize=5, label="Revenue (median day)")
ax.plot(months, target, color="#f59e0b", linewidth=1.5, linestyle="--", label="Target")
ax.fill_between(months, revenue, target, where=revenue > target, interpolate=True,
alpha=0.2, color="#16a34a", label="Above target")
ax.set_title("2025: Revenue vs Target", fontweight="bold")
ax.set_xlabel("Month"); ax.set_ylabel("Revenue per day (₹ thousand)")
ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda v, _: f"₹{v:.0f}k"))
ax.legend(framealpha=0.3)
fig.savefig("line.png")
print("revenue:", revenue.round(1))
print("months above target:", list(months[revenue > target]))
The green area appears only where revenue is above target. interpolate=True makes the shading meet the exact crossing points instead of stopping at the nearest month.
हरा हिस्सा सिर्फ़ वहाँ है जहाँ revenue target से ऊपर है। interpolate=True से shading सटीक crossing points तक जाती है, नज़दीकी महीने पर नहीं रुकती।
绿色区域只出现在收入高于目标的地方。interpolate=True 让阴影延伸到精确的交叉点,而不是停在最近的月份。
The daily data has one bad record (₹269k on 1 Feb 2025). A monthly mean would make February look like a great month. The median day ignores that one value. Small choices like this change what a chart says.
Daily data में एक ग़लत record है (1 फ़रवरी 2025 को ₹269k)। महीने का mean फ़रवरी को बहुत अच्छा महीना दिखाएगा। Median दिन उस एक मान को नज़रअंदाज़ करता है। ऐसे छोटे फ़ैसले chart का संदेश बदल देते हैं।
日数据里有一条错误记录(2025 年 2 月 1 日 ₹269k)。按月取均值会让 2 月看起来业绩很好,而取中位数日会忽略这个值。这类小选择会改变图表传达的信息。
"steelblue"), a hex code ("#3b82f6"), an RGB tuple, or "C0"–"C9" for the style's colour cycle."steelblue"), hex code ("#3b82f6"), RGB tuple, या style के colour cycle के लिए "C0"–"C9"।"steelblue")、十六进制("#3b82f6")、RGB 元组,或用 "C0"–"C9" 引用样式的颜色循环。"-" solid, "--" dashed, "-." dash-dot, ":" dotted, or a custom tuple like (0, (5, 2, 1, 2))."-" ठोस, "--" dashed, "-." dash-dot, ":" dotted, या custom tuple जैसे (0, (5, 2, 1, 2))।"-" 实线,"--" 虚线,"-." 点划线,":" 点线,或自定义元组如 (0, (5, 2, 1, 2))。"o" circle, "s" square, "^" triangle, "D" diamond, "x", "+". Size with markersize=."o" वृत्त, "s" वर्ग, "^" त्रिभुज, "D" हीरा, "x", "+"। आकार markersize= से।"o" 圆,"s" 方块,"^" 三角,"D" 菱形,"x","+"。大小用 markersize= 设置。ax.fill_between(x, y1, y2, where=mask, interpolate=True, alpha=0.2) shades the area between two curves.ax.fill_between(x, y1, y2, where=mask, interpolate=True, alpha=0.2) दो curves के बीच का हिस्सा भरता है।ax.fill_between(x, y1, y2, where=mask, interpolate=True, alpha=0.2) 填充两条曲线之间的区域。Pick a line style, marker and width. The chart redraws and the code below always matches it.
Line style, marker और width चुनें। Chart दोबारा बनता है और नीचे का code हमेशा उससे मेल खाता है।
选择线型、标记和线宽。图会重新绘制,下方代码始终与之对应。
Bar Charts — Comparing Categories 📊Bar Charts — Categories की तुलना 📊条形图 —— 比较类别 📊
Bar charts compare one number across categories. matplotlib can draw vertical, horizontal, grouped and stacked bars. For grouped bars, shift each group by half a bar width.
Bar charts एक संख्या की categories के बीच तुलना करते हैं। Matplotlib vertical, horizontal, grouped और stacked bars बना सकता है। Grouped bars के लिए हर group को आधी bar चौड़ाई से खिसकाएँ।
条形图用来比较不同类别的同一个数值。matplotlib 可以画竖直、水平、分组和堆叠条形图。画分组条形图时,把每组向左右移动半个柱宽。
# 3) Grouped bars (two age groups) and a sorted horizontal bar chart
df["age_group"] = np.where(df["age"] < 40, "Under 40", "40 and over")
avg = df.pivot_table(index="product_category", columns="age_group",
values="purchase_amount", aggfunc="mean") / 1000 # ₹ thousand
categories = avg.index.to_numpy()
total = df.groupby("product_category")["purchase_amount"].sum() / 1e6 # ₹ million
fig, axes = plt.subplots(1, 2, figsize=(12, 5), layout="constrained")
x = np.arange(len(categories)); width = 0.38
b1 = axes[0].bar(x - width / 2, avg["Under 40"], width, label="Under 40", color="#60a5fa")
b2 = axes[0].bar(x + width / 2, avg["40 and over"], width, label="40 and over", color="#f87171")
axes[0].bar_label(b1, fmt="%.1f", padding=3, fontsize=9)
axes[0].bar_label(b2, fmt="%.1f", padding=3, fontsize=9)
axes[0].set_xticks(x, categories, rotation=30, ha="right") # labels in one call (mpl ≥ 3.5)
axes[0].set_ylabel("Average spend (₹ thousand)"); axes[0].legend()
order = total.sort_values()
axes[1].barh(order.index, order.to_numpy(), color="#f59e0b")
axes[1].bar_label(axes[1].containers[0], fmt="₹%.1fM", padding=3)
axes[1].set_xlabel("Total revenue (₹ million)")
fig.savefig("bars.png")
print(avg.round(1)); print(order.round(2).to_string())
Customers aged 40 and over spend more in every category (they are more often in higher income brackets). Electronics brings the most money (₹4.0M) even though Clothing has more orders.
40 और ऊपर की उम्र के ग्राहक हर category में ज़्यादा ख़र्च करते हैं (वे ज़्यादा बार ऊँचे income brackets में हैं)। Electronics सबसे ज़्यादा पैसा लाता है (₹4.0M), जबकि Clothing के orders ज़्यादा हैं।
40 岁及以上的顾客在每个类别上都花得更多(他们更常处于较高收入档)。虽然 Clothing 订单更多,但 Electronics 带来的收入最多(₹4.0M)。
ax.bar_label(bars, fmt="%.1f") writes the value on every bar (matplotlib ≥ 3.4). ax.set_xticks(x, labels, rotation=30, ha="right") sets tick positions and labels in one call (≥ 3.5). Sort bars by value before you plot — a ranked chart is much easier to read.
ax.bar_label(bars, fmt="%.1f") हर bar पर मान लिखता है (matplotlib ≥ 3.4)। ax.set_xticks(x, labels, rotation=30, ha="right") एक ही call में tick positions और labels तय करता है (≥ 3.5)। Plot करने से पहले bars को मान के हिसाब से sort करें — ranked chart पढ़ना कहीं आसान है।
ax.bar_label(bars, fmt="%.1f") 会在每根柱上写出数值(matplotlib ≥ 3.4)。ax.set_xticks(x, labels, rotation=30, ha="right") 一次设置刻度位置和标签(≥ 3.5)。画图前按数值排序 —— 排好序的图更易读。
Scatter Plot — Four Variables at Once 🔵Scatter Plot — एक साथ चार Variables 🔵散点图 —— 同时展示四个变量 🔵
ax.scatter() can show up to four variables in one picture: x position, y position, colour (c=) and size (s=). Note that s is the marker area in points², not its width.
ax.scatter() एक तस्वीर में चार variables तक दिखा सकता है: x स्थिति, y स्थिति, रंग (c=) और आकार (s=)। ध्यान दें कि s marker का क्षेत्रफल है (points² में), चौड़ाई नहीं।
ax.scatter() 一张图最多能展示四个变量:x 位置、y 位置、颜色(c=)和大小(s=)。注意 s 是标记的面积(单位 points²),不是宽度。
# 4) Four variables in one scatter: x, y, colour and size — plus an annotation
fig, ax = plt.subplots(figsize=(9, 6), layout="constrained")
sc = ax.scatter(df["age"], df["purchase_amount"],
c=df["rating"], cmap="viridis", # colour = 3rd variable
s=df["delivery_days"] * 8, # size = 4th variable
alpha=0.6, edgecolors="white", linewidths=0.4)
ax.set_yscale("log")
fig.colorbar(sc, ax=ax, label="Customer rating")
top = df.loc[df["purchase_amount"].idxmax()]
ax.annotate(f"Largest order: ₹{top.purchase_amount:,.0f}",
xy=(top.age, top.purchase_amount), xytext=(top.age - 25, top.purchase_amount * 0.9),
arrowprops=dict(arrowstyle="->", color="#dc2626"), color="#dc2626",
bbox=dict(boxstyle="round,pad=0.3", facecolor="#fde68a", alpha=0.4))
ax.set(xlabel="Customer age", ylabel="Purchase amount (₹, log scale)",
title="Age vs purchase — colour = rating, size = delivery days")
fig.savefig("scatter.png")
print(f"largest order: age {top.age}, ₹{top.purchase_amount:,.0f}, {top.product_category}, "
f"rating {top.rating:.0f}, {top.delivery_days} delivery days")
700 customers plus the largest order (₹80,680, age 62). Big circles (slow delivery) tend to be dark (low rating) — the −0.71 link from the heatmap, visible without a single number.
700 ग्राहक और सबसे बड़ा order (₹80,680, उम्र 62)। बड़े घेरे (धीमी delivery) ज़्यादातर गहरे (कम rating) हैं — heatmap वाला −0.71 संबंध, बिना किसी संख्या के दिखता है।
700 位顾客外加最大的一笔订单(₹80,680,年龄 62)。大圆(配送慢)往往颜色深(评分低)—— 热力图里的 −0.71 关系,不看数字也能看出来。
ax.annotate("text", xy=(x_point, y_point), xytext=(x_text, y_text)) — xy is the point, xytext is where the label sits.ax.annotate("text", xy=(x_point, y_point), xytext=(x_text, y_text)) — xy वह point है, xytext वहाँ label बैठता है।ax.annotate("text", xy=(x_point, y_point), xytext=(x_text, y_text)) —— xy 是被指的点,xytext 是文字的位置。arrowprops=dict(arrowstyle="->", color="red", lw=1.5). Other styles: "-|>", "fancy", "simple".arrowprops=dict(arrowstyle="->", color="red", lw=1.5)। दूसरी styles: "-|>", "fancy", "simple"।arrowprops=dict(arrowstyle="->", color="red", lw=1.5)。其他样式:"-|>"、"fancy"、"simple"。bbox=dict(boxstyle="round,pad=0.3", facecolor="#fde68a", alpha=0.4) puts the text in a box.bbox=dict(boxstyle="round,pad=0.3", facecolor="#fde68a", alpha=0.4) text को box में रखता है।bbox=dict(boxstyle="round,pad=0.3", facecolor="#fde68a", alpha=0.4) 给文字加一个框。top = df.loc[df["purchase_amount"].idxmax()].top = df.loc[df["purchase_amount"].idxmax()]।top = df.loc[df["purchase_amount"].idxmax()]。300 customers. Choose what colour and size show, switch the colormap, and change transparency.
300 ग्राहक। चुनें कि रंग और आकार क्या दिखाएँ, colormap बदलें, और पारदर्शिता बदलें।
300 位顾客。选择颜色和大小表示什么,切换色图,调整透明度。
Histogram and KDE — Distributions 📉Histogram और KDE — Distributions 📉直方图与 KDE —— 分布 📉
ax.hist() returns three things: the bar heights n, the bin edges bins, and the bar objects patches. Because the bars are objects, you can recolour them one by one. With density=True the bars have area 1, so a KDE curve (from scipy.stats.gaussian_kde) fits on the same scale.
ax.hist() तीन चीज़ें लौटाता है: bar की ऊँचाइयाँ n, bin के किनारे bins, और bar objects patches। Bars objects हैं, इसलिए आप उन्हें एक-एक करके नया रंग दे सकते हैं। density=True से bars का कुल क्षेत्रफल 1 होता है, इसलिए KDE curve (scipy.stats.gaussian_kde से) उसी scale पर बैठती है।
ax.hist() 返回三样东西:柱高 n、分组边界 bins 和柱对象 patches。因为柱是对象,你可以逐个改颜色。设置 density=True 后柱的总面积为 1,因此 KDE 曲线(来自 scipy.stats.gaussian_kde)能画在同一刻度上。
# 5) Histogram + KDE, bars coloured by side of the mean; overlapping group histograms
from scipy.stats import gaussian_kde
data = df["purchase_amount"].to_numpy()
fig, axes = plt.subplots(1, 2, figsize=(12, 5), layout="constrained")
n, bins, patches = axes[0].hist(data, bins=40, range=(0, 30000), density=True,
color="#60a5fa", alpha=0.7, edgecolor="white", linewidth=0.4)
mean_val, med = data.mean(), np.median(data)
for patch, left in zip(patches, bins[:-1]):
if left >= mean_val:
patch.set_facecolor("#f59e0b") # bins at or right of the mean
xs = np.linspace(0, 30000, 300)
axes[0].plot(xs, gaussian_kde(data)(xs), color="#7c3aed", label="KDE")
axes[0].axvline(mean_val, color="#f59e0b", linestyle="--", label=f"Mean ₹{mean_val:,.0f}")
axes[0].axvline(med, color="#16a34a", linestyle="--", label=f"Median ₹{med:,.0f}")
axes[0].legend()
for name, colour in zip(["Low", "Medium", "High"], ["#60a5fa", "#f87171", "#34d399"]):
vals = np.log10(df.loc[df["income_bracket"] == name, "purchase_amount"])
axes[1].hist(vals, bins=30, alpha=0.5, color=colour, label=name, density=True)
axes[1].set_xlabel("log10(purchase amount)"); axes[1].legend(title="Income")
fig.savefig("hist.png")
print(f"mean ₹{mean_val:,.0f} median ₹{med:,.0f} bins at/after mean: {(bins[:-1] >= mean_val).sum()} of 40")
print("median spend:", df.groupby("income_bracket")["purchase_amount"].median().reindex(["Low", "Medium", "High"]).to_dict())
Left: most of the bins (32 of 40) are at or above the mean, but they hold few customers — a long right tail. Right: on a log scale, the High income group sits clearly to the right.
बाएँ: ज़्यादातर bins (40 में से 32) mean या उससे ऊपर हैं, पर उनमें कम ग्राहक हैं — लंबी right tail। दाएँ: log scale पर High income group साफ़ तौर पर दाईं ओर है।
左:大多数分组(40 个中的 32 个)不低于均值,但其中顾客很少 —— 右侧长尾。右:在对数刻度上,高收入组明显偏右。
bins has one more value than patches (41 edges for 40 bars). zip(patches, bins) still works because zip stops at the shorter list, but zip(patches, bins[:-1]) says clearly that you use each bar's left edge. For skewed money data, a log-scale x-axis (right panel) shows group differences much better.
bins में patches से एक मान ज़्यादा होता है (40 bars के लिए 41 किनारे)। zip(patches, bins) फिर भी चलता है क्योंकि zip छोटी list पर रुक जाता है, पर zip(patches, bins[:-1]) साफ़ बताता है कि आप हर bar का बायाँ किनारा इस्तेमाल कर रहे हैं। Skewed पैसे वाले data के लिए log-scale x-axis (दायाँ panel) groups का अंतर कहीं बेहतर दिखाता है।
bins 比 patches 多一个值(40 根柱有 41 个边界)。zip(patches, bins) 仍然能运行,因为 zip 在较短的列表处停止,但 zip(patches, bins[:-1]) 更清楚地表明你用的是每根柱的左边界。对于偏态的金额数据,对数刻度的 x 轴(右图)能更好地显示组间差异。
Subplots — Many Charts in One Figure 🧩Subplots — एक Figure में कई Charts 🧩子图 —— 一个 Figure 里的多张图 🧩
plt.subplots(nrows, ncols) makes a grid of Axes. It is the main layout tool for dashboards and side-by-side comparisons. Watch what it returns: the shape of axes depends on the grid size.
plt.subplots(nrows, ncols) Axes का grid बनाता है। Dashboards और साथ-साथ तुलना के लिए यह मुख्य layout tool है। ध्यान दें कि यह क्या लौटाता है: axes का shape grid के आकार पर निर्भर है।
plt.subplots(nrows, ncols) 创建一个 Axes 网格,是做仪表盘和并排比较的主要布局工具。注意它的返回值:axes 的形状取决于网格大小。
# 6) Subplots: a 2×2 dashboard, and an uneven layout with subplot_mosaic
fig, axes = plt.subplots(2, 2, figsize=(12, 8), layout="constrained")
fig.suptitle("Sales Dashboard — 2025", fontsize=16, fontweight="bold")
axes[0, 0].plot(months, revenue); axes[0, 0].set_title("Revenue trend")
axes[0, 1].barh(order.index, order.to_numpy()); axes[0, 1].set_title("Revenue by category")
axes[1, 0].hist(df["age"], bins=12); axes[1, 0].set_title("Age distribution")
axes[1, 1].scatter(df["age"], df["purchase_amount"], s=4, alpha=0.4)
axes[1, 1].set_yscale("log"); axes[1, 1].set_title("Age vs spend")
fig.savefig("dashboard.png")
print("axes array shape:", axes.shape, "| top-right is axes[0, 1]")
fig2, axd = plt.subplot_mosaic([["trend", "trend"], ["cat", "age"]], figsize=(10, 7), layout="constrained")
axd["trend"].plot(daily["date"], daily["revenue"], linewidth=0.8) # wide panel on top
print("mosaic panels:", list(axd))
Change rows and columns, then click a panel to see how to reach it. Try layout = none with 3 columns.
Rows और columns बदलें, फिर किसी panel पर click करके देखें कि उस तक कैसे पहुँचें। 3 columns के साथ layout = none आज़माएँ।
改变行数和列数,再点击某个面板,看看如何访问它。试试 3 列时设 layout = none。
fig, axes = plt.subplots(nrows, ncols, figsize=(w, h), layout="constrained")fig, axes = plt.subplots(nrows, ncols, figsize=(w, h), layout="constrained")fig, axes = plt.subplots(nrows, ncols, figsize=(w, h), layout="constrained")plt.subplots(2, 1, sharex=True) — both panels use the same x range; zoom one and the other follows.plt.subplots(2, 1, sharex=True) — दोनों panels एक ही x range इस्तेमाल करते हैं; एक को zoom करें तो दूसरा भी साथ चलता है।plt.subplots(2, 1, sharex=True) —— 两个面板共用 x 范围;缩放一个,另一个跟着变。fig, axd = plt.subplot_mosaic([["A", "A"], ["B", "C"]]) — returns a dict: axd["A"] spans the full top row.fig, axd = plt.subplot_mosaic([["A", "A"], ["B", "C"]]) — dict लौटाता है: axd["A"] पूरी ऊपरी row में फैला है।fig, axd = plt.subplot_mosaic([["A", "A"], ["B", "C"]]) —— 返回一个字典:axd["A"] 占满整个第一行。axes[1, 2].set_visible(False) or fig.delaxes(axes[1, 2]). (In a 2×2 grid, axes[1, 2] does not exist — it raises IndexError.)axes[1, 2].set_visible(False) या fig.delaxes(axes[1, 2])। (2×2 grid में axes[1, 2] मौजूद नहीं — IndexError आता है।)axes[1, 2].set_visible(False) 或 fig.delaxes(axes[1, 2])。(2×2 网格里没有 axes[1, 2] —— 会报 IndexError。)for ax in axes.flat: ax.grid(alpha=0.3) works for any grid shape.for ax in axes.flat: ax.grid(alpha=0.3) किसी भी grid shape पर चलता है।for ax in axes.flat: ax.grid(alpha=0.3) 适用于任意网格形状。layout="constrained". Older code uses plt.tight_layout(); manual control: fig.subplots_adjust(hspace=0.4).layout="constrained" बेहतर है। पुराना code plt.tight_layout() इस्तेमाल करता है; हाथ से नियंत्रण: fig.subplots_adjust(hspace=0.4)।layout="constrained"。旧代码用 plt.tight_layout();手动控制:fig.subplots_adjust(hspace=0.4)。Heatmap with imshow() 🌡️imshow() से Heatmap 🌡️用 imshow() 画热力图 🌡️
ax.imshow() draws any 2-D array as a grid of coloured cells. It is the base for correlation heatmaps, pivot tables and confusion matrices. You add the numbers yourself with ax.text().
ax.imshow() किसी भी 2-D array को रंगीन cells के grid के रूप में बनाता है। Correlation heatmaps, pivot tables और confusion matrices का यही आधार है। संख्याएँ आप ax.text() से ख़ुद जोड़ते हैं।
ax.imshow() 把任意二维数组画成彩色格子网格,是相关热力图、透视表和混淆矩阵的基础。格子里的数字需要你用 ax.text() 自己加。
# 7) A correlation heatmap with ax.imshow() and a value in every cell
cols = ["age", "income_code", "purchase_amount", "delivery_days", "rating"]
corr = df[cols].corr().to_numpy(); n = len(cols)
fig, ax = plt.subplots(figsize=(7, 6), layout="constrained")
im = ax.imshow(corr, cmap="coolwarm", vmin=-1, vmax=1)
ax.grid(False) # the style's grid would cut through the cells
fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
for i in range(n):
for j in range(n):
ax.text(j, i, f"{corr[i, j]:.2f}", ha="center", va="center", fontweight="bold",
color="white" if abs(corr[i, j]) > 0.6 else "black")
ax.set_xticks(range(n), cols, rotation=45, ha="right")
ax.set_yticks(range(n), cols)
ax.set_title("Correlation matrix (Pearson)")
fig.savefig("heatmap.png")
print(np.round(corr, 2))
Text turns white on dark cells (|r| > 0.6). Strongest links: age–income 0.68 and delivery–rating −0.71.
गहरे cells पर text सफ़ेद हो जाता है (|r| > 0.6)। सबसे मज़बूत संबंध: age–income 0.68 और delivery–rating −0.71।
深色格子上文字变白(|r| > 0.6)。最强的关联:age–income 0.68,delivery–rating −0.71。
If your style turns the grid on (seaborn and ggplot styles do), grid lines are drawn across the heatmap cells. Call ax.grid(False) after imshow. Also, imshow keeps square cells by default; pass aspect="auto" only if you want the cells to stretch.
अगर आपकी style grid चालू करती है (seaborn और ggplot styles करती हैं), तो grid lines heatmap cells के आर-पार बनती हैं। imshow के बाद ax.grid(False) बुलाएँ। साथ ही, imshow default रूप से cells को वर्गाकार रखता है; aspect="auto" तभी दें जब cells को फैलाना हो।
如果样式开启了网格(seaborn 和 ggplot 样式都会),网格线会穿过热力图格子。在 imshow 之后调用 ax.grid(False)。另外,imshow 默认保持方形格子;只有想拉伸格子时才传 aspect="auto"。
Box Plot and Violin Plot 🎻Box Plot और Violin Plot 🎻箱线图与小提琴图 🎻
ax.boxplot() shows the median, quartiles, whiskers and outliers. ax.violinplot() adds the full shape, using a KDE drawn on both sides. Money data is very skewed, so both plots below use log10 of the amount, with ₹ tick labels added by a formatter.
ax.boxplot() median, quartiles, whiskers और outliers दिखाता है। ax.violinplot() दोनों तरफ़ बनी KDE से पूरा आकार जोड़ता है। पैसे का data बहुत skewed है, इसलिए नीचे के दोनों plots राशि का log10 इस्तेमाल करते हैं, और ₹ tick labels एक formatter से जोड़े गए हैं।
ax.boxplot() 显示中位数、四分位数、须线和异常值。ax.violinplot() 用两侧对称的 KDE 补上完整形状。金额数据非常偏,所以下面两图都用金额的 log10,并用格式化器加上 ₹ 刻度标签。
# 8) Notched box plot and violin plot of the same groups (on log10 of the amount)
cats = ["Grocery", "Books", "Clothing", "Furniture", "Electronics"]
groups = [np.log10(df.loc[df["product_category"] == c, "purchase_amount"]).to_numpy() for c in cats]
colors = ["#0d9488", "#7c3aed", "#3b82f6", "#d97706", "#ea580c"]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), sharey=True, layout="constrained")
bp = ax1.boxplot(groups, tick_labels=cats, patch_artist=True, notch=True,
orientation="vertical", widths=0.5) # 'vert=' is deprecated since 3.11
for patch, c in zip(bp["boxes"], colors):
patch.set_facecolor(c); patch.set_alpha(0.6)
vp = ax2.violinplot(groups, showmedians=True)
for body, c in zip(vp["bodies"], colors):
body.set_facecolor(c); body.set_alpha(0.5)
ax2.set_xticks(range(1, 6), cats)
ax1.yaxis.set_major_formatter(mticker.FuncFormatter(lambda v, _: f"₹{10 ** v:,.0f}"))
fig.savefig("box_violin.png")
for c, g in zip(cats, groups):
q1, med, q3 = np.percentile(g, [25, 50, 75]); half = 1.57 * (q3 - q1) / np.sqrt(len(g)) # notch rule
print(f"{c:<12} n={len(g):<4} median ₹{10 ** med:>6,.0f} notch ₹{10 ** (med - half):,.0f} – ₹{10 ** (med + half):,.0f}")
No two notches overlap, so all five medians very likely differ. On a log scale the groups look roughly symmetric — the skew was mostly a scale effect.
कोई भी दो notches overlap नहीं करते, इसलिए पाँचों medians बहुत संभवतः अलग हैं। Log scale पर groups लगभग सममित दिखते हैं — skew ज़्यादातर scale का असर था।
没有两个凹口重叠,所以五个中位数很可能都不同。在对数刻度上各组大致对称 —— 偏态主要来自刻度。
vert=True is deprecated since 3.11 (it will be removed in 3.13): use orientation="vertical" or "horizontal". The labels= argument of boxplot was renamed tick_labels= in 3.9. The notch is a rough 95% interval for the median: median ± 1.57 × IQR / √n.
vert=True 3.11 से deprecated है (3.13 में हटेगा): orientation="vertical" या "horizontal" इस्तेमाल करें। boxplot का labels= argument 3.9 में tick_labels= हो गया। Notch median का लगभग 95% interval है: median ± 1.57 × IQR / √n।
vert=True 自 3.11 起已弃用(将在 3.13 移除):请用 orientation="vertical" 或 "horizontal"。boxplot 的 labels= 参数在 3.9 改名为 tick_labels=。凹口是中位数的大约 95% 区间:中位数 ± 1.57 × IQR / √n。
300 values from two groups. Pull the groups apart and compare the box (left) with the violin (right).
दो groups से 300 मान। Groups को अलग खींचें और box (बाएँ) की violin (दाएँ) से तुलना करें।
来自两组的 300 个值。把两组拉开,比较箱线图(左)和小提琴图(右)。
Styles and Saving Figures 💾Styles और Figures Save करना 💾样式与保存图片 💾
A style sheet changes many rcParams at once. plt.style.use() changes every later chart; with plt.style.context("ggplot"): changes only the code inside the block. The picture below uses the real colours each style sets.
Style sheet एक साथ कई rcParams बदलती है। plt.style.use() बाद के हर chart को बदलता है; with plt.style.context("ggplot"): सिर्फ़ block के अंदर के code को बदलता है। नीचे की तस्वीर हर style के असली रंग इस्तेमाल करती है।
样式表一次修改许多 rcParams。plt.style.use() 影响之后所有的图;with plt.style.context("ggplot"): 只影响代码块内部。下图使用了每种样式真实设置的颜色。
Values read from matplotlib itself. Note: seaborn-v0_8-darkgrid changes the background and grid but keeps the default colour cycle; bmh uses dashed grid lines and thicker lines (2.0).
मान ख़ुद matplotlib से पढ़े गए हैं। ध्यान दें: seaborn-v0_8-darkgrid पृष्ठभूमि और grid बदलता है पर default colour cycle रखता है; bmh dashed grid lines और मोटी lines (2.0) इस्तेमाल करता है।
数值直接读取自 matplotlib。注意:seaborn-v0_8-darkgrid 改变了背景和网格,但保留默认颜色循环;bmh 使用虚线网格和更粗的线(2.0)。
# 9) Save once, in the right format
import os
fig, ax = plt.subplots(figsize=(6, 4), layout="constrained")
ax.plot(months, revenue, marker="o")
ax.set(title="Revenue 2025", ylabel="₹ thousand per day")
for name, kw in [("chart.png", dict(dpi=300)), ("chart.pdf", {}), ("chart.svg", {})]:
fig.savefig(name, bbox_inches="tight", **kw)
print(f"{name:<10} {os.path.getsize(name) / 1024:6.1f} KB")
| FormatFormat格式 | Typeप्रकार类型 | Use it forकब इस्तेमाल करें用途 |
|---|---|---|
.png | raster (pixels)raster (pixels)位图(像素) | Web, slides, notebooks. Use dpi=300 for print.Web, slides, notebooks। Print के लिए dpi=300।网页、幻灯片、笔记本。打印用 dpi=300。 |
.pdf | vectorvector矢量 | Papers and reports (LaTeX accepts PDF directly). Stays sharp at any size.Papers और reports (LaTeX सीधे PDF लेता है)। किसी भी आकार पर तीखा रहता है।论文和报告(LaTeX 可直接使用 PDF)。任意缩放都清晰。 |
.svg | vectorvector矢量 | Web pages and further editing in Inkscape / Illustrator.Web pages और Inkscape / Illustrator में आगे editing।网页,以及在 Inkscape / Illustrator 中继续编辑。 |
.eps | vector (old)vector (पुराना)矢量(旧) | Only if a journal asks for it. It has no transparency, so alpha is lost.तभी जब journal माँगे। इसमें transparency नहीं होती, इसलिए alpha खो जाता है।仅在期刊要求时使用。它不支持透明度,alpha 会丢失。 |
bbox_inches="tight" trims empty margins and stops labels from being cut off when saving. A vector file stores every point as an object, so a scatter plot with 100,000 points can become a huge, slow PDF. In that case pass rasterized=True to the scatter call, or save as PNG.
bbox_inches="tight" ख़ाली हाशिये काटता है और save करते समय labels कटने से रोकता है। Vector file हर point को object के रूप में रखती है, इसलिए 100,000 points वाला scatter plot बहुत बड़ा और धीमा PDF बन सकता है। तब scatter call में rasterized=True दें, या PNG में save करें।
bbox_inches="tight" 会裁掉空白边距,防止保存时标签被截断。矢量文件把每个点都存成对象,所以有 10 万个点的散点图会变成巨大而缓慢的 PDF。这时在 scatter 调用中加 rasterized=True,或保存为 PNG。
Make the Practice Data 🐍Practice Data बनाएँ 🐍生成练习数据 🐍
These are the same two files used in the Data Visualisation tutorial. The patterns (older → higher income, category → spend, slow delivery → low rating, an October–November peak) are built in on purpose. Run this before the other blocks.
ये वही दो files हैं जो Data Visualisation tutorial में इस्तेमाल हुईं। Patterns (बड़ी उम्र → ज़्यादा income, category → ख़र्च, धीमी delivery → कम rating, अक्टूबर–नवंबर की चोटी) जानबूझकर डाले गए हैं। बाक़ी blocks से पहले इसे चलाएँ।
这就是“数据可视化”教程中用到的两个文件。其中的规律(年龄越大收入越高、类别决定消费、配送慢评分低、10–11 月高峰)都是有意内置的。请先运行它,再运行其他代码块。
# 0) Build two practice datasets that contain real patterns to find
import numpy as np, pandas as pd
rng = np.random.default_rng(11)
# --- customers.csv: one row per customer ---
n = 2000
age = rng.integers(18, 66, n)
p_hi = np.clip((age - 18) / 60, 0.05, 0.75) # older -> more likely higher income
income_code = np.minimum(3, rng.binomial(3, p_hi))
brackets = np.array(["Low", "Medium", "High", "Very High"])
cats = rng.choice(["Electronics", "Furniture", "Clothing", "Books", "Grocery"], n, p=[.15, .1, .3, .2, .25])
cat_base = pd.Series({"Electronics": 9.0, "Furniture": 8.7, "Clothing": 7.9, "Books": 7.0, "Grocery": 6.3})[cats].to_numpy()
spend = np.exp(cat_base + 0.35 * income_code + rng.normal(0, 0.45, n)).round(-1)
delivery = rng.integers(1, 11, n)
rating = np.clip(np.round(5.2 - 0.28 * delivery + rng.normal(0, 0.8, n)), 1, 5)
pd.DataFrame({"age": age, "income_bracket": brackets[income_code], "income_code": income_code,
"product_category": cats, "purchase_amount": spend,
"delivery_days": delivery, "rating": rating}).to_csv("customers.csv", index=False)
# --- daily_sales.csv: two years of daily revenue (₹ thousand) ---
days = pd.date_range("2024-01-01", "2025-12-31", freq="D")
t = np.arange(len(days))
trend = 40 + 20 * t / len(days)
weekly = np.where(days.dayofweek >= 5, 1.18, 1.0)
festive = 1 + 0.45 * np.exp(-((days.dayofyear - 305) / 18) ** 2) + 0.25 * np.exp(-((days.dayofyear - 358) / 6) ** 2)
revenue = np.asarray(trend * weekly * festive * rng.normal(1, 0.07, len(days)))
for d, f in [("2024-03-15", 2.2), ("2025-02-01", 5.0), ("2025-06-10", 0.2)]: # flash sale, data error, outage
revenue[days.get_loc(d)] *= f
pd.DataFrame({"date": days, "revenue": revenue.round(2)}).to_csv("daily_sales.csv", index=False)
print("customers.csv:", n, "rows daily_sales.csv:", len(days), "rows")
Golden Rules of Matplotlib 🏆Matplotlib के सुनहरे नियम 🏆Matplotlib 黄金法则 🏆
fig, ax = plt.subplots() and call methods on ax. Keep the quick plt.plot() style for throwaway one-panel charts.fig, ax = plt.subplots() इस्तेमाल करें और methods ax पर बुलाएँ। जल्दी वाला plt.plot() तरीक़ा सिर्फ़ एक panel वाले कच्चे charts के लिए रखें।fig, ax = plt.subplots(),在 ax 上调用方法。快速的 plt.plot() 写法只留给一次性的单面板图。layout="constrained" (or call fig.tight_layout()), and save with bbox_inches="tight" so nothing is cut off.layout="constrained" से बनाएँ (या fig.tight_layout() बुलाएँ), और bbox_inches="tight" से save करें ताकि कुछ न कटे।layout="constrained" 创建图(或调用 fig.tight_layout()),保存时加 bbox_inches="tight",避免内容被截断。ax.spines[["top", "right"]].set_visible(False), or set it once in rcParams.ax.spines[["top", "right"]].set_visible(False), या rcParams में एक बार तय करें।ax.spines[["top", "right"]].set_visible(False),或在 rcParams 中一次设好。dpi=300 for print, and PDF or SVG when the figure may be resized. Use rasterized=True for huge scatter plots.dpi=300 पर save करें, और जब figure का आकार बदल सकता हो तो PDF या SVG। बहुत बड़े scatter plots के लिए rasterized=True।dpi=300;图可能被缩放时用 PDF 或 SVG。超大散点图用 rasterized=True。ax.annotate() to point at the key fact on the chart — the peak, the drop, the outlier. Find its coordinates from the data, not by hand.ax.annotate() इस्तेमाल करें — चोटी, गिरावट, outlier। इसके coordinates data से निकालें, हाथ से नहीं।ax.annotate() 指出图上的关键事实 —— 峰值、下跌、异常点。坐标要从数据中求,而不是手写。viridis / cividis for ordered values, coolwarm when 0 is the middle, tab10 for categories. Avoid jet.viridis / cividis, जब 0 बीच में हो तो coolwarm, categories के लिए tab10। jet से बचें।viridis / cividis,以 0 为中心用 coolwarm,类别用 tab10。避免 jet。Matplotlib asks you to write more code, but in return every part of the chart is yours to control. Learn the Figure → Axes → artist model, use the ax methods, and you can fix any chart — including the ones that seaborn and pandas make for you.
Matplotlib में ज़्यादा code लिखना पड़ता है, पर बदले में chart का हर हिस्सा आपके नियंत्रण में रहता है। Figure → Axes → artist model सीखें, ax methods इस्तेमाल करें, और आप कोई भी chart ठीक कर सकते हैं — seaborn और pandas के बनाए charts भी।
Matplotlib 需要多写一些代码,但作为回报,图表的每个部分都由你掌控。掌握 Figure → Axes → artist 模型,使用 ax 的方法,你就能修好任何图 —— 包括 seaborn 和 pandas 替你画的那些。