diff --git a/fractals/sierpinski_triangle.py b/fractals/sierpinski_triangle.py index 084f6661f..c28ec00b2 100644 --- a/fractals/sierpinski_triangle.py +++ b/fractals/sierpinski_triangle.py @@ -1,76 +1,84 @@ -#!/usr/bin/python +""" +Author Anurag Kumar | anuragkumarak95@gmail.com | git/anuragkumarak95 -"""Author Anurag Kumar | anuragkumarak95@gmail.com | git/anuragkumarak95 +Simple example of fractal generation using recursion. -Simple example of Fractal generation using recursive function. +What is the Sierpiński Triangle? + The Sierpiński triangle (sometimes spelled Sierpinski), also called the +Sierpiński gasket or Sierpiński sieve, is a fractal attractive fixed set with +the overall shape of an equilateral triangle, subdivided recursively into +smaller equilateral triangles. Originally constructed as a curve, this is one of +the basic examples of self-similar sets—that is, it is a mathematically +generated pattern that is reproducible at any magnification or reduction. It is +named after the Polish mathematician Wacław Sierpiński, but appeared as a +decorative pattern many centuries before the work of Sierpiński. -What is Sierpinski Triangle? ->>The Sierpinski triangle (also with the original orthography Sierpinski), also called -the Sierpinski gasket or the Sierpinski Sieve, is a fractal and attractive fixed set -with the overall shape of an equilateral triangle, subdivided recursively into smaller -equilateral triangles. Originally constructed as a curve, this is one of the basic -examples of self-similar sets, i.e., it is a mathematically generated pattern that can -be reproducible at any magnification or reduction. It is named after the Polish -mathematician Wacław Sierpinski, but appeared as a decorative pattern many centuries -prior to the work of Sierpinski. -Requirements(pip): - - turtle - -Python: - - 2.6 - -Usage: - - $python sierpinski_triangle.py - -Credits: This code was written by editing the code from -https://www.riannetrujillo.com/blog/python-fractal/ +Usage: python sierpinski_triangle.py +Credits: + The above description is taken from + https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle + This code was written by editing the code from + https://www.riannetrujillo.com/blog/python-fractal/ """ import sys import turtle -PROGNAME = "Sierpinski Triangle" -points = [[-175, -125], [0, 175], [175, -125]] # size of triangle +def get_mid(p1: tuple[float, float], p2: tuple[float, float]) -> tuple[float, float]: + """ + Find the midpoint of two points + + >>> get_mid((0, 0), (2, 2)) + (1.0, 1.0) + >>> get_mid((-3, -3), (3, 3)) + (0.0, 0.0) + >>> get_mid((1, 0), (3, 2)) + (2.0, 1.0) + >>> get_mid((0, 0), (1, 1)) + (0.5, 0.5) + >>> get_mid((0, 0), (0, 0)) + (0.0, 0.0) + """ + return (p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2 -def get_mid(p1, p2): - return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2) # find midpoint - - -def triangle(points, depth): - +def triangle( + vertex1: tuple[float, float], + vertex2: tuple[float, float], + vertex3: tuple[float, float], + depth: int, +) -> None: + """ + Recursively draw the Sierpinski triangle given the vertices of the triangle + and the recursion depth + """ my_pen.up() - my_pen.goto(points[0][0], points[0][1]) + my_pen.goto(vertex1[0], vertex1[1]) my_pen.down() - my_pen.goto(points[1][0], points[1][1]) - my_pen.goto(points[2][0], points[2][1]) - my_pen.goto(points[0][0], points[0][1]) + my_pen.goto(vertex2[0], vertex2[1]) + my_pen.goto(vertex3[0], vertex3[1]) + my_pen.goto(vertex1[0], vertex1[1]) - if depth > 0: - triangle( - [points[0], get_mid(points[0], points[1]), get_mid(points[0], points[2])], - depth - 1, - ) - triangle( - [points[1], get_mid(points[0], points[1]), get_mid(points[1], points[2])], - depth - 1, - ) - triangle( - [points[2], get_mid(points[2], points[1]), get_mid(points[0], points[2])], - depth - 1, - ) + if depth == 0: + return + + triangle(vertex1, get_mid(vertex1, vertex2), get_mid(vertex1, vertex3), depth - 1) + triangle(vertex2, get_mid(vertex1, vertex2), get_mid(vertex2, vertex3), depth - 1) + triangle(vertex3, get_mid(vertex3, vertex2), get_mid(vertex1, vertex3), depth - 1) if __name__ == "__main__": if len(sys.argv) != 2: raise ValueError( - "right format for using this script: " - "$python fractals.py " + "Correct format for using this script: " + "python fractals.py " ) my_pen = turtle.Turtle() my_pen.ht() my_pen.speed(5) my_pen.pencolor("red") - triangle(points, int(sys.argv[1])) + + vertices = [(-175, -125), (0, 175), (175, -125)] # vertices of triangle + triangle(vertices[0], vertices[1], vertices[2], int(sys.argv[1])) diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.py b/machine_learning/local_weighted_learning/local_weighted_learning.py index df03fe0a1..6260e9ac6 100644 --- a/machine_learning/local_weighted_learning/local_weighted_learning.py +++ b/machine_learning/local_weighted_learning/local_weighted_learning.py @@ -1,76 +1,86 @@ -# Required imports to run this file import matplotlib.pyplot as plt import numpy as np -# weighted matrix -def weighted_matrix(point: np.mat, training_data_x: np.mat, bandwidth: float) -> np.mat: +def weighted_matrix( + point: np.array, training_data_x: np.array, bandwidth: float +) -> np.array: """ - Calculate the weight for every point in the - data set. It takes training_point , query_point, and tau - Here Tau is not a fixed value it can be varied depends on output. - tau --> bandwidth - xmat -->Training data - point --> the x where we want to make predictions - >>> weighted_matrix(np.array([1., 1.]),np.mat([[16.99, 10.34], [21.01,23.68], - ... [24.59,25.69]]), 0.6) - matrix([[1.43807972e-207, 0.00000000e+000, 0.00000000e+000], - [0.00000000e+000, 0.00000000e+000, 0.00000000e+000], - [0.00000000e+000, 0.00000000e+000, 0.00000000e+000]]) + Calculate the weight for every point in the data set. + point --> the x value at which we want to make predictions + >>> weighted_matrix( + ... np.array([1., 1.]), + ... np.array([[16.99, 10.34], [21.01,23.68], [24.59,25.69]]), + ... 0.6 + ... ) + array([[1.43807972e-207, 0.00000000e+000, 0.00000000e+000], + [0.00000000e+000, 0.00000000e+000, 0.00000000e+000], + [0.00000000e+000, 0.00000000e+000, 0.00000000e+000]]) """ - # m is the number of training samples - m, n = np.shape(training_data_x) - # Initializing weights as identity matrix - weights = np.mat(np.eye(m)) + m, _ = np.shape(training_data_x) # m is the number of training samples + weights = np.eye(m) # Initializing weights as identity matrix + # calculating weights for all training examples [x(i)'s] for j in range(m): diff = point - training_data_x[j] - weights[j, j] = np.exp(diff * diff.T / (-2.0 * bandwidth**2)) + weights[j, j] = np.exp(diff @ diff.T / (-2.0 * bandwidth**2)) return weights def local_weight( - point: np.mat, training_data_x: np.mat, training_data_y: np.mat, bandwidth: float -) -> np.mat: + point: np.array, + training_data_x: np.array, + training_data_y: np.array, + bandwidth: float, +) -> np.array: """ Calculate the local weights using the weight_matrix function on training data. Return the weighted matrix. - >>> local_weight(np.array([1., 1.]),np.mat([[16.99, 10.34], [21.01,23.68], - ... [24.59,25.69]]),np.mat([[1.01, 1.66, 3.5]]), 0.6) - matrix([[0.00873174], - [0.08272556]]) + >>> local_weight( + ... np.array([1., 1.]), + ... np.array([[16.99, 10.34], [21.01,23.68], [24.59,25.69]]), + ... np.array([[1.01, 1.66, 3.5]]), + ... 0.6 + ... ) + array([[0.00873174], + [0.08272556]]) """ weight = weighted_matrix(point, training_data_x, bandwidth) - w = (training_data_x.T * (weight * training_data_x)).I * ( - training_data_x.T * weight * training_data_y.T + w = np.linalg.inv(training_data_x.T @ (weight @ training_data_x)) @ ( + training_data_x.T @ weight @ training_data_y.T ) return w def local_weight_regression( - training_data_x: np.mat, training_data_y: np.mat, bandwidth: float -) -> np.mat: + training_data_x: np.array, training_data_y: np.array, bandwidth: float +) -> np.array: """ - Calculate predictions for each data point on axis. - >>> local_weight_regression(np.mat([[16.99, 10.34], [21.01,23.68], - ... [24.59,25.69]]),np.mat([[1.01, 1.66, 3.5]]), 0.6) + Calculate predictions for each data point on axis + >>> local_weight_regression( + ... np.array([[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]]), + ... np.array([[1.01, 1.66, 3.5]]), + ... 0.6 + ... ) array([1.07173261, 1.65970737, 3.50160179]) """ - m, n = np.shape(training_data_x) + m, _ = np.shape(training_data_x) ypred = np.zeros(m) for i, item in enumerate(training_data_x): - ypred[i] = item * local_weight( + ypred[i] = item @ local_weight( item, training_data_x, training_data_y, bandwidth ) return ypred -def load_data(dataset_name: str, cola_name: str, colb_name: str) -> np.mat: +def load_data( + dataset_name: str, cola_name: str, colb_name: str +) -> tuple[np.array, np.array, np.array, np.array]: """ - Function used for loading data from the seaborn splitting into x and y points + Load data from seaborn and split it into x and y points """ import seaborn as sns @@ -78,23 +88,25 @@ def load_data(dataset_name: str, cola_name: str, colb_name: str) -> np.mat: col_a = np.array(data[cola_name]) # total_bill col_b = np.array(data[colb_name]) # tip - mcol_a = np.mat(col_a) - mcol_b = np.mat(col_b) + mcol_a = col_a.copy() + mcol_b = col_b.copy() - m = np.shape(mcol_b)[1] - one = np.ones((1, m), dtype=int) + one = np.ones(np.shape(mcol_b)[0], dtype=int) - # horizontal stacking - training_data_x = np.hstack((one.T, mcol_a.T)) + # pairing elements of one and mcol_a + training_data_x = np.column_stack((one, mcol_a)) return training_data_x, mcol_b, col_a, col_b -def get_preds(training_data_x: np.mat, mcol_b: np.mat, tau: float) -> np.ndarray: +def get_preds(training_data_x: np.array, mcol_b: np.array, tau: float) -> np.array: """ Get predictions with minimum error for each training data - >>> get_preds(np.mat([[16.99, 10.34], [21.01,23.68], - ... [24.59,25.69]]),np.mat([[1.01, 1.66, 3.5]]), 0.6) + >>> get_preds( + ... np.array([[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]]), + ... np.array([[1.01, 1.66, 3.5]]), + ... 0.6 + ... ) array([1.07173261, 1.65970737, 3.50160179]) """ ypred = local_weight_regression(training_data_x, mcol_b, tau) @@ -102,15 +114,15 @@ def get_preds(training_data_x: np.mat, mcol_b: np.mat, tau: float) -> np.ndarray def plot_preds( - training_data_x: np.mat, - predictions: np.ndarray, - col_x: np.ndarray, - col_y: np.ndarray, + training_data_x: np.array, + predictions: np.array, + col_x: np.array, + col_y: np.array, cola_name: str, colb_name: str, ) -> plt.plot: """ - This function used to plot predictions and display the graph + Plot predictions and display the graph """ xsort = training_data_x.copy() xsort.sort(axis=0) @@ -128,6 +140,10 @@ def plot_preds( if __name__ == "__main__": + import doctest + + doctest.testmod() + training_data_x, mcol_b, col_a, col_b = load_data("tips", "total_bill", "tip") predictions = get_preds(training_data_x, mcol_b, 0.5) plot_preds(training_data_x, predictions, col_a, col_b, "total_bill", "tip")