|
1 | 1 | """ pyplots.ai |
2 | 2 | violin-basic: Basic Violin Plot |
3 | | -Library: seaborn 0.13.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-23 |
| 3 | +Library: seaborn 0.13.2 | Python 3.14.3 |
| 4 | +Quality: 92/100 | Updated: 2026-02-21 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import matplotlib.pyplot as plt |
|
12 | 12 |
|
13 | 13 | # Data - Salary distributions across departments |
14 | 14 | np.random.seed(42) |
15 | | -categories = ["Engineering", "Marketing", "Sales", "Support"] |
16 | | -data = [] |
17 | | - |
18 | | -for cat in categories: |
19 | | - # Different distribution shapes per category |
20 | | - if cat == "Engineering": |
21 | | - values = np.random.normal(85000, 15000, 150) |
22 | | - elif cat == "Marketing": |
23 | | - values = np.random.normal(70000, 12000, 150) |
24 | | - elif cat == "Sales": |
25 | | - # Bimodal distribution for sales (junior vs senior) |
26 | | - values = np.concatenate([np.random.normal(55000, 8000, 75), np.random.normal(90000, 10000, 75)]) |
27 | | - else: # Support |
28 | | - values = np.random.normal(55000, 10000, 150) |
29 | | - |
30 | | - for v in values: |
31 | | - data.append({"Department": cat, "Salary": v}) |
32 | | - |
33 | | -df = pd.DataFrame(data) |
34 | | - |
35 | | -# Plot |
| 15 | +departments = ["Engineering", "Marketing", "Sales", "Support"] |
| 16 | +records = [] |
| 17 | + |
| 18 | +for dept in departments: |
| 19 | + if dept == "Engineering": |
| 20 | + salaries = np.random.normal(85000, 15000, 150) |
| 21 | + elif dept == "Marketing": |
| 22 | + salaries = np.random.normal(70000, 12000, 150) |
| 23 | + elif dept == "Sales": |
| 24 | + # Bimodal distribution (junior vs senior) — showcases KDE strength |
| 25 | + salaries = np.concatenate([np.random.normal(55000, 8000, 75), np.random.normal(90000, 10000, 75)]) |
| 26 | + else: |
| 27 | + salaries = np.random.normal(55000, 10000, 150) |
| 28 | + for s in salaries: |
| 29 | + records.append({"Department": dept, "Salary": s}) |
| 30 | + |
| 31 | +df = pd.DataFrame(records) |
| 32 | + |
| 33 | +# Distinctive palette with good contrast between categories |
| 34 | +palette = ["#306998", "#E8825A", "#5BA38B", "#C46BAE"] |
| 35 | + |
36 | 36 | fig, ax = plt.subplots(figsize=(16, 9)) |
| 37 | +fig.patch.set_facecolor("#FAFAFA") |
| 38 | +ax.set_facecolor("#FAFAFA") |
37 | 39 |
|
| 40 | +# Violin plot |
38 | 41 | sns.violinplot( |
39 | 42 | data=df, |
40 | 43 | x="Department", |
41 | 44 | y="Salary", |
42 | 45 | hue="Department", |
43 | | - palette=["#306998", "#FFD43B", "#306998", "#FFD43B"], |
44 | | - inner="quart", # Show quartiles inside violin |
45 | | - linewidth=2, |
| 46 | + palette=palette, |
| 47 | + inner="box", |
| 48 | + cut=0, |
| 49 | + linewidth=1.2, |
| 50 | + saturation=0.85, |
| 51 | + legend=False, |
| 52 | + ax=ax, |
| 53 | +) |
| 54 | + |
| 55 | +# Stripplot overlay — signature seaborn layering pattern |
| 56 | +sns.stripplot( |
| 57 | + data=df, |
| 58 | + x="Department", |
| 59 | + y="Salary", |
| 60 | + hue="Department", |
| 61 | + palette=palette, |
| 62 | + dodge=False, |
| 63 | + jitter=0.25, |
| 64 | + size=2.5, |
| 65 | + alpha=0.25, |
46 | 66 | legend=False, |
47 | 67 | ax=ax, |
48 | 68 | ) |
49 | 69 |
|
| 70 | +# Annotate the bimodal Sales distribution to guide the viewer |
| 71 | +sales_data = df[df["Department"] == "Sales"]["Salary"] |
| 72 | +lower_peak = sales_data[sales_data < 72000].median() |
| 73 | +upper_peak = sales_data[sales_data >= 72000].median() |
| 74 | + |
| 75 | +ax.annotate( |
| 76 | + "Junior cohort", |
| 77 | + xy=(1.85, lower_peak), |
| 78 | + xytext=(1.35, lower_peak - 5000), |
| 79 | + fontsize=12, |
| 80 | + fontstyle="italic", |
| 81 | + color="#555555", |
| 82 | + ha="right", |
| 83 | + va="center", |
| 84 | + arrowprops={"arrowstyle": "->", "color": "#888888", "lw": 1.0}, |
| 85 | +) |
| 86 | +ax.annotate( |
| 87 | + "Senior cohort", |
| 88 | + xy=(1.85, upper_peak), |
| 89 | + xytext=(1.35, upper_peak + 5000), |
| 90 | + fontsize=12, |
| 91 | + fontstyle="italic", |
| 92 | + color="#555555", |
| 93 | + ha="right", |
| 94 | + va="center", |
| 95 | + arrowprops={"arrowstyle": "->", "color": "#888888", "lw": 1.0}, |
| 96 | +) |
| 97 | + |
50 | 98 | # Style |
51 | | -ax.set_xlabel("Department", fontsize=20) |
52 | | -ax.set_ylabel("Salary ($)", fontsize=20) |
53 | | -ax.set_title("violin-basic · seaborn · pyplots.ai", fontsize=24) |
| 99 | +ax.set_xlabel("Department", fontsize=20, labelpad=12) |
| 100 | +ax.set_ylabel("Salary ($)", fontsize=20, labelpad=12) |
| 101 | +ax.set_title("violin-basic · seaborn · pyplots.ai", fontsize=24, fontweight="medium", pad=20) |
54 | 102 | ax.tick_params(axis="both", labelsize=16) |
55 | | -ax.grid(True, alpha=0.3, linestyle="--", axis="y") |
| 103 | + |
| 104 | +for spine in ax.spines.values(): |
| 105 | + spine.set_visible(False) |
| 106 | + |
| 107 | +ax.yaxis.grid(True, alpha=0.3, linewidth=0.6, color="#CCCCCC") |
| 108 | +ax.set_axisbelow(True) |
56 | 109 |
|
57 | 110 | # Format y-axis as currency |
58 | 111 | ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f"${x / 1000:.0f}k")) |
59 | 112 |
|
60 | 113 | plt.tight_layout() |
61 | | -plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
| 114 | +plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor=fig.get_facecolor()) |
0 commit comments