Skip to content

Commit

Permalink
Code session 2 (#72)
Browse files Browse the repository at this point in the history
* Code session 2

* fix data

* fix doc link
  • Loading branch information
xadupre authored Sep 15, 2024
1 parent 40802cc commit 6e1bb43
Show file tree
Hide file tree
Showing 9 changed files with 1,460 additions and 96 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
*.dbf
*.xlsx
*.pickle
*.shp
*.shx
.coverage
data.*
paris*.*
Expand Down
4 changes: 4 additions & 0 deletions _doc/api/datasets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Jeux de données
Cartographie
============

.. autofunction:: teachpyx.datasets.get_naturalearth_cities

.. autofunction:: teachpyx.datasets.get_naturalearth_lowres

.. autofunction:: teachpyx.datasets.load_enedis_dataset

Classification
Expand Down
6 changes: 5 additions & 1 deletion _doc/articles/2024/2024-11-31-route2024.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ Séance 1
Séance 2
++++++++

Tests unitaires et classes
Tests unitaires et classes toujours avec les dames

:ref:`Prises aux dames <nbl-practice-py-base-dame_prise>`

Séance 3
++++++++

Héritage

:ref:`classes pour représenter un graphe <nbl-practice-py-base-classe_tree>`

Fin des classes puis :ref:`les itérateurs <nbl-practice-py-base-classe_iterateur>` et
Expand Down
1,273 changes: 1,184 additions & 89 deletions _doc/c_data/enedis_cartes.ipynb

Large diffs are not rendered by default.

193 changes: 187 additions & 6 deletions _doc/practice/py-base/dame_prise.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@
"Les valeurs numériques sont toujours plus efficace que des chaînes de caractères. Elles prennent moins de place en mémoire et les opérations sont plus efficaces."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Partie I : sans les classes"
]
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 1,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -75,7 +82,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 2,
"metadata": {},
"outputs": [
{
Expand All @@ -84,7 +91,7 @@
"[(0, 0), (4, 4), (4, 6)]"
]
},
"execution_count": 6,
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
Expand Down Expand Up @@ -114,7 +121,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 3,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -183,7 +190,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 4,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -213,7 +220,181 @@
{
"cell_type": "markdown",
"metadata": {},
"source": []
"source": [
"## Partie 2 : Avec les classes\n",
"\n",
"Dans cette partie, on construit deux classes ``Coup`` et ``Damier`` avec pour objectif de déterminer si le fait de commencer est un avantage dans une partie où les deux joueurs jouent de façon aléatoire. Pour y répondre, il faut simuler un grand nombre de parties.\n",
"\n",
"La classe ``Coup`` contient au moins deux positions, celle de départ et celle d'arrivée. Elle peut en contenir plus si le pion ou la dame peut en prendre plusieurs."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[(0, 0), (3, 3)]\n"
]
}
],
"source": [
"class Coup:\n",
"\n",
" def __init__(self, positions: list[tuple[int, int]]):\n",
"\n",
" self.positions = positions\n",
"\n",
" def __len__(self) -> int:\n",
" \"Retourne le nombre de positions.\"\n",
" return len(self.positions)\n",
"\n",
" def __str__(self) -> str:\n",
" \"Appelée implicitement par Python si print(c) où c est un Coup\"\n",
" return str(self.positions)\n",
"\n",
" def __getitem__(self, i):\n",
" \"Donne un sens à c[0] où c est de type Coup.\"\n",
" return self.positions[i]\n",
"\n",
"\n",
"# Vérification rapide.\n",
"c = Coup([(0, 0), (3, 3)])\n",
"print(c)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Maintenant le ``Damier``:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[3 0 3 0 3 0 3 0 3 0]\n",
" [0 3 0 3 0 3 0 3 0 3]\n",
" [3 0 3 0 3 0 3 0 3 0]\n",
" [0 3 0 3 0 3 0 3 0 3]\n",
" [0 0 0 0 0 0 0 0 0 0]\n",
" [0 0 0 0 0 0 0 0 0 0]\n",
" [4 0 4 0 4 0 4 0 4 0]\n",
" [0 4 0 4 0 4 0 4 0 4]\n",
" [4 0 4 0 4 0 4 0 4 0]\n",
" [0 4 0 4 0 4 0 4 0 4]]\n"
]
}
],
"source": [
"class Damier:\n",
"\n",
" def __init__(self, N: int = 10):\n",
"\n",
" self.damier = np.zeros((N, N), dtype=int)\n",
"\n",
" def __str__(self) -> str:\n",
" \"Appelée implicitement par Python si print(c) où c est un Coup\"\n",
" return str(self.damier)\n",
"\n",
" def __len__(self) -> int:\n",
" \"Retourne la dimension du damier.\"\n",
" return len(self.damier)\n",
"\n",
" def init(self):\n",
" \"Initialise le damier pour un début de partie.\"\n",
" N = len(self)\n",
" for i in range(N):\n",
" if i in ((N - 1) // 2, N // 2):\n",
" continue\n",
" c = 3 if i < N // 2 else 4\n",
" for j in range(N):\n",
" if (i + j) % 2 == 0:\n",
" self.damier[i, j] = c\n",
"\n",
" def joue(self, coup: Coup):\n",
" \"Joue un coup. On suppose que celui-ci est valide.\"\n",
" for i in range(1, len(coup)):\n",
" self.annule(coup[i - 1], coup[i])\n",
" self.damier[coup[1]] = self.damier[coup[0]]\n",
" self.damier[coup[0]] = 0\n",
"\n",
" def annule(self, p1: tuple[int, int], p2: tuple[int, int]):\n",
" \"Annule toutes les cases du damier entre deux positions.\"\n",
" di = (p2[0] - p1[0]) // abs(p2[0] - p1[0])\n",
" dj = (p2[1] - p1[1]) // abs(p2[1] - p1[1])\n",
" for k in range(1, abs(p2[0] - p1[0])):\n",
" self.damier[p1[0] + di * k, p1[1] + dj * k] = 0\n",
"\n",
"\n",
"d = Damier()\n",
"d.init()\n",
"print(d) # équivalent à print(d.__str__())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"On écrit un test unitaire pour vérifier que la méthode ``init`` est valide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def test_init():\n",
" d = Damier(4)\n",
" d.init()\n",
" assert d.damier.tolist() == [\n",
" [3, 0, 3, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 4, 0, 4],\n",
" ], f\"{d.damier.tolist()}\"\n",
"\n",
"\n",
"test_init()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"On fait de même pour la méthode ``joue``."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def test_coup():\n",
" d = Damier(4)\n",
" d.init()\n",
" d.joue(Coup([(0, 0), (1, 1)]))\n",
" assert d.damier.tolist() == [\n",
" [0, 0, 3, 0],\n",
" [0, 3, 0, 0],\n",
" [0, 0, 0, 0],\n",
" [0, 4, 0, 4],\n",
" ], f\"{d.damier.tolist()}\"\n",
"\n",
"\n",
"test_coup()"
]
}
],
"metadata": {
Expand Down
19 changes: 19 additions & 0 deletions _unittests/ut_datasets/test_gpd_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import unittest
from teachpyx.ext_test_case import ExtTestCase
from teachpyx.datasets import get_naturalearth_cities, get_naturalearth_lowres


class TestGpdHelper(ExtTestCase):
def test_get_naturalearth_cities(self):
filenames = get_naturalearth_cities()
for filename in filenames:
self.assertExists(filename)

def test_get_naturalearth_lowres(self):
filenames = get_naturalearth_lowres()
for filename in filenames:
self.assertExists(filename)


if __name__ == "__main__":
unittest.main()
2 changes: 2 additions & 0 deletions teachpyx/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
# from .titanic import load_titanic_dataset
# from .tweets import load_tweet_dataset
from .wines import load_wines_dataset, load_wine_dataset

from .gpd_helper import get_naturalearth_cities, get_naturalearth_lowres
31 changes: 31 additions & 0 deletions teachpyx/datasets/gpd_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from ..tools.data_helper import download


def get_naturalearth_cities(dest: str = ".", timeout: int = 10, verbose: bool = False):
"""
Retrieves file ``naturalearth_cities.shp``, ``naturalearth_cities.shx``,
``naturalearth_cities.dbf`` in
`teachdata/geopandas/data/naturalearth_cities/
<https://github.com/sdpython/teachdata/blob/main/geopandas/data/naturalearth_cities/>`_.
"""
urls = [
"https://github.com/sdpython/teachdata/raw/main/geopandas/data/naturalearth_cities/naturalearth_cities.shp",
"https://github.com/sdpython/teachdata/raw/main/geopandas/data/naturalearth_cities/naturalearth_cities.shx",
"https://github.com/sdpython/teachdata/raw/main/geopandas/data/naturalearth_cities/naturalearth_cities.dbf",
]
return [download(url, dest=dest, timeout=timeout, verbose=verbose) for url in urls]


def get_naturalearth_lowres(dest: str = ".", timeout: int = 10, verbose: bool = False):
"""
Retrieves files ``naturalearth_lowres.shp``, ``naturalearth_lowres.shx``,
``naturalearth_lowres.dbf`` in
`teachdata/geopandas/data/naturalearth_lowres/
<https://github.com/sdpython/teachdata/blob/main/geopandas/data/naturalearth_lowres/>`_.
"""
urls = [
"https://github.com/sdpython/teachdata/raw/main/geopandas/data/naturalearth_lowres/naturalearth_lowres.shp",
"https://github.com/sdpython/teachdata/raw/main/geopandas/data/naturalearth_lowres/naturalearth_lowres.shx",
"https://github.com/sdpython/teachdata/raw/main/geopandas/data/naturalearth_lowres/naturalearth_lowres.dbf",
]
return [download(url, dest=dest, timeout=timeout, verbose=verbose) for url in urls]
26 changes: 26 additions & 0 deletions teachpyx/tools/data_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,32 @@ def decompress_zip(filename, dest: str, verbose: bool = False) -> List[str]:
return files


def download(
url: str, dest: str = ".", timeout: int = 10, verbose: bool = False
) -> str:
"""
Download one file.
:param url: url
:param dest: destination folder
:param timeout: timeout
:param verbose: display progress
:return: filename
"""
filename = url.split("/")[-1]
dest_zip = os.path.join(dest, filename)
if not os.path.exists(dest_zip):
if verbose:
print(f"downloads into {dest_zip!r} from {url!r}")
with urlopen(url, timeout=timeout) as u:
content = u.read()
with open(dest_zip, "wb") as f:
f.write(content)
elif verbose:
print(f"already downloaded {dest_zip!r}")
return dest_zip


def download_and_unzip(
url: str, dest: str = ".", timeout: int = 10, verbose: bool = False
) -> List[str]:
Expand Down

0 comments on commit 6e1bb43

Please sign in to comment.