#!/usr/bin/env python3 import sys, os import matplotlib.pyplot as plt from random import random, choice import numpy as np from time import sleep import argparse """ MNI TP3 - 27 janvier 2020 Dylan Voisin - M1 Informatique Luminy Franck Palacios - M1 Informatique Luminy Notes: - u1 et u2 ne semblent pas orthogonaux, ceci est dû à la non utilisation d'une échelle 1:1 pour l'affichage des points - u1 et u2 ne sont pas proportionnels par rapport à lambda mais à la distance entre le barycentre et le point le plus éloigné selon la composante x ou y - u1 est orienté «vers la droite» et u2 «vers le haut» dans ce programme """ def acquérir_depuis_fichier(filename): # l_pts = [] # with open(filename) as f: # for line in f: # split = line.split() # if len(split) == 2: # l_pts.append(list(map(float, split))) # return np.array(l_pts) return np.loadtxt(filename) def calc_barycentre(l_pts): x = sum(l_pts[:,0]) / len(l_pts) y = sum(l_pts[:,1]) / len(l_pts) return x, y def centrer_points(l_pts, barycentre): return l_pts - barycentre def calc_matrice_corrélation(l_pts): c_pts = centrer_points(l_pts, calc_barycentre(l_pts)) A = np.array([ [sum(pt[0] ** 2 for pt in c_pts), sum(pt[0] * pt[1] for pt in c_pts)], [sum(pt[0] * pt[1] for pt in c_pts), sum(pt[1] ** 2 for pt in c_pts)] ]) return A def diagonaliser_matrice22(m): a, b, c, d = m[0][0], m[0][1], m[1][0], m[1][1] discr = (-d - a) ** 2 - 4 * (a * d - b**2) lambda1 = ((d + a) + discr ** .5) / 2 lambda2 = ((d + a) - discr ** .5) / 2 if lambda1 != 0: if a - lambda1 != 0 and b != 0: u1 = np.array([-b, (a-lambda1)]) u1 *= -1 if b > 0 else 1 else: u1 = np.array([lambda1, 0]) u2 = np.array([-u1[1], u1[0]]) else: if a - lambda2 != 0 and b != 0: u2 = np.array([-b, (a-lambda2)]) u2 *= -1 if b < 0 else 1 else: u2 = np.array([lambda2, 0]) u1 = np.array([u2[1], -u2[0]]) return u1, u2, lambda1, lambda2 def get_info_affichage(l_pts): """renvoie les vecteurs u1 et u2 pour l'affichage ainsi que xmin, xmax, ymin, ymax""" bary = calc_barycentre(l_pts) u1, u2 = diagonaliser_matrice22(calc_matrice_corrélation(l_pts))[:2] dist = min(max(l_pts[:,0]) - bary[0], max(l_pts[:,1]) - bary[1]) dist_x = max(l_pts[:,0]) - min(l_pts[:,0]) dist_y = max(l_pts[:,1]) - min(l_pts[:,1]) x_range = min(l_pts[:,0]) - 0.1 * dist_x, max(l_pts[:,0]) + 0.1 * dist_x y_range = min(l_pts[:,1]) - 0.1 * dist_y, max(l_pts[:,1]) + 0.1 * dist_y u1 = u1 / np.linalg.norm(u1) * dist u2 = u2 / np.linalg.norm(u2) * 1/2 * dist return u1, u2, x_range, y_range, dist def visualiser_nuage(l_pts): bary = calc_barycentre(l_pts) u1, u2 = diagonaliser_matrice22(calc_matrice_corrélation(l_pts))[:2] dist = min(max(l_pts[:,0]) - bary[0], max(l_pts[:,1]) - bary[1]) u1 = u1 / np.linalg.norm(u1) * dist u2 = u2 / np.linalg.norm(u2) * 1/2 * dist ax = plt.axes() # plot du nuage plt.scatter(l_pts[:,0], l_pts[:,1]) ax.arrow(*bary, *u1, head_length=0.1*dist, head_width=0.1*dist, ec="black", fc="white") ax.arrow(*bary, *u2, head_length=0.1*dist, head_width=0.1*dist, ec="black", fc="white") # ax.set_aspect('equal') plt.show() def animation_nuage(l_pts, loops=10, time_sleep=2, delta_bruit=3): plt.ion() fig = plt.figure() ax = fig.add_subplot(111) fig.canvas.draw() # nuage courant (pas de do while en Python) bary = calc_barycentre(l_pts) u1, u2, x_range, y_range, dist = get_info_affichage(l_pts) sc = ax.scatter(l_pts[:,0], l_pts[:,1]) arr1 = ax.arrow(*bary, *u1, head_length=0.1*dist, head_width=0.1*dist, ec="black", fc="white") arr2 = ax.arrow(*bary, *u2, head_length=0.1*dist, head_width=0.1*dist, ec="black", fc="white") plt.title("itération 0") fig.canvas.flush_events() fig.canvas.draw() fig.canvas.flush_events() sleep(time_sleep) # attente avant animation suivante for i in range(loops): plt.title("itération " + str(i+1)) # aléa ran = np.random.random(l_pts.shape) * 2 * delta_bruit - delta_bruit l_pts += ran # recalcul des données bary = calc_barycentre(l_pts) u1, u2, x_range, y_range, dist = get_info_affichage(l_pts) ax.set_xlim(*x_range) ax.set_ylim(*y_range) # plot sc.set_offsets(l_pts) # repositionnement des points arr1.remove() # on enlève les vecteurs arr2.remove() arr1 = ax.arrow(*bary, *u1, head_length=0.1*dist, head_width=0.1*dist, ec="black", fc="white") arr2 = ax.arrow(*bary, *u2, head_length=0.1*dist, head_width=0.1*dist, ec="black", fc="white") fig.canvas.flush_events() fig.canvas.draw() # on redessine fig.canvas.flush_events() sleep(time_sleep) # attente avant animation suivante def main(argv): parser = argparse.ArgumentParser() gps = parser.add_mutually_exclusive_group(required=True) gpi = parser.add_mutually_exclusive_group(required=True) gpi.add_argument("-i", "--input", help="input file for dots - if none, generate dots") gpi.add_argument("-f", "--function", help="function to use to generate dots, ex -f 3*x+4") parser.add_argument("-N", "--nb-dots", type=int, default=500, help="number of dots to generate (from 0 to NB_DOTS) from function") gps.add_argument("-s", "--show", action="store_true", help="show cloud of dots with u1 and u2") gps.add_argument("-l", "--loop", type=int, default=10, help="number of loop to show/perform on randomized dots from originals ones") parser.add_argument("-d", "--delay", type=float, default=2, help="amount of time between each loop") parser.add_argument("-n", "--noise", type = float, default=10, help="maximum value of the noise applied between each step") if not argv: parser.print_usage() sys.exit(0) args = parser.parse_args(argv) if args.input: if not os.path.isfile(args.input): print("File", args.input, "not found.", file=sys.stderr) sys.exit(1) else: l_pts = acquérir_depuis_fichier(args.input) else: l_pts = [] n = args.nb_dots # nombre de points f = lambda x: eval(args.function) # fonction génératrice de la ditribution delta_bruit = 0 # bruit généré, 0 si on en veut aucun for i in range(n): y = f(i) + choice((-1, 1)) * random() * delta_bruit/2 l_pts.append([i, y]) l_pts = np.array(l_pts) if args.show: visualiser_nuage(l_pts) elif args.loop: animation_nuage(l_pts, args.loop, args.delay, args.noise) if __name__ == "__main__": # if len(sys.argv) == 2: # l_pts = acquérir_depuis_fichier(sys.argv[1]) # else: # l_pts = [] # n = 500 # nombre de points # f = lambda x: x/2 + 3 # fonction génératrice de la ditribution # delta_bruit = 10 # bruit généré, 0 si on en veut aucun # for i in range(n): # y = f(i) + choice((-1, 1)) * random() * delta_bruit/2 # l_pts.append([i, y]) # l_pts = np.array(l_pts) # # np.savetxt("pts.txt", l_pts, "%f") # u1, u2 = diagonaliser_matrice22(calc_matrice_corrélation(l_pts))[:2] # # print(f"{u1/np.linalg.norm(u1)=}\n{u2/np.linalg.norm(u2)=}") # visualiser_nuage(l_pts) # animation_nuage(l_pts, 20, delta_bruit=100) main(sys.argv[1:])