diff --git a/homeworks/sem01/hw1/backoff.py b/homeworks/sem01/hw1/backoff.py index 696ffa73a..a2d8aa70a 100644 --- a/homeworks/sem01/hw1/backoff.py +++ b/homeworks/sem01/hw1/backoff.py @@ -1,5 +1,5 @@ -from random import uniform -from time import sleep +# from random import uniform +# from time import sleep from typing import ( Callable, ParamSpec, diff --git a/labyrinth.gif b/labyrinth.gif new file mode 100644 index 000000000..c9da25d16 Binary files /dev/null and b/labyrinth.gif differ diff --git a/loaded_labyrinth.gif b/loaded_labyrinth.gif new file mode 100644 index 000000000..f840bfbc4 Binary files /dev/null and b/loaded_labyrinth.gif differ diff --git a/modulated_signal.gif b/modulated_signal.gif new file mode 100644 index 000000000..60f708b3b Binary files /dev/null and b/modulated_signal.gif differ diff --git a/requirements-ci.txt b/requirements-ci.txt index 581b008d7..9f6047f2d 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,6 +1,6 @@ matplotlib==3.8.0 -numpy==1.26.1 -pandas==2.2.2 +numpy +pandas pytest==8.4.2 pytest-cov==7.0.0 diff --git a/solutions/sem01/__init__.py b/solutions/sem01/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/solutions/sem01/lesson02/__init__.py b/solutions/sem01/lesson02/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/solutions/sem01/lesson02/task1.py b/solutions/sem01/lesson02/task1.py deleted file mode 100644 index c782dcd88..000000000 --- a/solutions/sem01/lesson02/task1.py +++ /dev/null @@ -1,4 +0,0 @@ -def get_factorial(num: int) -> int: - factorial = 1 - # ваш код - return factorial diff --git a/solutions/sem01/lesson02/task2.py b/solutions/sem01/lesson02/task2.py deleted file mode 100644 index b91420c56..000000000 --- a/solutions/sem01/lesson02/task2.py +++ /dev/null @@ -1,4 +0,0 @@ -def get_doubled_factorial(num: int) -> int: - factorial = 1 - # ваш код - return factorial diff --git a/solutions/sem01/lesson02/task3.py b/solutions/sem01/lesson02/task3.py deleted file mode 100644 index ee2a84ecf..000000000 --- a/solutions/sem01/lesson02/task3.py +++ /dev/null @@ -1,4 +0,0 @@ -def get_amount_of_ways_to_climb(stair_amount: int) -> int: - step_prev, step_curr = 1, 1 - # ваш код - return step_curr diff --git a/solutions/sem01/lesson02/task4.py b/solutions/sem01/lesson02/task4.py deleted file mode 100644 index 45ff4bb42..000000000 --- a/solutions/sem01/lesson02/task4.py +++ /dev/null @@ -1,4 +0,0 @@ -def get_multiplications_amount(num: int) -> int: - multiplications_amount = 0 - # ваш код - return multiplications_amount diff --git a/solutions/sem01/lesson02/task5.py b/solutions/sem01/lesson02/task5.py deleted file mode 100644 index 8fb9a048d..000000000 --- a/solutions/sem01/lesson02/task5.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_gcd(num1: int, num2: int) -> int: - # ваш код - return num1 diff --git a/solutions/sem01/lesson02/task6.py b/solutions/sem01/lesson02/task6.py deleted file mode 100644 index bec4b6cd9..000000000 --- a/solutions/sem01/lesson02/task6.py +++ /dev/null @@ -1,4 +0,0 @@ -def get_sum_of_prime_divisors(num: int) -> int: - sum_of_divisors = 0 - # ваш код - return sum_of_divisors diff --git a/solutions/sem01/lesson02/task7.py b/solutions/sem01/lesson02/task7.py deleted file mode 100644 index 4b2d73beb..000000000 --- a/solutions/sem01/lesson02/task7.py +++ /dev/null @@ -1,5 +0,0 @@ -def is_palindrome(num: int) -> bool: - num_reversed = 0 - num_origin = num - # ваш код - return num_origin == num_reversed diff --git a/solutions/sem01/lesson03/task1.py b/solutions/sem01/lesson03/task1.py deleted file mode 100644 index f1d8fe26b..000000000 --- a/solutions/sem01/lesson03/task1.py +++ /dev/null @@ -1,3 +0,0 @@ -def flip_bits_in_range(num: int, left_bit: int, right_bit: int) -> int: - # ваш код - return num \ No newline at end of file diff --git a/solutions/sem01/lesson03/task2.py b/solutions/sem01/lesson03/task2.py deleted file mode 100644 index a3a738c2a..000000000 --- a/solutions/sem01/lesson03/task2.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_cube_root(n: float, eps: float) -> float: - # ваш код - return n \ No newline at end of file diff --git a/solutions/sem01/lesson03/task3.py b/solutions/sem01/lesson03/task3.py deleted file mode 100644 index 5e91a6ac5..000000000 --- a/solutions/sem01/lesson03/task3.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_nth_digit(num: int) -> int: - # ваш код - return 0 diff --git a/solutions/sem01/lesson04/task1.py b/solutions/sem01/lesson04/task1.py deleted file mode 100644 index 47384423a..000000000 --- a/solutions/sem01/lesson04/task1.py +++ /dev/null @@ -1,3 +0,0 @@ -def is_arithmetic_progression(lst: list[list[int]]) -> bool: - # ваш код - return False \ No newline at end of file diff --git a/solutions/sem01/lesson04/task2.py b/solutions/sem01/lesson04/task2.py deleted file mode 100644 index 4591d0a3e..000000000 --- a/solutions/sem01/lesson04/task2.py +++ /dev/null @@ -1,3 +0,0 @@ -def merge_intervals(intervals: list[list[int, int]]) -> list[list[int, int]]: - # ваш код - return [[0,0]] \ No newline at end of file diff --git a/solutions/sem01/lesson04/task3.py b/solutions/sem01/lesson04/task3.py deleted file mode 100644 index 7253f6cbd..000000000 --- a/solutions/sem01/lesson04/task3.py +++ /dev/null @@ -1,3 +0,0 @@ -def find_single_number(nums: list[int]) -> int: - # ваш код - return 0 diff --git a/solutions/sem01/lesson04/task4.py b/solutions/sem01/lesson04/task4.py deleted file mode 100644 index b21bc5a39..000000000 --- a/solutions/sem01/lesson04/task4.py +++ /dev/null @@ -1,3 +0,0 @@ -def move_zeros_to_end(nums: list[int]) -> list[int]: - # ваш код - return 0 \ No newline at end of file diff --git a/solutions/sem01/lesson04/task5.py b/solutions/sem01/lesson04/task5.py deleted file mode 100644 index 02d7742bb..000000000 --- a/solutions/sem01/lesson04/task5.py +++ /dev/null @@ -1,3 +0,0 @@ -def find_row_with_most_ones(matrix: list[list[int]]) -> int: - # ваш код - return 0 \ No newline at end of file diff --git a/solutions/sem01/lesson04/task6.py b/solutions/sem01/lesson04/task6.py deleted file mode 100644 index 16df27ca6..000000000 --- a/solutions/sem01/lesson04/task6.py +++ /dev/null @@ -1,3 +0,0 @@ -def count_cycles(arr: list[int]) -> int: - # ваш код - return 0 \ No newline at end of file diff --git a/solutions/sem01/lesson05/__init__.py b/solutions/sem01/lesson05/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/solutions/sem01/lesson05/task1.py b/solutions/sem01/lesson05/task1.py deleted file mode 100644 index 9a17211e5..000000000 --- a/solutions/sem01/lesson05/task1.py +++ /dev/null @@ -1,3 +0,0 @@ -def is_palindrome(text: str) -> bool: - # ваш код - return False \ No newline at end of file diff --git a/solutions/sem01/lesson05/task2.py b/solutions/sem01/lesson05/task2.py deleted file mode 100644 index 367503802..000000000 --- a/solutions/sem01/lesson05/task2.py +++ /dev/null @@ -1,3 +0,0 @@ -def are_anagrams(word1: str, word2: str) -> bool: - # ваш код - return False \ No newline at end of file diff --git a/solutions/sem01/lesson05/task3.py b/solutions/sem01/lesson05/task3.py deleted file mode 100644 index e368e2f49..000000000 --- a/solutions/sem01/lesson05/task3.py +++ /dev/null @@ -1,3 +0,0 @@ -def is_punctuation(text: str) -> bool: - # ваш код - return False diff --git a/solutions/sem01/lesson05/task4.py b/solutions/sem01/lesson05/task4.py deleted file mode 100644 index 4c4e9086e..000000000 --- a/solutions/sem01/lesson05/task4.py +++ /dev/null @@ -1,3 +0,0 @@ -def unzip(compress_text: str) -> str: - # ваш код - return compress_text \ No newline at end of file diff --git a/solutions/sem01/lesson05/task5.py b/solutions/sem01/lesson05/task5.py deleted file mode 100644 index 076c5bb6c..000000000 --- a/solutions/sem01/lesson05/task5.py +++ /dev/null @@ -1,3 +0,0 @@ -def reg_validator(reg_expr: str, text: str) -> bool: - # ваш код - return False \ No newline at end of file diff --git a/solutions/sem01/lesson05/task6.py b/solutions/sem01/lesson05/task6.py deleted file mode 100644 index 1b914ada7..000000000 --- a/solutions/sem01/lesson05/task6.py +++ /dev/null @@ -1,3 +0,0 @@ -def simplify_path(path: str) -> str: - # ваш код - return path \ No newline at end of file diff --git a/solutions/sem01/lesson06/task1.py b/solutions/sem01/lesson06/task1.py deleted file mode 100644 index 2d1e30e96..000000000 --- a/solutions/sem01/lesson06/task1.py +++ /dev/null @@ -1,3 +0,0 @@ -def int_to_roman(num: int) -> str: - # ваш код - return "" \ No newline at end of file diff --git a/solutions/sem01/lesson06/task2.py b/solutions/sem01/lesson06/task2.py deleted file mode 100644 index f535b5a0c..000000000 --- a/solutions/sem01/lesson06/task2.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_len_of_longest_substring(text: str) -> int: - # ваш код - return 0 \ No newline at end of file diff --git a/solutions/sem01/lesson06/task3.py b/solutions/sem01/lesson06/task3.py deleted file mode 100644 index 7449a1e72..000000000 --- a/solutions/sem01/lesson06/task3.py +++ /dev/null @@ -1,7 +0,0 @@ -def is_there_any_good_subarray( - nums: list[int], - k: int, -) -> bool: - - # ваш код - return False diff --git a/solutions/sem01/lesson06/task4.py b/solutions/sem01/lesson06/task4.py deleted file mode 100644 index 5b75a110c..000000000 --- a/solutions/sem01/lesson06/task4.py +++ /dev/null @@ -1,3 +0,0 @@ -def count_unique_words(text: str) -> int: - # ваш код - return 0 \ No newline at end of file diff --git a/solutions/sem01/lesson08/__init__.py b/solutions/sem01/lesson08/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/solutions/sem01/lesson08/task1.py b/solutions/sem01/lesson08/task1.py deleted file mode 100644 index 4390f6c84..000000000 --- a/solutions/sem01/lesson08/task1.py +++ /dev/null @@ -1,5 +0,0 @@ -from typing import Callable - -def make_averager(accumulation_period: int) -> Callable[[float], float]: - # ваш код - pass \ No newline at end of file diff --git a/solutions/sem01/lesson08/task2.py b/solutions/sem01/lesson08/task2.py deleted file mode 100644 index 6e4af8707..000000000 --- a/solutions/sem01/lesson08/task2.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Callable, TypeVar - -T = TypeVar("T") - -def collect_statistic( - statistics: dict[str, list[float, int]] -) -> Callable[[T], T]: - - # ваш код - pass \ No newline at end of file diff --git a/solutions/sem01/lesson11/__init__.py b/solutions/sem01/lesson11/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/solutions/sem01/lesson11/task1.py b/solutions/sem01/lesson11/task1.py deleted file mode 100644 index 6a15f31fb..000000000 --- a/solutions/sem01/lesson11/task1.py +++ /dev/null @@ -1,10 +0,0 @@ -class Vector2D: - def conj(self) -> "Vector2D": - # ваш код - return Vector2D() - - def get_angle(self, other: "Vector2D") -> float: - # ваш код - return 0 - - # ваш код diff --git a/solutions/sem01/lesson12/__init__.py b/solutions/sem01/lesson12/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/solutions/sem01/lesson12/task1.py b/solutions/sem01/lesson12/task1.py deleted file mode 100644 index d1bb828c1..000000000 --- a/solutions/sem01/lesson12/task1.py +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Any, Generator, Iterable - - -def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: - # ваш код - ... diff --git a/solutions/sem01/lesson12/task2.py b/solutions/sem01/lesson12/task2.py deleted file mode 100644 index 3ad802ee7..000000000 --- a/solutions/sem01/lesson12/task2.py +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Any, Generator, Iterable - - -def circle(iterable: Iterable) -> Generator[Any, None, None]: - # ваш код - ... diff --git a/solutions/sem01/lesson12/task3.py b/solutions/sem01/lesson12/task3.py deleted file mode 100644 index 64c112ccc..000000000 --- a/solutions/sem01/lesson12/task3.py +++ /dev/null @@ -1,12 +0,0 @@ -import sys - - -class FileOut: - def __init__( - self, - path_to_file: str, - ) -> None: - # ваш код - ... - - # ваш код diff --git a/solutions/sem02/lesson03/task1.py b/solutions/sem02/lesson03/task1.py index 2c3fc0b58..8dcc06216 100644 --- a/solutions/sem02/lesson03/task1.py +++ b/solutions/sem02/lesson03/task1.py @@ -8,13 +8,26 @@ 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 lhs + rhs -def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: ... +def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: + return 3 * (abscissa**2) + 2 * abscissa + 1 def get_mutual_l2_distances_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if lhs.shape[1] != rhs.shape[1]: + raise ShapeMismatchError + """ + [[[1, 1]]] [[[0, 0]] + [[[1, 1]]] [[0, 0]]] + [[[1, 1]]] + """ + arr = lhs[:, np.newaxis, :] - rhs[np.newaxis, :, :] + return np.sqrt(np.sum(arr**2, axis=2)) diff --git a/solutions/sem02/lesson03/task2.py b/solutions/sem02/lesson03/task2.py index fc823c1d6..ba6356f0a 100644 --- a/solutions/sem02/lesson03/task2.py +++ b/solutions/sem02/lesson03/task2.py @@ -9,11 +9,28 @@ 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 + + r = np.sqrt(abscissa**2 + ordinates**2 + applicates**2) + az = np.arctan2(ordinates, abscissa) + + inc = np.zeros(r.shape) + inc[r != 0] = np.arccos(applicates[r != 0] / r[r != 0]) + + return r, az, inc diff --git a/solutions/sem02/lesson03/task3.py b/solutions/sem02/lesson03/task3.py index 477acd0ce..7a073a49e 100644 --- a/solutions/sem02/lesson03/task3.py +++ b/solutions/sem02/lesson03/task3.py @@ -3,4 +3,17 @@ def get_extremum_indices( ordinates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray]: + if ordinates.size < 3: + raise ValueError + + left = ordinates[:-2] + n = ordinates[1:-1] + right = ordinates[2:] + + ordinates = np.arange(1, len(ordinates) - 1) + + mins = ordinates[((n < left) & (n < right))] + maxs = ordinates[((n > left) & (n > right))] + + return mins, maxs diff --git a/solutions/sem02/lesson04/task1.py b/solutions/sem02/lesson04/task1.py index 1b5526c1f..6ea2f5e10 100644 --- a/solutions/sem02/lesson04/task1.py +++ b/solutions/sem02/lesson04/task1.py @@ -2,16 +2,68 @@ def pad_image(image: np.ndarray, pad_size: int) -> np.ndarray: - # ваш код - return image + if pad_size < 1: + raise ValueError + + new_sh = list(image.shape) + new_sh[0] += 2 * pad_size + new_sh[1] += 2 * pad_size + + with_pad = np.zeros(new_sh, dtype=image.dtype) + + with_pad[ + pad_size:-pad_size, + pad_size:-pad_size, + ] = image + return with_pad def blur_image( image: np.ndarray, kernel_size: int, ) -> np.ndarray: - # ваш код - return image + if kernel_size < 1 or kernel_size % 2 == 0: + raise ValueError + + if kernel_size == 1: + return image + + pad_size = kernel_size // 2 + with_pad = pad_image(image, pad_size) + h, w = image.shape[:2] + + csy = np.cumsum(with_pad, axis=0) + y_sum = np.zeros((h, with_pad.shape[1]) + image.shape[2:]) + + y_sum[0,] = csy[kernel_size - 1,] + y_sum[1:,] = csy[kernel_size:,] - csy[:-kernel_size,] + + csx = np.cumsum(y_sum, axis=1) + xy_sum = np.zeros((h, w) + image.shape[2:]) + + xy_sum[ + :, + 0, + ] = csx[ + :, + kernel_size - 1, + ] + xy_sum[ + :, + 1:, + ] = ( + csx[ + :, + kernel_size:, + ] + - csx[ + :, + :-kernel_size, + ] + ) + + blur_im = xy_sum / (kernel_size**2) + return blur_im.astype(image.dtype) if __name__ == "__main__": diff --git a/solutions/sem02/lesson04/task2.py b/solutions/sem02/lesson04/task2.py index be9a2288f..074d806cf 100644 --- a/solutions/sem02/lesson04/task2.py +++ b/solutions/sem02/lesson04/task2.py @@ -5,6 +5,30 @@ def get_dominant_color_info( image: np.ndarray[np.uint8], threshold: int = 5, ) -> tuple[np.uint8, float]: - # ваш код + if threshold < 1: + raise ValueError("threshold must be positive") - return 0, 0 + flat = image.flatten() + + counts = np.zeros(256, dtype=np.int64) + colors = np.arange(256, dtype=np.int32).reshape(-1, 1) + + np.add.at(counts, flat, 1) + + valid_colors = counts > 0 + + diffs = np.abs(colors - colors.T) + mask = diffs < threshold + + groups = np.sum(mask * counts, axis=1) + + scores = groups * (image.size + 1) + counts + + scores[~valid_colors] = -1 + + ans_ind = np.argmax(scores) + + ans = np.uint8(ans_ind) + percent = float(groups[ans_ind] / image.size) + + return ans, percent diff --git a/solutions/sem02/lesson05/task1.py b/solutions/sem02/lesson05/task1.py index e9c7c3c56..fb379cc35 100644 --- a/solutions/sem02/lesson05/task1.py +++ b/solutions/sem02/lesson05/task1.py @@ -9,4 +9,15 @@ def can_satisfy_demand( costs: np.ndarray, resource_amounts: np.ndarray, demand_expected: np.ndarray, -) -> bool: ... +) -> bool: + if ( + costs.ndim != 2 + or resource_amounts.ndim != 1 + or demand_expected.ndim != 1 + or costs.shape[0] != resource_amounts.shape[0] + or costs.shape[1] != demand_expected.shape[0] + ): + raise ShapeMismatchError + + res = costs @ demand_expected + return bool(np.all(res <= resource_amounts)) diff --git a/solutions/sem02/lesson05/task2.py b/solutions/sem02/lesson05/task2.py index be1fb9d2b..7b52d30c3 100644 --- a/solutions/sem02/lesson05/task2.py +++ b/solutions/sem02/lesson05/task2.py @@ -8,4 +8,18 @@ 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]: + if matrix.ndim != 2 or matrix.shape[0] != matrix.shape[1] or matrix.shape[1] != vector.shape[0]: + raise ShapeMismatchError + + if np.linalg.matrix_rank(matrix) < matrix.shape[0]: + return None, None + + dots = matrix @ vector + norms = np.sum(matrix**2, axis=1) + sc = dots / norms + + proj = sc[:, np.newaxis] * matrix + ort = vector[np.newaxis, :] - proj + + return proj, ort diff --git a/solutions/sem02/lesson05/task3.py b/solutions/sem02/lesson05/task3.py index 0c66906cb..763e8a0ef 100644 --- a/solutions/sem02/lesson05/task3.py +++ b/solutions/sem02/lesson05/task3.py @@ -9,4 +9,22 @@ def adaptive_filter( Vs: np.ndarray, Vj: np.ndarray, diag_A: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if ( + Vs.ndim != 2 + or Vj.ndim != 2 + or diag_A.ndim != 1 + or Vs.shape[0] != Vj.shape[0] + or Vj.shape[1] != diag_A.shape[0] + ): + raise ShapeMismatchError + + K = Vj.shape[1] + + VjH = Vj.conj().T + A = np.diag(diag_A) + + E = np.eye(K, dtype=Vj.dtype) + + inner = E + VjH @ Vj @ A + return Vs - Vj @ np.linalg.inv(inner) @ (VjH @ Vs) diff --git a/solutions/sem02/lesson07/task1.py b/solutions/sem02/lesson07/task1.py index 3a505d89b..72fc2f8e4 100644 --- a/solutions/sem02/lesson07/task1.py +++ b/solutions/sem02/lesson07/task1.py @@ -13,8 +13,76 @@ def visualize_diagrams( ordinates: np.ndarray, diagram_type: Any, ) -> None: - # ваш код - pass + if abscissa.shape != ordinates.shape: + raise ShapeMismatchError + + if diagram_type not in {"hist", "violin", "box"}: + raise ValueError + + space = 0.2 + figure = plt.figure(figsize=(8, 8)) + grid = plt.GridSpec(4, 4, wspace=space, hspace=space) + + axis_scatter = figure.add_subplot(grid[:-1, 1:]) + axis_vert = figure.add_subplot(grid[:-1, 0], sharey=axis_scatter) + axis_hor = figure.add_subplot(grid[-1, 1:], sharex=axis_scatter) + + axis_scatter.scatter(abscissa, ordinates, color="mediumpurple", alpha=0.5) + + if diagram_type == "hist": + axis_hor.hist( + abscissa, + bins=50, + color="mediumpurple", + density=True, + alpha=0.5, + ) + axis_vert.hist( + ordinates, + bins=50, + color="mediumpurple", + orientation="horizontal", + density=True, + alpha=0.5, + ) + + axis_hor.invert_yaxis() + axis_vert.invert_xaxis() + + elif diagram_type == "violin": + parts_hor = axis_hor.violinplot(abscissa, vert=False, showmedians=True) + parts_vert = axis_vert.violinplot(ordinates, vert=True, showmedians=True) + for parts in (parts_hor, parts_vert): + for body in parts["bodies"]: + body.set_facecolor("cornflowerblue") + body.set_edgecolor("blue") + for key in parts: + if key != "bodies": + parts[key].set_edgecolor("cornflowerblue") + axis_hor.set_yticks([]) + axis_vert.set_xticks([]) + + elif diagram_type == "box": + box_props = dict(facecolor="lightsteelblue") + median_props = dict(color="k") + axis_hor.boxplot( + abscissa, + vert=False, + patch_artist=True, + boxprops=box_props, + medianprops=median_props, + ) + axis_vert.boxplot( + ordinates, + vert=True, + patch_artist=True, + boxprops=box_props, + medianprops=median_props, + ) + axis_hor.set_yticks([]) + axis_vert.set_xticks([]) + + plt.show() if __name__ == "__main__": diff --git a/solutions/sem02/lesson07/task2.py b/solutions/sem02/lesson07/task2.py index decd607ef..b2be1c0db 100644 --- a/solutions/sem02/lesson07/task2.py +++ b/solutions/sem02/lesson07/task2.py @@ -1 +1,53 @@ -# ваш код (используйте функции или классы для решения данной задачи) +import json + +import matplotlib.pyplot as plt +import numpy as np + + +def count_by_stage(records: list) -> np.ndarray: + stages = ["I", "II", "III", "IV"] + counts = np.zeros(len(stages), dtype=int) + for record in records: + counts[stages.index(record)] += 1 + return counts + + +def build_bar_chart( + before_counts: np.ndarray, + after_counts: np.ndarray, +) -> None: + figure, axis = plt.subplots(figsize=(16, 9)) + x = np.arange(4) + + axis.set_title( + "Mitral disease stages", + fontsize=17, + fontweight="bold", + c="dimgray", + ) + axis.set_ylabel( + "amount of people", + fontsize=14, + fontweight="bold", + c="dimgray", + ) + axis.set_xticks(x, labels=["I", "II", "III", "IV"], weight="bold") + axis.tick_params(axis="x", labelsize=14, labelcolor="dimgray") + + axis.bar( + x - 0.2, before_counts, width=0.35, color="mediumpurple", edgecolor="purple", label="before" + ) + axis.bar(x + 0.2, after_counts, width=0.35, color="plum", edgecolor="purple", label="after") + axis.legend(fontsize=14) + + figure.savefig("mitral_disease_stages.png") + plt.show() + + +if __name__ == "__main__": + plt.style.use("ggplot") + with open("lesson07/data/medic_data.json") as f: + data = json.load(f) + before_counts = count_by_stage(data["before"]) + after_counts = count_by_stage(data["after"]) + build_bar_chart(before_counts, after_counts) diff --git a/solutions/sem02/lesson08/task1.py b/solutions/sem02/lesson08/task1.py index 89f88572f..92e5ab06b 100644 --- a/solutions/sem02/lesson08/task1.py +++ b/solutions/sem02/lesson08/task1.py @@ -7,28 +7,73 @@ from matplotlib.animation import FuncAnimation +def compute_signal(time: np.ndarray, modulation, fc: float) -> np.ndarray: + carrier = np.sin(2 * np.pi * fc * time) + if modulation is None: + return carrier + return modulation(time) * carrier + + +def update_frame( + frame_id: int, + *, + line: plt.Line2D, + signal: np.ndarray, + window_size: int, + step_size: int, +) -> tuple[plt.Line2D]: + start = frame_id * step_size + line.set_ydata(signal[start : start + window_size]) + return (line,) + + def create_modulation_animation( - modulation, - fc, - num_frames, - plot_duration, - time_step=0.001, + modulation, + fc, + num_frames, + plot_duration, + time_step=0.001, animation_step=0.01, - save_path="" + save_path="", ) -> FuncAnimation: - # ваш код - return FuncAnimation() + total_duration = (num_frames - 1) * animation_step + plot_duration + time = np.linspace(0, total_duration, int(total_duration / time_step)) + signal = compute_signal(time, modulation, fc) + + window_size = int(plot_duration / time_step) + step_size = int(animation_step / time_step) + + figure, axis = plt.subplots(figsize=(16, 9)) + axis.set_xlim(time[0], time[window_size - 1]) + axis.set_ylim(signal.min() - 0.1, signal.max() + 0.1) + + line, *_ = axis.plot(time[:window_size], signal[:window_size], c="royalblue") + + animation = FuncAnimation( + figure, + partial( + update_frame, line=line, signal=signal, window_size=window_size, step_size=step_size + ), + frames=num_frames, + interval=50, + blit=True, + ) + + if save_path: + animation.save(save_path, writer="pillow", fps=24) + + 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( diff --git a/solutions/sem02/lesson08/task2.py b/solutions/sem02/lesson08/task2.py index b677c0702..e4f88e61b 100644 --- a/solutions/sem02/lesson08/task2.py +++ b/solutions/sem02/lesson08/task2.py @@ -7,16 +7,184 @@ from matplotlib.animation import FuncAnimation +from functools import partial + +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.animation import FuncAnimation + + +def run_wave( + maze: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], +) -> tuple[list[np.ndarray], list[tuple[int, int]]]: + rows, cols = maze.shape + wave = maze.astype(float) - 2 + wave[start] = 0 + + frames = [wave.copy()] + frontier = [start] + found = False + + while frontier and not found: + next_frontier = [] + for r, c in frontier: + for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + nr, nc = r + dr, c + dc + if 0 <= nr < rows and 0 <= nc < cols and wave[nr, nc] == -1: + wave[nr, nc] = wave[r, c] + 1 + next_frontier.append((nr, nc)) + if (nr, nc) == end: + found = True + frontier = next_frontier + frames.append(wave.copy()) + + path = backtrack(wave, start, end) if found else [] + return frames, path + + +def backtrack( + wave: np.ndarray, + start: tuple[int, int], + end: tuple[int, int], +) -> list[tuple[int, int]]: + path = [end] + r, c = end + + while (r, c) != start: + for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + nr, nc = r + dr, c + dc + if ( + 0 <= nr < wave.shape[0] + and 0 <= nc < wave.shape[1] + and wave[nr, nc] == wave[r, c] - 1 + ): + path.append((nr, nc)) + r, c = nr, nc + break + + return path + + +def build_overlay( + maze: np.ndarray, + frame: np.ndarray, + path: list[tuple[int, int]], + path_length: int, + path_value: float, +) -> np.ndarray: + overlay = np.full(maze.shape, np.nan) + + wave_mask = frame >= 0 + overlay[wave_mask] = frame[wave_mask] + + for r, c in path[:path_length]: + overlay[r, c] = path_value + + return overlay + + +def update_frame( + frame_id: int, + *, + maze: np.ndarray, + base_image, + overlay_image, + frames: list[np.ndarray], + path: list[tuple[int, int]], + path_value: float, +) -> tuple: + wave_count = len(frames) + + if frame_id < wave_count: + current_wave = frames[frame_id] + path_length = 0 + else: + current_wave = frames[-1] + path_length = frame_id - wave_count + 1 + + overlay = build_overlay( + maze=maze, + frame=current_wave, + path=path, + path_length=path_length, + path_value=path_value, + ) + + base_image.set_data(maze) + overlay_image.set_data(overlay) + return base_image, overlay_image 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: + frames, path = run_wave(maze, start, end) + + if not path: + print("Путь не существует") + + last_wave = frames[-1] + max_wave = last_wave[last_wave >= 0].max() if (last_wave >= 0).any() else 1.0 + path_value = max_wave + 5 + + figure, axis = plt.subplots(figsize=(9, 9)) + axis.grid(False) + + base_image = axis.imshow( + maze, + cmap="gray", + vmin=0, + vmax=1, + origin="upper", + ) + + overlay_cmap = plt.cm.hot.copy() + overlay_cmap.set_bad(alpha=0) + + first_overlay = build_overlay( + maze=maze, + frame=frames[0], + path=path, + path_length=0, + path_value=path_value, + ) + + overlay_image = axis.imshow( + first_overlay, + cmap=overlay_cmap, + vmin=0, + vmax=path_value, + origin="upper", + alpha=0.9, + ) + + axis.axis("image") + + animation = FuncAnimation( + figure, + partial( + update_frame, + maze=maze, + base_image=base_image, + overlay_image=overlay_image, + frames=frames, + path=path, + path_value=path_value, + ), + frames=len(frames) + len(path), + interval=100, + blit=False, + ) + + if save_path: + animation.save(save_path, writer="pillow", fps=10) + + return animation if __name__ == "__main__": # Пример 1 @@ -39,12 +207,12 @@ def animate_wave_algorithm( # Пример 2 - maze_path = "./data/maze.npy" + maze_path = "solutions/sem02/lesson08/data/maze.npy" loaded_maze = np.load(maze_path) # можете поменять, если захотите запустить из других точек - start = (2, 0) - end = (5, 0) + start = (0, 17) + end = (101, 43) loaded_save_path = "loaded_labyrinth.gif" loaded_animation = animate_wave_algorithm(loaded_maze, start, end, loaded_save_path)