|
| 1 | +""" pyplots.ai |
| 2 | +polar-line: Polar Line Plot |
| 3 | +Library: letsplot 4.8.2 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2025-12-30 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import pandas as pd |
| 9 | +from lets_plot import ( |
| 10 | + LetsPlot, |
| 11 | + aes, |
| 12 | + coord_fixed, |
| 13 | + element_text, |
| 14 | + geom_path, |
| 15 | + geom_point, |
| 16 | + geom_text, |
| 17 | + ggplot, |
| 18 | + ggsize, |
| 19 | + labs, |
| 20 | + scale_color_manual, |
| 21 | + theme, |
| 22 | + theme_void, |
| 23 | +) |
| 24 | +from lets_plot.export import ggsave |
| 25 | + |
| 26 | + |
| 27 | +LetsPlot.setup_html() |
| 28 | + |
| 29 | +# Data - Monthly average temperature pattern (cyclical) |
| 30 | +np.random.seed(42) |
| 31 | +months = np.arange(0, 360, 30) # 12 months as angles (0, 30, 60, ... 330) |
| 32 | +month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
| 33 | + |
| 34 | +# Simulated temperature pattern (warm summer, cold winter) |
| 35 | +temp_city_a = np.array([2, 4, 10, 15, 20, 25, 28, 27, 22, 14, 7, 3]) |
| 36 | +temp_city_b = np.array([5, 7, 12, 17, 22, 27, 30, 29, 24, 16, 10, 6]) |
| 37 | + |
| 38 | +# Close the loop by adding the first point at the end |
| 39 | +angles_closed = np.append(months, 360) |
| 40 | +temp_a_closed = np.append(temp_city_a, temp_city_a[0]) |
| 41 | +temp_b_closed = np.append(temp_city_b, temp_city_b[0]) |
| 42 | + |
| 43 | +# Convert to radians for polar coordinates |
| 44 | +theta_a = np.radians(angles_closed) |
| 45 | +theta_b = np.radians(angles_closed) |
| 46 | + |
| 47 | +# Create x, y coordinates from polar (for lets-plot which uses Cartesian) |
| 48 | +x_a = temp_a_closed * np.cos(theta_a) |
| 49 | +y_a = temp_a_closed * np.sin(theta_a) |
| 50 | +x_b = temp_b_closed * np.cos(theta_b) |
| 51 | +y_b = temp_b_closed * np.sin(theta_b) |
| 52 | + |
| 53 | +# Create DataFrame |
| 54 | +df = pd.DataFrame( |
| 55 | + { |
| 56 | + "x": np.concatenate([x_a, x_b]), |
| 57 | + "y": np.concatenate([y_a, y_b]), |
| 58 | + "radius": np.concatenate([temp_a_closed, temp_b_closed]), |
| 59 | + "angle": np.concatenate([angles_closed, angles_closed]), |
| 60 | + "city": ["City A"] * len(x_a) + ["City B"] * len(x_b), |
| 61 | + } |
| 62 | +) |
| 63 | + |
| 64 | +# Create concentric circles for polar grid |
| 65 | +grid_radii = [10, 20, 30] |
| 66 | +circle_points = 100 |
| 67 | +grid_circles = [] |
| 68 | +for r in grid_radii: |
| 69 | + theta_grid = np.linspace(0, 2 * np.pi, circle_points) |
| 70 | + grid_circles.append(pd.DataFrame({"x": r * np.cos(theta_grid), "y": r * np.sin(theta_grid), "radius": r})) |
| 71 | +grid_df = pd.concat(grid_circles, ignore_index=True) |
| 72 | + |
| 73 | +# Create radial lines for grid (every 30 degrees = each month) |
| 74 | +radial_lines = [] |
| 75 | +for angle_deg in range(0, 360, 30): |
| 76 | + angle_rad = np.radians(angle_deg) |
| 77 | + radial_lines.append( |
| 78 | + pd.DataFrame({"x": [0, 35 * np.cos(angle_rad)], "y": [0, 35 * np.sin(angle_rad)], "angle": angle_deg}) |
| 79 | + ) |
| 80 | +radial_df = pd.concat(radial_lines, ignore_index=True) |
| 81 | + |
| 82 | +# Month labels positions |
| 83 | +label_radius = 34 |
| 84 | +month_labels_df = pd.DataFrame( |
| 85 | + { |
| 86 | + "x": [label_radius * np.cos(np.radians(a)) for a in months], |
| 87 | + "y": [label_radius * np.sin(np.radians(a)) for a in months], |
| 88 | + "label": month_names, |
| 89 | + } |
| 90 | +) |
| 91 | + |
| 92 | +# Plot |
| 93 | +plot = ( |
| 94 | + ggplot() |
| 95 | + # Concentric grid circles |
| 96 | + + geom_path(aes(x="x", y="y", group="radius"), data=grid_df, color="#CCCCCC", size=0.5, alpha=0.6) |
| 97 | + # Radial grid lines |
| 98 | + + geom_path(aes(x="x", y="y", group="angle"), data=radial_df, color="#CCCCCC", size=0.5, alpha=0.6) |
| 99 | + # Data lines |
| 100 | + + geom_path(aes(x="x", y="y", color="city"), data=df, size=2) |
| 101 | + # Data points |
| 102 | + + geom_point(aes(x="x", y="y", color="city"), data=df, size=5) |
| 103 | + # Month labels |
| 104 | + + geom_text(aes(x="x", y="y", label="label"), data=month_labels_df, size=14, color="#333333") |
| 105 | + # Colors |
| 106 | + + scale_color_manual(values=["#306998", "#FFD43B"]) |
| 107 | + # Theme and labels |
| 108 | + + labs(title="polar-line · letsplot · pyplots.ai", color="Location") |
| 109 | + + theme_void() |
| 110 | + + theme( |
| 111 | + plot_title=element_text(size=24, hjust=0.5), |
| 112 | + legend_position="right", |
| 113 | + legend_title=element_text(size=18), |
| 114 | + legend_text=element_text(size=16), |
| 115 | + ) |
| 116 | + + coord_fixed() |
| 117 | + + ggsize(1600, 900) |
| 118 | +) |
| 119 | + |
| 120 | +# Save |
| 121 | +ggsave(plot, "plot.png", path=".", scale=3) |
| 122 | + |
| 123 | +# Also save HTML for interactive version |
| 124 | +ggsave(plot, "plot.html", path=".") |
0 commit comments