Kod do wyzwania Adwentowego – Dzień 17.


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


import seaborn as sns
from scipy.interpolate import splprep, splev
from scipy.spatial.transform import Rotation as R
from sklearn.decomposition import PCA
from statsmodels.nonparametric.smoothers_lowess import lowess


def smooth_closed_curve(points, per=True, s=0.5, n=300):
„””
Smooth a (possibly closed) polyline using a B-spline.
points: (m,2)
„””
pts = np.asarray(points, dtype=float)
x, y = pts[:, 0], pts[:, 1]
tck, _ = splprep([x, y], s=s, per=per)
u = np.linspace(0, 1, n)
xs, ys = splev(u, tck)
return np.column_stack([xs, ys])


def ellipse(cx, cy, rx, ry, n=250, angle_deg=0):
t = np.linspace(0, 2 * np.pi, n)
x = rx * np.cos(t)
y = ry * np.sin(t)
if angle_deg != 0:
rot = np.array(
[
[np.cos(np.deg2rad(angle_deg)), -np.sin(np.deg2rad(angle_deg))],
[np.sin(np.deg2rad(angle_deg)), np.cos(np.deg2rad(angle_deg))],
]
)
xy = rot @ np.vstack([x, y])
x, y = xy[0], xy[1]
return np.column_stack([cx + x, cy + y])


def draw_filled(ax, pts, face=None, edge=”black”, lw=2, alpha=1.0, z=1):
pts = np.asarray(pts)
ax.fill(
pts[:, 0],
pts[:, 1],
facecolor=face,
edgecolor=edge,
linewidth=lw,
alpha=alpha,
zorder=z,
)


def draw_line(ax, pts, color=”black”, lw=2, alpha=1.0, z=2, ls=”-„):
pts = np.asarray(pts)
ax.plot(
pts[:, 0],
pts[:, 1],
color=color,
linewidth=lw,
alpha=alpha,
zorder=z,
linestyle=ls,
)


fig, ax = plt.subplots(figsize=(12, 6))
ax.set_aspect(„equal”)
ax.axis(„off”)

# Ground line
ax.plot([-1, 13], [-0.2, -0.2], linewidth=3)

body = ellipse(cx=5.3, cy=2.0, rx=2.8, ry=1.5, n=300, angle_deg=5)
draw_filled(ax, body, face=”#8B5A2B”, lw=2, z=2)

neck_pts = np.array(
[
[3.4, 2.6],
[2.7, 3.2],
[2.2, 3.6],
[2.0, 4.1],
[2.3, 4.5],
[2.9, 4.6],
[3.6, 4.3],
[3.9, 3.8],
[4.0, 3.2],
]
)
neck = smooth_closed_curve(neck_pts, per=True, s=0.1, n=220)
draw_filled(ax, neck, face=”#8B5A2B”, lw=2, z=3)

# Head
head = ellipse(cx=1.8, cy=4.7, rx=1.2, ry=0.85, n=260, angle_deg=-10)
draw_filled(ax, head, face=”#8B5A2B”, lw=2, z=4)

# Snout
snout = ellipse(cx=0.8, cy=4.5, rx=0.75, ry=0.5, n=220, angle_deg=-12)
draw_filled(ax, snout, face=”#A46B3E”, lw=2, z=5)

# Nose (red)
nose = ellipse(cx=0.15, cy=4.35, rx=0.22, ry=0.22, n=120)
draw_filled(ax, nose, face=”#D11F1F”, lw=2, z=6)

# Eye
eye = ellipse(cx=2.1, cy=4.9, rx=0.12, ry=0.12, n=80)
draw_filled(ax, eye, face=”black”, lw=1, z=7)

# Ear
ear = np.array([[2.6, 5.2], [3.1, 5.6], [2.7, 5.8], [2.4, 5.5]])
ear_s = smooth_closed_curve(ear, per=True, s=0.01, n=140)
draw_filled(ax, ear_s, face=”#8B5A2B”, lw=2, z=6)

# Legs
for x0 in [4.2, 5.2, 6.1, 7.0]:
leg = np.array([[x0, 0.2], [x0 + 0.25, 0.2], [x0 + 0.35, 1.8], [x0 + 0.05, 1.9]])
leg_s = smooth_closed_curve(leg, per=True, s=0.01, n=140)
draw_filled(ax, leg_s, face=”#6B3E1E”, lw=2, z=1)

# Hooves
for x0 in [4.25, 5.25, 6.15, 7.05]:
hoof = np.array(
[[x0 – 0.05, 0.2], [x0 + 0.35, 0.2], [x0 + 0.32, -0.05], [x0 – 0.08, -0.05]]
)
hoof_s = smooth_closed_curve(hoof, per=True, s=0.01, n=120)
draw_filled(ax, hoof_s, face=”#2B2B2B”, lw=1.5, z=0)

# Tail
tail_pts = np.array([[8.2, 2.3], [9.0, 2.4], [9.2, 2.0], [8.8, 1.7], [8.2, 1.8]])
tail = smooth_closed_curve(tail_pts, per=True, s=0.01, n=160)
draw_filled(ax, tail, face=”#6B3E1E”, lw=2, z=3)

# Antlers: draw „branches” smoothed with LOWESS and lightly rotated via PCA/Rotation
antler_base = np.array(
[
[2.1, 5.5],
[2.0, 6.2],
[2.1, 6.9],
[2.4, 7.4],
[2.8, 7.8],
[2.6, 7.2],
[2.9, 6.9],
[3.2, 7.2],
[3.5, 7.6],
[3.4, 7.0],
[3.1, 6.6],
[3.4, 6.3],
[3.6, 6.6],
[3.9, 7.0],
[3.7, 6.1],
[3.1, 5.8],
[2.6, 5.6],
]
)

# Use LOWESS to smooth y as function of x (stylized antler curve)
df_ant = pd.DataFrame(antler_base, columns=[„x”, „y”]).sort_values(„x”)
sm = lowess(df_ant[„y”], df_ant[„x”], frac=0.4, return_sorted=True)
ant_smooth = np.column_stack([sm[:, 0], sm[:, 1]])


pca = PCA(n_components=2).fit(ant_smooth)
coords = pca.transform(ant_smooth)
coords[:, 1] *= -1 # mirror
mirrored = pca.inverse_transform(coords)

rot = R.from_euler(„z”, 10, degrees=True).as_matrix()[:2, :2]
mirrored = (rot @ (mirrored – ant_smooth.mean(axis=0)).T).T + ant_smooth.mean(axis=0)

draw_line(ax, ant_smooth, lw=3, z=8)
draw_line(ax, mirrored, lw=3, z=8)


harness = np.array([[3.2, 2.9], [5.1, 1.4], [7.1, 2.2]])
draw_line(ax, harness, lw=3, z=9)


coat = ellipse(cx=5.9, cy=4.2, rx=1.25, ry=1.1, n=300, angle_deg=-5)
draw_filled(ax, coat, face=”#C41212″, lw=2, z=10)


belt = np.array([[5.0, 4.0], [6.9, 3.85], [6.95, 4.25], [5.05, 4.4]])
belt_s = smooth_closed_curve(belt, per=True, s=0.01, n=220)
draw_filled(ax, belt_s, face=”#1B1B1B”, lw=1.5, z=11)

buckle = ellipse(cx=6.2, cy=4.12, rx=0.18, ry=0.14, n=120)
draw_filled(ax, buckle, face=”#D4AF37″, lw=1.2, z=12)


head_santa = ellipse(cx=5.4, cy=5.2, rx=0.55, ry=0.55, n=220)
draw_filled(ax, head_santa, face=”#F2C9A0″, lw=2, z=12)


beard_pts = np.array([[4.95, 5.05], [5.05, 4.65], [5.25, 4.5], [5.55, 4.55], [5.75, 4.75], [5.8, 5.05], [5.5, 5.0]])
beard = smooth_closed_curve(beard_pts, per=True, s=0.01, n=220)
draw_filled(ax, beard, face=”white”, lw=2, z=13)


hat_red = np.array([[5.0, 5.55], [5.65, 5.95], [6.0, 5.55], [5.4, 5.4]])
hat_red_s = smooth_closed_curve(hat_red, per=True, s=0.01, n=180)
draw_filled(ax, hat_red_s, face=”#C41212″, lw=2, z=14)

hat_trim = np.array([[5.0, 5.55], [6.0, 5.55], [6.0, 5.35], [5.0, 5.35]])
hat_trim_s = smooth_closed_curve(hat_trim, per=True, s=0.01, n=140)
draw_filled(ax, hat_trim_s, face=”white”, lw=1.5, z=15)

pom = ellipse(cx=6.05, cy=5.55, rx=0.16, ry=0.16, n=100)
draw_filled(ax, pom, face=”white”, lw=1.2, z=16)


arm = np.array([[6.4, 4.6], [7.1, 4.2], [6.95, 3.95], [6.2, 4.25]])
arm_s = smooth_closed_curve(arm, per=True, s=0.01, n=180)
draw_filled(ax, arm_s, face=”#C41212″, lw=2, z=13)

hand = ellipse(cx=7.05, cy=4.05, rx=0.18, ry=0.15, n=100)
draw_filled(ax, hand, face=”#F2C9A0″, lw=1.5, z=14)


reins = np.array([[7.05, 4.05], [3.2, 3.0], [2.2, 4.4]])
draw_line(ax, reins, lw=2.5, z=16)


sack = ellipse(cx=6.9, cy=5.0, rx=0.65, ry=0.55, n=220, angle_deg=10)
draw_filled(ax, sack, face=”#8E6B3E”, lw=2, z=9)

tie = ellipse(cx=6.45, cy=5.1, rx=0.12, ry=0.12, n=80)
draw_filled(ax, tie, face=”#6E4D2A”, lw=1.5, z=10)

rng = np.random.default_rng(7)
snow = rng.uniform([-0.5, 0.2], [12.5, 7.8], size=(160, 2))
ax.scatter(snow[:, 0], snow[:, 1], s=rng.uniform(2, 10, size=160), alpha=0.7, zorder=20)

ax.set_xlim(-0.8, 12.5)
ax.set_ylim(-0.6, 8.3)
plt.title(„Twój wykres Pythonowy w PBI”, pad=12)
plt.show()

Dane są wszędzie, wiesz?

Zapisz się, jeśli interesujesz się Power BI'em i danymi. Co jakiś czas odezwę się z ciekawymi materiałami.