EC321 : évaluation en contrôle continu

Durée : 2h
Tout document autorisé

1. NYC Athletic Facilities API

via GIPHY

Cette API répertorie les 6879 installations sportives réparties dans les 5 arrondissements (borough) de la ville de New York.

https://data.cityofnewyork.us/resource/qnem-b8re.json

Astuce

Le nombre de résultats étant limité à 1000 par page, je vous encourage à utiliser le paramètre $limit dans vos requêtes en fixant sa valeur à 10000.

Exercice 1

Écrivez un script nyc-boroughs.py qui calcule le nombre d'installations sportives par arrondissement sachant que :



#############################
#                           #
#  NYC Athletic Facilities  #
#                           #
#############################

Brooklyn     : 2212
Manhattan    : 1099
Queens       : 2120
Staten Island: 293
The Bronx    : 1146
Correction
import requests as rq
import pandas as pd

def get_facilities_by_borough(code):
  parameters = {
    '$limit': 10000,
    'borough': code
  }

  response = rq.get('https://data.cityofnewyork.us/resource/qnem-b8re.json', params=parameters)
  if response.status_code == 200:
    return len(response.json())
  else:
    print('Error', response.status_code, response.reason)
    return -1

print('Brooklyn     :', get_facilities_by_borough('B'))
print('Manhattan    :', get_facilities_by_borough('M'))
print('Queens       :', get_facilities_by_borough('Q'))
print('Staten Island:', get_facilities_by_borough('R'))
print('The Bronx    :', get_facilities_by_borough('X'))
Queens map
Terrains de basket du Queens

Exercice 2

Écrivez un script queens-courts.py qui, sur une carte centrée sur [40.742054, -73.769417] place un marqueur sur chaque terrain de basketball du Queens dont la couleur dépend du type de revêtement :

Correction
import requests as rq
import pandas as pd
import folium as fl

m = fl.Map(location=[40.742054, -73.769417], tiles='OpenStreetMap', zoom_start=12)

parameters = {
  '$limit': 10000,
  'borough': 'Q',
  'basketball': True
}

response = rq.get('https://data.cityofnewyork.us/resource/qnem-b8re.json', params=parameters)
if response.status_code == 200:
  for court in response.json():
    if court['surface_type'] == 'Asphalt':
      c = 'gray'
    elif court['surface_type'] == 'Synthetic':
      c = 'red'
    else:
      c = 'blue'
    
    coords = court['multipolygon']['coordinates'][0][0][0]
    fl.Marker([coords[1], coords[0]],
      popup=court['surface_type'],
      icon=fl.Icon(color=c)).add_to(m)

  m.save('queens-courts.html')
else:
  print('Error', response.status_code, response.reason)

2. One Punch Man

Le fichier joints.xlsx contient les positions 3D des segments articulaires estimés par MediaPipe pour chaque frame de la vidéo.

Télécharger le fichier

La feuille IMAGE contient les coordonnées, exprimées en pixels, de chaque articulation au fil du temps.

Exercice 3

Écrivez un script punch-time.py qui affiche l'instant où le poing frappe le sac, c'est à dire le moment où le poignet est le plus avancé dans la vidéo.

Punch time at frame 199
Correction
import pandas as pd

joints = pd.read_excel('joints.xlsx', sheet_name='IMAGE')

i = joints['LEFT_WRIST_X'].idxmin()
print('Punch time at frame', joints['frame'][i])

Afin d'évaluer la qualité du suivi effectué par MediaPipe, on souhaite calculer la longueur du bras, distance entre l'épaule et le coude, exprimée en pixels pour chaque frame de la vidéo

Exercice 4

Écrivez un script upper-arm-length.py qui ajoute une nouvelle colonne UAL au fichier joints.xlsx contenant la longueur du bras, exprimée en pixels, au fil du temps. Le script affichera la moyenne et l'écart de cette nouvelle colonne ainsi que le graphique des valeurs au fil du temps.

Upper arm length: 202 ± 22 px
Correction
import pandas as pd
from math import sqrt
import matplotlib.pyplot as plt

def vector_length(x, y, z):
  return sqrt(x**2 + y**2 + z**2)

def distance(x1, y1, z1, x2, y2, z2):
  return vector_length(x2-x1, y2-y1, z2-z1)

joints = pd.read_excel('joints.xlsx', sheet_name='IMAGE')

for i in joints.index:
  joints.loc[i, 'UAL'] = distance(joints['LEFT_SHOULDER_X'][i], joints['LEFT_SHOULDER_Y'][i], joints['LEFT_SHOULDER_Z'][i], joints['LEFT_ELBOW_X'][i], joints['LEFT_ELBOW_Y'][i], joints['LEFT_ELBOW_Z'][i])

print('Upper arm length:', round(joints['UAL'].mean()), '±', round(joints['UAL'].std()), 'px')

plt.plot(joints['UAL'])
plt.title('Upper arm length')
plt.xlabel('frame')
plt.ylabel('length (pixels)')
plt.legend()
plt.show()

3. Test de Sargent

White Men Can't Jump

Le test de Sargent est un test physique qui permet d'évaluer la détente verticale d'une personne. Ce même test permet aussi d'évaluer la puissance des membres inférieurs.

Le test se déroule en deux étapes:

  1. La personne se tient droite, dos au mur à une distance de 15cm. Elle lève un bras le plus haut possible et on mesure la hauteur H0 (en cm) de l'extrémité des doigts.
  2. Sans changer de position, le personne doit chercher à sauter le plus haut possible. À l'apogée du saut, on mesure la hauteur H1 (en cm) de l'extrémité des doigts.

La détente sèche D correspond à l'écart entre les hauteurs H0 et H1 :

D = H1 - H0

Intervalle Interprétation
D > 71 excellente
66 < D ≤ 71 très bonne
53 < D ≤ 66 bonne
49 < D ≤ 53 moyenne
40 < D ≤ 49 faible
37 < D ≤ 40 insuffisante
D ≤ 37 très insuffisante

Mais à partir de la valeur D de la détente, on peut déduire la puissance des membres inférieurs grâce à la formule d'Harman :

P = 21.2 × D + 23 × M - 1393

M est la masse de l'individu en kg.

Exercice 5

Écrivez un script sargent.py qui calcule la détente sèche et la puissance des membres inférieurs à partir de la saisie au clavier des hauteurs H0, H1 et de la masse M.



#####################
#                   #
#  Test de Sargent  #
#                   #
#####################


H0 (cm) ? 245
H1 (cm) ? 298
M  (kg) ? 84

D = 53.0 cm
Détente sèche moyenne

P = 1663 W
Correction
print('\n\n')
print('#####################')
print('#                   #')
print('#  Test de Sargent  #')
print('#                   #')
print('#####################')
print('\n')

h0 = float(input('H0 (cm) ? '))
h1 = float(input('H1 (cm) ? '))
m  = float(input('M  (kg) ? '))

d = h1 - h0

if d > 71:
interpretation = 'excellente'
elif d > 66:
interpretation = 'très bonne'
elif d > 53:
interpretation = 'bonne'
elif d > 49:
interpretation = 'moyenne'
elif d > 40:
interpretation = 'faible'
elif d > 37:
interpretation = 'insuffisante'
else:
interpretation = 'très insuffisante'

print('\nD =', d, 'cm')
print('Détente sèche', interpretation)
print('\nP =', round(21.2 * d + 23.0 * m - 1393), 'W')