importsqlite3try:conn=sqlite3.connect('exoplanets_migrated.db')cur=conn.cursor()print("Base de données crée et correctement connectée à SQLite")sql="SELECT sqlite_version();"cur.execute(sql)res=cur.fetchall()print("La version de SQLite est: ",res)exceptsqlite3.Erroraserror:print("Erreur lors de la connexion à SQLite",error)
Table planets
Pour avoir des informations sur la table:
sql="""
PRAGMA table_info(planets);
"""
try:cur.execute(sql)res=cur.fetchall()print(f"{'Attribut':<20}{'Domaine':<10}")print("-"*30)forrowinres:print(f"{row[1]:<20}{row[2]:<10}")exceptsqlite3.Erroraserror:print("Erreur lors de la connexion à SQLite",error)
Résultat:
Attribut Domaine
------------------------------
planet_id INTEGER
name TEXT
planet_status TEXT
mass REAL
radius REAL
orbital_period REAL
semi_major_axis REAL
eccentricity REAL
discovered INTEGER
detection_type TEXT
molecules TEXT
star_id INTEGER
table stars
Travail: Adapter le script précédent pour avoir les informations de la table stars
Selection avec jointure
La requête suivante va donner les informations sur les premières exoplanètes de la table, ainsi que leur étoile, avec jointure entre les 2 tables.
try:sql="""
SELECT p.name, p.mass, p.radius, s.star_name, s.star_teff
FROM planets p
JOIN stars s ON p.star_id = s.star_id
LIMIT 5;
"""cur.execute(sql)res=cur.fetchall()print(f"{'Planète':<20}{'Masse':<10}{'Rayon':<10}{'Étoile':<15}{'Teff'}")print("-"*60)forrowinres:print(f"{str(row[0]):<20}{str(row[1]):<10}{str(row[2]):<10}{str(row[3]):<15}{row[4]}")exceptsqlite3.Erroraserror:print("Erreur lors de la connexion à SQLite",error)
A Faire: Executer le script précédent pour obtenir…
Planète Masse Rayon Étoile Teff
------------------------------------------------------------
11 Com b None None 11 Com 4742.0
11 Oph b 21.0 None 11 Oph 2375.0
11 UMi b None None 11 UMi 4340.0
14 And b None None 14 And 4813.0
14 Her b None None 14 Her 5311.0
PARTIE 2: Requetes diverses sur la BDD
Explorer la base de données
Aidez vous du script précédent, que vous modifierez pour repondre aux questions ci-dessous. Sélectionnez en au moins 4.
(table stars) Toutes les données relatives à l’étoile Kepler-89 b
(table planets) Tous les noms des planètes dont la masse est plus de 10 fois supérieure à celle de Jupiter (Indiquer quelles sont ces planètes dans l’extrait de la table)
(tables planets ET stars) Les planètes ainsi que le nom de l’étoile hôte, dont l’étoile est située à moins de 40 pc de la Terre.
(tables planets ET stars) Les noms des 20 étoiles les plus proches de la Terre où des planètes ont été détectées.
(table planets) Nombre total de planètes dans la table planets(utiliser COUNT(*))
(table stars) Nombre total d’étoiles dans la table stars
(table planets) Table des planètes qui n’ont été détectées ni par “Radial Velocity”, ni par “Primary Transit”
(table planets) Le nombre de planètes qui n’ont été détectées ni par “Radial Velocity”, ni par “Primary Transit”
(table stars) Moyenne des distances entre le Soleil et les différentes étoiles de la table (utiliser AVG). Attention à ne compter qu’une seule fois chacune des étoiles.
(tables planets ET stars) Table des planètes où le type de l’étoile est G … (utiliser LIKE "G%")
(tables planets ET stars) Nom des planètes et distances au soleil, où de l’eau a été détectée (utiliser LIKE)
(tables planets ET stars) Le classement des étoiles, par ordre décroissant de leur nombre de planètes. (GROUP BY, ORDER BY). Limiter à 10 étoiles.
Traitement
Ajouter une colonne masse volumique
Travail: Commencer par créer une nouvelle colonne density dans la table planets, et afficher les attributs de cette table:
try:cur.execute(sql1)cur.execute(sql2)res=cur.fetchall()print(f"{'Attribut':<20}{'Domaine':<10}")print("-"*30)forrowinres:print(f"{row[1]:<20}{row[2]:<10}")exceptsqlite3.Erroraserror:print("Erreur lors de la connexion à SQLite",error)
Les données de la table planets sont relative à Jupiter. On rappelle que:
On peut définir une densité pour la planète comme le rapport $\tfrac{\rho_{planete}}{\rho_{Jupiter}} = \tfrac{mass}{radius^3}$.
On ajoutera simplement dans la colonne density
$$density = \tfrac{mass}{radius^3}$$
Travail: Ecrire la requête sql1 (UPDATE SET) pour calculer la density de chaque planète. Le calcul de $x^y$ peut necessiter l’ajout de la fonction power dans le langage SQL si celle-ci n’est pas implémentée (POWER):
conn.create_function("power",2,lambdax,y:x**yifx!=Noneandy!=NoneelseNone)sql1="""
UPDATE planets SET density=mass/power(radius,3)
"""
Travail: Récuperer ensuite les valeurs de la table (sql2), et afficher par ordre decroissant de densité. Executer les 2 requêtes (voir script complet ci-dessous)
Obtenir le tableau suivant avec le classement des planètes par densité décroissante:
Planète Masse Rayon Density
------------------------------------------------------------
Kepler-57 c 6.95 0.138 2644.5241987281925
Kepler-57 b 18.86 0.195 2543.5357979736677
Kepler-52 c 10.41 0.164 2360.0390301939897
Kepler-59 b 2.05 0.098 2178.0890615304843
...
On peut utiliser le script ci-dessous (compléter la requête SQL):
conn.create_function("power",2,lambdax,y:x**yifx!=Noneandy!=NoneelseNone)try:sql1="""
UPDATE planets SET density=mass/power(radius,3)
"""sql2="""
SELECT ... from planets order by ... DESC;
"""cur.execute(sql1)cur.execute(sql2)res=cur.fetchall()print(f"{'Planète':<20}{'Masse':<10}{'Rayon':<10}{'density':<15}")print("-"*60)forrowinres[:10]:# on limite la sortie à 10 planètesprint(f"{str(row[0]):<20}{str(row[1]):<10}{str(row[2]):<10}{str(row[3]):<15}")exceptsqlite3.Erroraserror:print("Erreur lors de la connexion à SQLite",error)
Etude graphique
La recherche de dépendances entre grandeurs se fera en traçant un graphique, avec différents essais pour les axes x,y.
On utilisera le resultat res de la requete precedente. C’est un tableau avec pour colonnes les nom, masse, rayon, densité.
On peut chercher par exemple la dépendance masse <-> rayon.
importmatplotlib.pyplotaspltplt.clf()axes=plt.gca()x=[P[1]forPinres]# massy=[P[2]forPinres]# radiusplt.scatter(x,y,color='silver',marker='.')plt.xlabel('mass')plt.ylabel('radius')axes.set_xscale("log",base=10);# ajout d'un marqueur "Jupiter"z="Jupiter"MJ,RJ=1,1plt.plot(MJ,RJ,marker='o',color='red')axes.text(MJ,RJ,f"({z})",fontsize=8)plt.show()
Analyser chaque partie de ce script:
(1) Que font les instructions: x = [P[1] for P in res] et y = [P[2] for P in res] ?
(2) Comment adapter ces instructions pour tracer un graphique mass/densité?
(3) Pourquoi met-on l’echelle des abscisses en logarithme: axes.set_xscale("log", base=10)?
(4) Compléter ce script pour placer un marqueur de couleur bleue à l’emplacement de la Terre. Ajouter l’etiquette “Terre”
try:sql="""
SELECT name, mass, radius from planets;
"""cur.execute(sql)res=cur.fetchall()print(f"{'Planète':<20}{'Masse':<10}{'Rayon':<10}")print("-"*60)forrowinres[:10]:# on limite la sortie à 10 planètesprint(f"{str(row[0]):<20}{str(row[1]):<10}{str(row[2]):<10}")exceptsqlite3.Erroraserror:print("Erreur lors de la connexion à SQLite",error)
Visualisation
On adoptera la visualisation des axes : x = masse (en echelle logarithmique) et y = rayon.
Pour adapter les valeurs à ce système de coordonnées, on aura besoin de la fonction suivante:
defcoordonnees_cercle_xlog_y(x_center,y_center,radius):# Cercle adapté à l'échelle logarithmiquex_center_log=np.log10(x_center)theta=np.linspace(0,2*np.pi,100)# Points du cercle en coordonnées (log(x), y)x_circle_log=x_center_log+radius*np.cos(theta)y_circle=y_center+radius*np.sin(theta)# Reconvertir x en échelle normalex_circle=10**x_circle_logreturnx_circle,y_circle
Puis, pour le tracé:
importmatplotlib.pyplotaspltimportnumpyasnpplt.clf()axes=plt.gca()x=[P[1]forPinres]# massy=[P[2]forPinres]# radiusplt.scatter(x,y,color='silver',marker='.')plt.xlabel('mass')plt.ylabel('radius')axes.set_xscale("log",base=10)# ajout d'une etiquette Jupiterz="Jupiter"MJ,RJ=1,1plt.plot(MJ,RJ,marker='o',color='red')axes.text(MJ,RJ,f"({z})",fontsize=8)# ajout d'une etiquette Terrez2="Terre"plt.plot(x_T,y_T,marker='o',color='blue')axes.text(x_T,y_T,f"({z2})",fontsize=8)# Tracer le cercle autour de la Terrex_circle,y_circle=coordonnees_cercle_xlog_y(x_T,y_T,0.3)plt.plot(x_circle,y_circle,color='gold',linewidth=1.5)plt.show()
Travail: Obtenir le graphique en nuage de points masse-rayon. Ajouter les instructions dans ce script pour tracer un autre cercle, centré autour de Jupiter.
Détermination des plus proches voisins
fonction distance_knn pour SQL
A Faire: Copier-coller la fonction suivante dans votre programme
# Distance KNN SQLdefdistance_knn_sql(x,y):"""
Calcule la distance euclidienne à la Terre dans l'espace (log(masse), rayon)
Compatible avec SQLite create_function
Parameters:
x : masse de la planète
y : rayon de la planète
Returns:
distance : distance euclidienne à la Terre (ou None si données manquantes)
"""ifxisNoneoryisNoneorx<=0:# log nécessite x > 0returnNoneimportmathx_log=math.log10(x)x_T_log=math.log10(x_T)distance=math.sqrt((x_log-x_T_log)**2+(y-y_T)**2)returndistance# Enregistrement de la fonction dans SQLiteconn.create_function("distance_knn",2,distance_knn_sql)
nouvelle colonne distance
Travail:
(1) Créer une nouvelle colonne distance_terre dans la table planets :cur.execute("""ALTER TABLE ... ADD COLUMN ...""")
(2) Ajouter le script suivant, que vous devrez compléter et executer
# Calculer la distance pour toutes les planètescur.execute("""
UPDATE planets
SET distance_terre = ... ...(mass, radius)
""")# Puis pour sélectionner les K plus proches voisinsK=5cur.execute(f"""
SELECT name, mass, radius, ... ...(mass, radius) as distance
FROM planets
WHERE ... ...(mass, radius) IS NOT NULL
ORDER BY ...
LIMIT {K}""")res=cur.fetchall()
Questions:
(1) Combien de planètes sont à l’intérieur du cercle de rayon 0.3, centré autour de la Terre?
(2) Quelle est l’exoplanète la plus ressemblante à la Terre?