Les UV (région du spectre éléctro-magniétique comprise ente 10 nm et 400 nm) sont connus depuis longtemps pour être des agents mutagènes (agents augmentant la fréquence des mutations). Les dimères de thymine induits par les UV provoquent des mésappariements lors de la réplication conduisant ainsi à des mutations. Lorsque ces mutations affectent des mélanocytes du derme ou de l’épiderme, cela peut causer des mélanomes ou cancers de la peau.
Les expérimentations menées notamment chez les levures (exposées à des doses et/ou durées variables d’UV) confirment l’effet mutagène des UV. Ce type d’expériences ne pouvant être mené chez des humains pour des raisons éthiques , une approche épidémiologique s’impose donc pour vérifier l’effet mutagène des UV chez l’humain.
import pandas as pd
import geopandas as gp
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
from koala import Koala
Le module koala est disponible sur github
La base UV est disponible ici au format .xlsx
uvv = pd.read_excel("https://gis.cancer.gov/tools/uv-exposure/uv-county.xlsx")
Les bases "mélanome" sont disponibles ici au format .csv
On utilisera 2 bases: une pour toutes les ethnies et une pour les blancs non hispaniques (les personnes à la peau noire et les hispaniques sont moins touchés par le mélanome ce qui pourrait avoir une incidence sur les résultats en raison du % variable d’afro-américains et d’hispaniques selon les états)
Dans chacune des 2 bases utilisées, la colonne Crude Rate indique le nombre de cas pour 100 000 personnes
Les 2 bases sont également disponibles sur ipfs: melanome.csv , melanome_blanc.csv
all_mela = pd.read_csv("melanome.csv", skiprows=2)
white_mela = pd.read_csv("melanome_blanc.csv", skiprows=2)
On supprime les 2 premières lignes d’informations lors de l’importation sinon ca bug avec pandas
La base geojson des états d'amérique est disponible ici au format .geojson
On l’importe avec GeoPandas:
usa = gp.read_file("usastates.geojson")
On obtient un GeoDataFrame.
Cette base contient le taux d’UV en Wh/m2 par ville, il faut donc calculer la moyenne par état.
Création d'un DataFrame vide avec colonne state et uvrate
uv = pd.DataFrame({"state":(), "uvrate":()})
Création d'une liste (pd.Series) des états américains
state = uvv.STATENAME
state = state.drop_duplicates()
Pour chaque état on calcule la moyenne des "uvrate" de l'ensemble des villes de l'état, puis on crée une ligne qu'on ajoute au DataFrame uv
for i in state:
x = uvv[uvv.STATENAME == i]
m = x.mean()
r = pd.DataFrame([(i, m[1])], columns = ("state", "uvrate"))
uv = uv.append(r)
On ré-indexe:
uv.index = range(49)
On supprime les informations présentes sur les lignes 49 à 53:
cc = all_mela.drop([49,50,51,52,52,53])
On range et on ré-indexe:
cc = cc.sort_values(by=['United States'])
cc.index = range(49)
On élimine les colonnes inutiles, en ne conservant que united states, crude rate, et age-adjusted rate:
cc = cc.loc[:,['United States', 'Crude Rate', 'Age-adjusted Rate']]
On renomme les colonnes:
cc.columns = ['state', 'Crude Rate', 'Age-adjusted Rate']
(La colonne "state" doit être commune à toutes les bases pour permettre d’indexer sur cette colonne lorsqu’on les assemblera)
On fait la même modification sauf qu’il n’y a pas de ligne 53
ccw = white_mela.drop([49,50,51,52])
ccw = ccw.sort_values(by=['United States'])
ccw.index = range(49)
ccw = ccw.loc[:,['United States', 'Crude Rate', 'Age-adjusted Rate']]
ccw.columns = ['state', 'Crude Rate W', 'Age-adjusted Rate W']
On utilise la méthode .join et on indexe les 3 bases sur leur colonne ‘state’
ccuv = cc.join(uv.set_index('state'), on='state')
ccuv = ccuv.join(ccw.set_index('state'), on='state')
On crée une liste de colonnes:
x = list(usa.columns)
On modifie le nom de la colonne 6:
x[6] = 'state'
usa.columns = x
On supprime toutes les colonnes sauf la colonne ‘state’ et la colonne ‘geometry’
x.remove('state')
x.remove('geometry')
usa = usa.drop(columns = x)
On assemble le GeoDataFrame usa
avec le DataFrame ccuv
obtenu précédemment, en indexant encore sur la colonne ‘state’:
ccuvusa = usa.join(ccuv.set_index('state'), on='state')
On supprime l’Alaska, Puerto Rico et Hawaï (sinon ca "élargie" trop la carte):
ccuvusa = ccuvusa.drop([7,17,34])
On supprime les états pour lesquels il manque des données:
ccuvusa = ccuvusa.drop([10,14,46])
On sauvegarde le GeoDataFrame au format geojson:
ccuvusa.to_file("baseUV.geojson", driver='GeoJSON')
plt.close()
fig, ax = plt.subplots(1, 1)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
ccuvusa.plot(column='uvrate', ax=ax, legend=True, cax=cax)
plt.title('UV Wh/m2', fontsize=6)
plt.savefig("uvrate.png")
plt.close()
fig, ax = plt.subplots(1, 1)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
ccuvusa.plot(column='Crude Rate', ax=ax, legend=True, cax=cax)
plt.title("Melanoma per 100000", fontsize=6)
plt.savefig("cancer.png")
plt.close()
fig, ax = plt.subplots(1, 1)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
ccuvusa.plot(column='Crude Rate W', ax=ax, legend=True, cax=cax)
plt.title("Melanoma per 100000 White", fontsize=5)
plt.savefig("cancerW.png")
Les corrélations mélanomes (toutes ethnies ou population blanche) / taux d´UV ne semblent pas évidentes lorsqu’on compare les cartes visuellement !
On va donc mesurer ces corrélations avec Koala.
Régressions avec Koala
Koala permet avec la méthode reg
de tester simultanément les régressions linéaire, exponentielle et puissance, tout en affichant les droites de régression et en retournant les équations de ces droites ainsi que les coefficients de corrélation.
On rappelle que le coefficient de corrélations R est compris entre -1 (corrélation négative) et 1 (corrélation positive), et qu’une corrélation est d’autant plus forte que R est proche de 1 ou de -1.
plt.close()
k = Koala(ccuvusa)
rall = k.reg("uvrate", "Crude Rate")
rall.plt.savefig("canceruvall.png")
rwhite = k.reg("uvrate", "Crude Rate W")
rwhite.plt.savefig("canceruvwhite.png")
Dans tous les cas on s’aperçoit que les corrélations sont très mauvaises (proche de 0):
>>> rall.lin.r
-0.40153153897104943
>>> rall.exp.r
-0.42431332286138584
>>> rall.pwr.r
-0.4263730816599278
>>> rwhite.lin.r
0.1530027636233132
>>> rwhite.exp.r
0.14227221269991572
>>> rwhite.pwr.r
0.13763196949543993
Les corrélations sont encore plus mauvaises lorsqu’on utilise les taux de mélanomes ajustés (colonne Age-adjusted Rate).
Le manuel Belin programme 2011 suggère cette corrélation (sans donner le coefficient de corrélation) en combinant sur la même carte index UV et mortalité par mélanome:
Vu de loin , en clignant des yeux et en tournant la tête on peut imaginer une corrélation positive, cependant on ne nous donne pas de coefficient de corrélation pour vérifier cela. Certains états comme le Nouveau Mexique posent un peu problème !
On peut obtenir le même type de graphique avec geopandas en superposant les couches graphiques.
On réalise d’abord la carte mélanome par état:
plt.close()
fig, ax = plt.subplots(1, 1)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
base = ccuvusa.plot(column='Crude Rate W', ax=ax, legend=True, cax=cax)
plt.title("Melanoma per 100000 White", fontsize=5)
Puis on crée un nouveau GeoDataFrame ayant une colonne geometry contenant non plus des polygones mais des points (points centraux de chaque polygone), et on conserve les colonnes geometry et uvrate:
On copie le GeoDataFrame ccuvusa en l’affectant à une nouvelle variable pt:
pt = ccuvusa
On ajoute une colonne center
contenant les coordonnées du point central de chaque état:
pt['center'] = pt.geometry.centroid
On conserve les colonnes center
et uvrate
:
pt = pt.loc[:, ['center', 'uvrate']]
On renomme la colonne center
en geometry
:
pt.columns = ['geometry', 'uvrate']
On prend l’exponentielle de l’UVrate/1000 le tout /2 pour "étaler" les valeurs, sinon on ne distinguera pas la différence de diamètre des cercles représentant l’uvrate sur la carte.
ms = np.exp(pt.uvrate / 1000) / 2
On plot en superposant sur la carte mélanome
pt.plot(ax=ax, color='red', marker='o', legend=True, markersize=ms)
plt.savefig("uvcancerW.png")
Le diamètre de chaque cercle rouge est proportionnel au taux d’UV dans l’etat.
Les données utilisées proviennent de sources fiables: NAACCR (North American Association of Central Cancer Registries) pour les bases "mélanome", et NCI (National Cancer Institute) pour la base UV. Il manque cependant des données pour 3 états; l’échantillon de données et donc relativement petit (48 états) pour mesurer une corrélation significative. Par ailleurs , le fait qu’il n’y ait pas de corrélation linéaire , puissance , ou exponentielle, ne signifie pas qu’il n’y a pas de corrélation (la corrélation pourrait être d’un autre type).