diff --git a/solutions/sem02/lesson03/task1.py b/solutions/sem02/lesson03/task1.py index 2c3fc0b58..84987cadb 100644 --- a/solutions/sem02/lesson03/task1.py +++ b/solutions/sem02/lesson03/task1.py @@ -8,13 +8,22 @@ class ShapeMismatchError(Exception): def sum_arrays_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs.shape != rhs.shape: + raise ShapeMismatchError + return rhs+lhs -def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: ... +def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: + ans = 3 * (abscissa ** 2) + 2 * abscissa + 1 + return ans def get_mutual_l2_distances_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs.shape != rhs.shape: + raise ShapeMismatchError + + return np.sqrt(np.sum((lhs[:, np.newaxis, :] - rhs[np.newaxis, :, :])**2, axis=2)) diff --git a/solutions/sem02/lesson03/task2.py b/solutions/sem02/lesson03/task2.py index fc823c1d6..3bd06fed5 100644 --- a/solutions/sem02/lesson03/task2.py +++ b/solutions/sem02/lesson03/task2.py @@ -9,11 +9,32 @@ def convert_from_sphere( distances: np.ndarray, azimuth: np.ndarray, inclination: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + if not distances.shape == azimuth.shape == inclination.shape: + raise ShapeMismatchError + + x = distances * np.sin(inclination) * np.cos(azimuth) + y = distances * np.sin(inclination) * np.sin(azimuth) + z = distances * np.cos(inclination) + return x, y, z + def convert_to_sphere( abscissa: np.ndarray, ordinates: np.ndarray, applicates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + + if not abscissa.shape == ordinates.shape == applicates.shape: + raise ShapeMismatchError + + distances = np.sqrt(abscissa**2 + ordinates**2 + applicates**2) + azimuth = np.arctan2(ordinates, abscissa) + inclinatian = np.zeros(distances.shape) + + not_zero = distances > 0 + devide = applicates[not_zero] / distances[not_zero] + inclinatian[not_zero] = np.arccos(devide) + + return distances, azimuth, inclinatian \ No newline at end of file diff --git a/solutions/sem02/lesson03/task3.py b/solutions/sem02/lesson03/task3.py index 477acd0ce..e5ebaa766 100644 --- a/solutions/sem02/lesson03/task3.py +++ b/solutions/sem02/lesson03/task3.py @@ -3,4 +3,18 @@ def get_extremum_indices( ordinates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray]: + n = len(ordinates) + if n < 3: + raise ValueError + + ordinates_safe = ordinates[1:-1] + + max_mask = (ordinates_safe > ordinates[:-2]) & (ordinates_safe > ordinates[2:]) + min_mask = (ordinates_safe < ordinates[:-2]) & (ordinates_safe < ordinates[2:]) + indexes = np.arange(1,n-1) + + ans = (indexes[min_mask], indexes[max_mask]) + + return ans + diff --git a/solutions/sem02/lesson04/requirements.txt b/solutions/sem02/lesson04/requirements.txt index 039f1d503..9ee038186 100644 --- a/solutions/sem02/lesson04/requirements.txt +++ b/solutions/sem02/lesson04/requirements.txt @@ -1,3 +1,3 @@ matplotlib==3.8.0 -numpy==1.26.1 +numpy opencv-python-headless==4.9.0.80 diff --git a/solutions/sem02/lesson04/task1.py b/solutions/sem02/lesson04/task1.py index 1b5526c1f..c3b956982 100644 --- a/solutions/sem02/lesson04/task1.py +++ b/solutions/sem02/lesson04/task1.py @@ -2,16 +2,53 @@ def pad_image(image: np.ndarray, pad_size: int) -> np.ndarray: - # ваш код - return image + if pad_size < 1: + raise ValueError + if len(image.shape) != 2: + padded = np.zeros( + (image.shape[0] + pad_size * 2, image.shape[1] + pad_size * 2, image.shape[2]), + dtype=image.dtype, + ) + padded[pad_size:-pad_size, pad_size:-pad_size, :] = image + else: + padded = np.zeros( + (image.shape[0] + pad_size * 2, image.shape[1] + pad_size * 2), dtype=image.dtype + ) + padded[pad_size:-pad_size, pad_size:-pad_size] = image + + return padded def blur_image( image: np.ndarray, kernel_size: int, ) -> np.ndarray: - # ваш код - return image + + N = image.shape[0] + M = image.shape[1] + + if kernel_size % 2 == 0: + raise ValueError + if kernel_size < 1: + raise ValueError + if kernel_size == 1: + return image + + blurred_image = np.zeros_like(image) + image = pad_image(image, kernel_size // 2) + + if len(image.shape) == 3: + for i in range(N): + for j in range(M): + blurred_image[i, j] = np.mean( + image[i : i + kernel_size, j : j + kernel_size], axis=(0, 1) + ) + else: + for i in range(N): + for j in range(M): + blurred_image[i, j] = np.mean(image[i : i + kernel_size, j : j + kernel_size]) + + return blurred_image if __name__ == "__main__": diff --git a/solutions/sem02/lesson04/task2.py b/solutions/sem02/lesson04/task2.py index be9a2288f..8b1016bd6 100644 --- a/solutions/sem02/lesson04/task2.py +++ b/solutions/sem02/lesson04/task2.py @@ -5,6 +5,25 @@ def get_dominant_color_info( image: np.ndarray[np.uint8], threshold: int = 5, ) -> tuple[np.uint8, float]: - # ваш код - return 0, 0 + if threshold < 1: + raise ValueError("threshold must be positive") + + colors = [0] * 256 + + for i in range(image.shape[0]): + for j in range(image.shape[1]): + colors[image[i, j]] += 1 + + max_sum = 0 + dominant_color = -1 + for i in range(256): + if colors[i] > 0: + start = max(0, i - threshold + 1) + end = min(255, i + threshold - 1) + now_sum = sum(colors[start : end + 1]) + if now_sum > max_sum: + max_sum = now_sum + dominant_color = i + + return np.uint8(dominant_color), max_sum / (image.shape[0] * image.shape[1]) diff --git a/solutions/sem02/lesson05/task1.py b/solutions/sem02/lesson05/task1.py index e9c7c3c56..4cc201c19 100644 --- a/solutions/sem02/lesson05/task1.py +++ b/solutions/sem02/lesson05/task1.py @@ -9,4 +9,13 @@ def can_satisfy_demand( costs: np.ndarray, resource_amounts: np.ndarray, demand_expected: np.ndarray, -) -> bool: ... +) -> bool: + + M, N = costs.shape + if len(resource_amounts) != M or len(demand_expected) != N: + raise ShapeMismatchError + + required = costs @ demand_expected + ans = required <= resource_amounts + ans = ans.all() + return ans diff --git a/solutions/sem02/lesson05/task2.py b/solutions/sem02/lesson05/task2.py index be1fb9d2b..dfd293595 100644 --- a/solutions/sem02/lesson05/task2.py +++ b/solutions/sem02/lesson05/task2.py @@ -8,4 +8,22 @@ class ShapeMismatchError(Exception): def get_projections_components( matrix: np.ndarray, vector: np.ndarray, -) -> tuple[np.ndarray | None, np.ndarray | None]: ... +) -> tuple[np.ndarray | None, np.ndarray | None]: + M, N = matrix.shape + + if M != N: + raise ShapeMismatchError + if N != len(vector): + raise ShapeMismatchError + + sign_det, _ = np.linalg.slogdet(matrix) + if sign_det == 0: + return (None, None) + + scalar_res = matrix @ vector + norm_matrix_2 = np.sum(matrix**2, axis=1) + ortogonal_proj_c = scalar_res / norm_matrix_2 + ortogonal_proj = ortogonal_proj_c[:, np.newaxis] * matrix + ortogonal_parts = vector - ortogonal_proj + + return (ortogonal_proj, ortogonal_parts) diff --git a/solutions/sem02/lesson05/task3.py b/solutions/sem02/lesson05/task3.py index 0c66906cb..819737eb4 100644 --- a/solutions/sem02/lesson05/task3.py +++ b/solutions/sem02/lesson05/task3.py @@ -9,4 +9,17 @@ def adaptive_filter( Vs: np.ndarray, Vj: np.ndarray, diag_A: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + + if Vs.shape[0] != Vj.shape[0]: + raise ShapeMismatchError + if len(diag_A) != Vj.shape[1]: + raise ShapeMismatchError + + print(diag_A) + Vjh = Vj.conj().T + VjA = Vj * diag_A + inn = np.eye(Vj.shape[1]) + Vjh @ VjA + temp = np.linalg.inv(inn) + y = Vs - Vj @ (temp @ (Vjh @ Vs)) + return y diff --git a/solutions/sem02/lesson07/class_distribution.png b/solutions/sem02/lesson07/class_distribution.png new file mode 100644 index 000000000..17529cab3 Binary files /dev/null and b/solutions/sem02/lesson07/class_distribution.png differ diff --git a/solutions/sem02/lesson07/scatter_with_diagrams.png b/solutions/sem02/lesson07/scatter_with_diagrams.png new file mode 100644 index 000000000..c55fbc451 Binary files /dev/null and b/solutions/sem02/lesson07/scatter_with_diagrams.png differ diff --git a/solutions/sem02/lesson07/task1.py b/solutions/sem02/lesson07/task1.py index 3a505d89b..23178d1d3 100644 --- a/solutions/sem02/lesson07/task1.py +++ b/solutions/sem02/lesson07/task1.py @@ -1,5 +1,6 @@ from typing import Any +import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import numpy as np @@ -13,7 +14,66 @@ def visualize_diagrams( ordinates: np.ndarray, diagram_type: Any, ) -> None: - # ваш код + + if abscissa.shape != ordinates.shape: + raise ShapeMismatchError + if diagram_type not in ("hist", "violin", "box"): + raise ValueError + + fig = plt.figure(figsize=(10, 10), facecolor="#cfd3cd") + + gs = gridspec.GridSpec( + 2, + 2, + width_ratios=[1, 4], + height_ratios=[4, 1], + hspace=0.1, + wspace=0.1, + left=0.08, + right=0.98, + top=0.98, + bottom=0.08, + ) + + ax_scatter = fig.add_subplot(gs[0, 1]) + ax_diag_x = fig.add_subplot(gs[1, 1]) + ax_diag_y = fig.add_subplot(gs[0, 0]) + ax_diag_x.grid(True) + ax_diag_y.grid(True) + ax_scatter.grid(True) + ax_scatter.tick_params(labelsize=16) + ax_diag_x.tick_params(labelsize=16) + ax_diag_y.tick_params(labelsize=16) + + ax_scatter.scatter(abscissa, ordinates, alpha=0.5, marker="o", s=100, color="sandybrown") + + if diagram_type == "hist": + ax_diag_x.hist( + abscissa, bins=30, color="cornflowerblue", edgecolor="dimgray", alpha=0.7, density=True + ) + ax_diag_y.hist( + ordinates, + bins=30, + orientation="horizontal", + color="cornflowerblue", + edgecolor="dimgray", + alpha=0.7, + density=True, + ) + + elif diagram_type == "violin": + ax_diag_x.violinplot(abscissa, vert=False) + ax_diag_y.violinplot(ordinates, vert=True) + + elif diagram_type == "box": + ax_diag_x.boxplot(abscissa, vert=False) + ax_diag_y.boxplot(ordinates, vert=True) + + ax_diag_x.invert_yaxis() + ax_diag_y.invert_xaxis() + + plt.savefig("scatter_with_diagrams.png") + pass diff --git a/solutions/sem02/lesson07/task2.py b/solutions/sem02/lesson07/task2.py index decd607ef..c67b03feb 100644 --- a/solutions/sem02/lesson07/task2.py +++ b/solutions/sem02/lesson07/task2.py @@ -1 +1,48 @@ -# ваш код (используйте функции или классы для решения данной задачи) +import json + +import matplotlib.pyplot as plt +import numpy as np + + +def get_data_for_plot(file_path: str) -> tuple[np.ndarray, np.ndarray]: + with open(file_path, "r") as f: + data = json.load(f) + before = np.array(data["before"]) + after = np.array(data["after"]) + return before, after + + +def sum_classes(before: np.ndarray, after: np.ndarray, classes=["I", "II", "III", "IV"]): + before_sum = {} + after_sum = {} + for cl in classes: + mask = before == cl + before_sum[cl] = np.sum(mask) + mask = after == cl + after_sum[cl] = np.sum(mask) + return before_sum, after_sum + + +classes = ["I", "II", "III", "IV"] +before, after = get_data_for_plot("data/medic_data.json") +before_sum, after_sum = sum_classes(before, after) + +fig, ax = plt.subplots(figsize=(9, 6)) +x = np.arange(len(classes)) +ax.tick_params(labelsize=16) + +width = 0.35 +bars1 = ax.bar(x - width / 2, before_sum.values(), width, label="before", color="cornflowerblue") +bars2 = ax.bar(x + width / 2, after_sum.values(), width, label="after", color="sandybrown") + +ax.set_xlabel("Mitral disease stage", fontsize=16) +ax.set_ylabel("Amount of people", fontsize=16) +ax.set_title("Mitral disease stages", fontsize=16) +ax.grid(axis="y", color="#cacaca", linewidth=0.8) +ax.set_axisbelow(True) +ax.set_xticks(x) +ax.set_xticklabels(classes) +ax.legend(fontsize=16) + +plt.savefig("class_distribution.png") +plt.show() diff --git a/solutions/sem02/lesson08/labyrinth.gif b/solutions/sem02/lesson08/labyrinth.gif new file mode 100644 index 000000000..9c239efe3 Binary files /dev/null and b/solutions/sem02/lesson08/labyrinth.gif differ diff --git a/solutions/sem02/lesson08/modulated_signal.gif b/solutions/sem02/lesson08/modulated_signal.gif new file mode 100644 index 000000000..3dd7eced5 Binary files /dev/null and b/solutions/sem02/lesson08/modulated_signal.gif differ diff --git a/solutions/sem02/lesson08/task1.py b/solutions/sem02/lesson08/task1.py index 89f88572f..86973b69e 100644 --- a/solutions/sem02/lesson08/task1.py +++ b/solutions/sem02/lesson08/task1.py @@ -2,33 +2,71 @@ import matplotlib.pyplot as plt import numpy as np - from IPython.display import HTML from matplotlib.animation import FuncAnimation +def modulated_signal_math(modulation, fc, t): + if modulation is None: + return np.sin(2 * np.pi * fc * t) + else: + return modulation(t) * np.sin(2 * np.pi * fc * t) + + +def update(frame, modulation, fc, plot_duration, animation_step, time_step, line, ax): + t_start = frame * animation_step + t_end = t_start + plot_duration + ax.set_xlim(t_start, t_end) + t = np.linspace(t_start, t_end, int(plot_duration / time_step)) + signal = modulated_signal_math(modulation, fc, t) + line.set_data(t, signal) + return (line,) + + def create_modulation_animation( - modulation, - fc, - num_frames, - plot_duration, - time_step=0.001, - animation_step=0.01, - save_path="" + modulation, fc, num_frames, plot_duration, time_step=0.001, animation_step=0.01, save_path="" ) -> FuncAnimation: - # ваш код - return FuncAnimation() + fig = plt.figure(figsize=(10, 6)) + ax = plt.axes(xlim=(0, plot_duration)) + (line,) = ax.plot([], [], lw=2) + ax.set_ylim(-1.5, 1.5) + ax.set_xlabel("Время (с)", fontsize=14) + ax.set_ylabel("Амплитуда", fontsize=14) + fig.suptitle("Модулированный сигнал", fontsize=16) + + animation = FuncAnimation( + fig, + partial( + update, + modulation=modulation, + fc=fc, + plot_duration=plot_duration, + animation_step=animation_step, + time_step=time_step, + line=line, + ax=ax, + ), + frames=num_frames, + interval=animation_step * 1000, + blit=False, + ) + + if save_path: + animation.save(save_path, writer="pillow", fps=30) + + return animation if __name__ == "__main__": + def modulation_function(t): - return np.cos(t * 6) + return np.cos(t * 6) - num_frames = 100 - plot_duration = np.pi / 2 - time_step = 0.001 - animation_step = np.pi / 200 - fc = 50 + num_frames = 100 + plot_duration = np.pi / 2 + time_step = 0.001 + animation_step = np.pi / 200 + fc = 50 save_path_with_modulation = "modulated_signal.gif" animation = create_modulation_animation( @@ -38,6 +76,6 @@ def modulation_function(t): plot_duration=plot_duration, time_step=time_step, animation_step=animation_step, - save_path=save_path_with_modulation + save_path=save_path_with_modulation, ) - HTML(animation.to_jshtml()) \ No newline at end of file + HTML(animation.to_jshtml()) diff --git a/solutions/sem02/lesson08/task2.py b/solutions/sem02/lesson08/task2.py index b677c0702..3c0e7934c 100644 --- a/solutions/sem02/lesson08/task2.py +++ b/solutions/sem02/lesson08/task2.py @@ -1,53 +1,143 @@ +from collections import deque from functools import partial import matplotlib.pyplot as plt import numpy as np - from IPython.display import HTML from matplotlib.animation import FuncAnimation +def run_wave(maze: np.ndarray, start: tuple[int, int], end: tuple[int, int]): + + distances = np.full_like(maze, -1) + distances[start] = 0 + + vis_matrix = maze.copy() + vis_matrix[start] = 2 + + frames = [vis_matrix.copy()] + queue = deque([start]) + + directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + max_x, max_y = maze.shape + + while queue: + current_cell = queue.popleft() + + if current_cell == end: + break + x, y = current_cell + + for dx, dy in directions: + nx, ny = x + dx, y + dy + if 0 <= nx < max_x and 0 <= ny < max_y: + if maze[nx, ny] == 1 and distances[nx, ny] == -1: + distances[nx, ny] = distances[x, y] + 1 + queue.append((nx, ny)) + vis_matrix[nx, ny] = 2 + frames.append(vis_matrix.copy()) + + return distances, frames, vis_matrix + + +def restore_path( + distances: np.ndarray, start: tuple[int, int], end: tuple[int, int], final_vis: np.ndarray +): + + frames = [] + + if distances[end] == -1: + return frames + + current_cell = end + directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + max_x, max_y = distances.shape + + vis_matrix = final_vis.copy() + + while current_cell != start: + vis_matrix[current_cell] = 3 + frames.append(vis_matrix.copy()) + + x, y = current_cell + for dx, dy in directions: + nx, ny = x + dx, y + dy + if 0 <= nx < max_x and 0 <= ny < max_y and distances[nx, ny] == distances[x, y] - 1: + vis_matrix[nx, ny] = 3 + current_cell = (nx, ny) + break + + vis_matrix[start] = 3 + frames.append(vis_matrix.copy()) + + return frames + + +def update(frame, frames, img): + img.set_data(frames[frame]) + return [img] def animate_wave_algorithm( - maze: np.ndarray, - start: tuple[int, int], - end: tuple[int, int], - save_path: str = "" -) -> FuncAnimation: - # ваш код - return FuncAnimation() + maze: np.ndarray, start: tuple[int, int], end: tuple[int, int], save_path: str = "" +) -> FuncAnimation: + + distances, wave_frames, final_wave_vis = run_wave(maze, start, end) + path_frames = restore_path(distances, start, end, final_wave_vis) + + all_frames = wave_frames + path_frames + title = "Волновой алгоритм" if distances[end] != -1 else "Путь не найден!" + + fig = plt.figure() + ax = plt.axes() + fig.set_size_inches(8, 8) + ax.set_title(title, fontsize=16) + + ax.set_xticks([]) + ax.set_yticks([]) + + img = ax.imshow(all_frames[0], cmap = "viridis", vmin=0, vmax=3) + + animation = FuncAnimation( + fig, + partial(update, frames=all_frames, img=img), + frames=len(all_frames), + interval=200, + blit=True, + ) + + if save_path: + animation.save(save_path, writer="pillow", fps=10) + + return animation + if __name__ == "__main__": - # Пример 1 - maze = np.array([ - [0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0], - [1, 1, 0, 1, 0, 1, 0], - [0, 0, 1, 1, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0], - [1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0], - ]) + maze = np.array( + [ + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 0], + [1, 1, 0, 1, 0, 1, 0], + [0, 0, 1, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0], + [1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + ] + ) start = (2, 0) end = (5, 0) - save_path = "labyrinth.gif" # Укажите путь для сохранения анимации + save_path = "labyrinth.gif" animation = animate_wave_algorithm(maze, start, end, save_path) HTML(animation.to_jshtml()) - - # Пример 2 - + maze_path = "./data/maze.npy" loaded_maze = np.load(maze_path) - # можете поменять, если захотите запустить из других точек start = (2, 0) end = (5, 0) loaded_save_path = "loaded_labyrinth.gif" loaded_animation = animate_wave_algorithm(loaded_maze, start, end, loaded_save_path) HTML(loaded_animation.to_jshtml()) - - \ No newline at end of file