Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading