Auto-update: 2025-10-23 15:04:00
This commit is contained in:
parent
7698240a79
commit
6170c48371
28 changed files with 19938 additions and 0 deletions
9
activite1/activite1.aux
Normal file
9
activite1/activite1.aux
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
\relax
|
||||
\providecommand \babel@aux [2]{\global \let \babel@toc \@gobbletwo }
|
||||
\@nameuse{bbl@beforestart}
|
||||
\catcode `:\active
|
||||
\catcode `;\active
|
||||
\catcode `!\active
|
||||
\catcode `?\active
|
||||
\babel@aux{french}{}
|
||||
\gdef \@abspage@last{4}
|
||||
750
activite1/activite1.log
Normal file
750
activite1/activite1.log
Normal file
|
|
@ -0,0 +1,750 @@
|
|||
This is pdfTeX, Version 3.141592653-2.6-1.40.27 (TeX Live 2026/dev/Arch Linux) (preloaded format=pdflatex 2025.8.2) 23 OCT 2025 12:21
|
||||
entering extended mode
|
||||
restricted \write18 enabled.
|
||||
%&-line parsing enabled.
|
||||
**activite1.tex
|
||||
(./activite1.tex
|
||||
LaTeX2e <2024-11-01> patch level 2
|
||||
L3 programming layer <2025-01-18>
|
||||
(/usr/share/texmf-dist/tex/latex/base/article.cls
|
||||
Document Class: article 2024/06/29 v1.4n Standard LaTeX document class
|
||||
(/usr/share/texmf-dist/tex/latex/base/size11.clo
|
||||
File: size11.clo 2024/06/29 v1.4n Standard LaTeX file (size option)
|
||||
)
|
||||
\c@part=\count196
|
||||
\c@section=\count197
|
||||
\c@subsection=\count198
|
||||
\c@subsubsection=\count199
|
||||
\c@paragraph=\count266
|
||||
\c@subparagraph=\count267
|
||||
\c@figure=\count268
|
||||
\c@table=\count269
|
||||
\abovecaptionskip=\skip49
|
||||
\belowcaptionskip=\skip50
|
||||
\bibindent=\dimen141
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/base/inputenc.sty
|
||||
Package: inputenc 2024/02/08 v1.3d Input encoding file
|
||||
\inpenc@prehook=\toks17
|
||||
\inpenc@posthook=\toks18
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/base/fontenc.sty
|
||||
Package: fontenc 2021/04/29 v2.0v Standard LaTeX package
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/babel/babel.sty
|
||||
Package: babel 2025/02/14 v25.4 The multilingual framework for pdfLaTeX, LuaLaT
|
||||
eX and XeLaTeX
|
||||
\babel@savecnt=\count270
|
||||
\U@D=\dimen142
|
||||
\l@unhyphenated=\language5
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/babel/txtbabel.def)
|
||||
\bbl@readstream=\read2
|
||||
\bbl@dirlevel=\count271
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/babel-french/francais.ldf
|
||||
Language: francais 2024-07-25 v3.6c French support from the babel system
|
||||
|
||||
|
||||
Package francais.ldf Warning: Option `francais' for Babel is *deprecated*,
|
||||
(francais.ldf) it might be removed sooner or later. Please
|
||||
(francais.ldf) use `french' instead; reported on input line 31.
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/babel-french/french.ldf
|
||||
Language: french 2024-07-25 v3.6c French support from the babel system
|
||||
Package babel Info: Hyphen rules for 'acadian' set to \l@french
|
||||
(babel) (\language4). Reported on input line 91.
|
||||
Package babel Info: Hyphen rules for 'canadien' set to \l@french
|
||||
(babel) (\language4). Reported on input line 92.
|
||||
\FB@stdchar=\count272
|
||||
Package babel Info: Making : an active character on input line 421.
|
||||
Package babel Info: Making ; an active character on input line 422.
|
||||
Package babel Info: Making ! an active character on input line 423.
|
||||
Package babel Info: Making ? an active character on input line 424.
|
||||
\FBguill@level=\count273
|
||||
\FBold@everypar=\toks19
|
||||
\FB@Mht=\dimen143
|
||||
\mc@charclass=\count274
|
||||
\mc@charfam=\count275
|
||||
\mc@charslot=\count276
|
||||
\std@mcc=\count277
|
||||
\dec@mcc=\count278
|
||||
\FB@parskip=\dimen144
|
||||
\listindentFB=\dimen145
|
||||
\descindentFB=\dimen146
|
||||
\labelindentFB=\dimen147
|
||||
\labelwidthFB=\dimen148
|
||||
\leftmarginFB=\dimen149
|
||||
\parindentFFN=\dimen150
|
||||
\FBfnindent=\dimen151
|
||||
)))
|
||||
(/usr/share/texmf-dist/tex/latex/carlisle/scalefnt.sty)
|
||||
(/usr/share/texmf-dist/tex/latex/geometry/geometry.sty
|
||||
Package: geometry 2020/01/02 v5.9 Page Geometry
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
|
||||
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
|
||||
\KV@toks@=\toks20
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty
|
||||
Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead.
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/iftex/iftex.sty
|
||||
Package: iftex 2024/12/12 v1.0g TeX engine tests
|
||||
))
|
||||
\Gm@cnth=\count279
|
||||
\Gm@cntv=\count280
|
||||
\c@Gm@tempcnt=\count281
|
||||
\Gm@bindingoffset=\dimen152
|
||||
\Gm@wd@mp=\dimen153
|
||||
\Gm@odd@mp=\dimen154
|
||||
\Gm@even@mp=\dimen155
|
||||
\Gm@layoutwidth=\dimen156
|
||||
\Gm@layoutheight=\dimen157
|
||||
\Gm@layouthoffset=\dimen158
|
||||
\Gm@layoutvoffset=\dimen159
|
||||
\Gm@dimlist=\toks21
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
|
||||
\pgfutil@everybye=\toks22
|
||||
\pgfutil@tempdima=\dimen160
|
||||
\pgfutil@tempdimb=\dimen161
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
|
||||
\pgfutil@abb=\box52
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
|
||||
Package: pgfrcs 2023-01-15 v3.1.10 (3.1.10)
|
||||
))
|
||||
Package: pgf 2023-01-15 v3.1.10 (3.1.10)
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
|
||||
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
|
||||
Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR)
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
|
||||
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
|
||||
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
|
||||
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
|
||||
)
|
||||
Package graphics Info: Driver file: pdftex.def on input line 106.
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
|
||||
File: pdftex.def 2024/04/13 v1.2c Graphics/color driver for pdftex
|
||||
))
|
||||
\Gin@req@height=\dimen162
|
||||
\Gin@req@width=\dimen163
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
|
||||
Package: pgfsys 2023-01-15 v3.1.10 (3.1.10)
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
|
||||
\pgfkeys@pathtoks=\toks23
|
||||
\pgfkeys@temptoks=\toks24
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
|
||||
x
|
||||
\pgfkeys@tmptoks=\toks25
|
||||
))
|
||||
\pgf@x=\dimen164
|
||||
\pgf@y=\dimen165
|
||||
\pgf@xa=\dimen166
|
||||
\pgf@ya=\dimen167
|
||||
\pgf@xb=\dimen168
|
||||
\pgf@yb=\dimen169
|
||||
\pgf@xc=\dimen170
|
||||
\pgf@yc=\dimen171
|
||||
\pgf@xd=\dimen172
|
||||
\pgf@yd=\dimen173
|
||||
\w@pgf@writea=\write3
|
||||
\r@pgf@reada=\read3
|
||||
\c@pgf@counta=\count282
|
||||
\c@pgf@countb=\count283
|
||||
\c@pgf@countc=\count284
|
||||
\c@pgf@countd=\count285
|
||||
\t@pgf@toka=\toks26
|
||||
\t@pgf@tokb=\toks27
|
||||
\t@pgf@tokc=\toks28
|
||||
\pgf@sys@id@count=\count286
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
|
||||
File: pgf.cfg 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
Driver file for pgf: pgfsys-pdftex.def
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
|
||||
File: pgfsys-pdftex.def 2023-01-15 v3.1.10 (3.1.10)
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
|
||||
File: pgfsys-common-pdf.def 2023-01-15 v3.1.10 (3.1.10)
|
||||
)))
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
|
||||
File: pgfsyssoftpath.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgfsyssoftpath@smallbuffer@items=\count287
|
||||
\pgfsyssoftpath@bigbuffer@items=\count288
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
|
||||
File: pgfsysprotocol.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
|
||||
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
|
||||
File: color.cfg 2016/01/02 v1.6 sample color configuration
|
||||
)
|
||||
Package xcolor Info: Driver file: pdftex.def on input line 274.
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
|
||||
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
|
||||
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
|
||||
Package xcolor Info: Model `RGB' extended on input line 1365.
|
||||
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
|
||||
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
|
||||
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
|
||||
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
|
||||
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
|
||||
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
|
||||
Package: pgfcore 2023-01-15 v3.1.10 (3.1.10)
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
|
||||
\pgfmath@dimen=\dimen174
|
||||
\pgfmath@count=\count289
|
||||
\pgfmath@box=\box53
|
||||
\pgfmath@toks=\toks29
|
||||
\pgfmath@stack@operand=\toks30
|
||||
\pgfmath@stack@operation=\toks31
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
|
||||
.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
|
||||
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
|
||||
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
|
||||
\c@pgfmathroundto@lastzeros=\count290
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
|
||||
File: pgfcorepoints.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@picminx=\dimen175
|
||||
\pgf@picmaxx=\dimen176
|
||||
\pgf@picminy=\dimen177
|
||||
\pgf@picmaxy=\dimen178
|
||||
\pgf@pathminx=\dimen179
|
||||
\pgf@pathmaxx=\dimen180
|
||||
\pgf@pathminy=\dimen181
|
||||
\pgf@pathmaxy=\dimen182
|
||||
\pgf@xx=\dimen183
|
||||
\pgf@xy=\dimen184
|
||||
\pgf@yx=\dimen185
|
||||
\pgf@yy=\dimen186
|
||||
\pgf@zx=\dimen187
|
||||
\pgf@zy=\dimen188
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
|
||||
File: pgfcorepathconstruct.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@path@lastx=\dimen189
|
||||
\pgf@path@lasty=\dimen190
|
||||
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
|
||||
File: pgfcorepathusage.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@shorten@end@additional=\dimen191
|
||||
\pgf@shorten@start@additional=\dimen192
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
|
||||
File: pgfcorescopes.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgfpic=\box54
|
||||
\pgf@hbox=\box55
|
||||
\pgf@layerbox@main=\box56
|
||||
\pgf@picture@serial@count=\count291
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
|
||||
File: pgfcoregraphicstate.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgflinewidth=\dimen193
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
|
||||
ex
|
||||
File: pgfcoretransformations.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@pt@x=\dimen194
|
||||
\pgf@pt@y=\dimen195
|
||||
\pgf@pt@temp=\dimen196
|
||||
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
|
||||
File: pgfcorequick.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
|
||||
File: pgfcoreobjects.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
|
||||
x
|
||||
File: pgfcorepathprocessing.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
|
||||
File: pgfcorearrows.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgfarrowsep=\dimen197
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
|
||||
File: pgfcoreshade.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@max=\dimen198
|
||||
\pgf@sys@shading@range@num=\count292
|
||||
\pgf@shadingcount=\count293
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
|
||||
File: pgfcoreimage.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
|
||||
File: pgfcoreexternal.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgfexternal@startupbox=\box57
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
|
||||
File: pgfcorelayers.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
|
||||
File: pgfcoretransparency.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
|
||||
File: pgfcorepatterns.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
|
||||
File: pgfcorerdf.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)))
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
|
||||
File: pgfmoduleshapes.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgfnodeparttextbox=\box58
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
|
||||
File: pgfmoduleplot.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
|
||||
Package: pgfcomp-version-0-65 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@nodesepstart=\dimen199
|
||||
\pgf@nodesepend=\dimen256
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
|
||||
Package: pgfcomp-version-1-18 2023-01-15 v3.1.10 (3.1.10)
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
|
||||
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
|
||||
Package: pgffor 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgffor@iter=\dimen257
|
||||
\pgffor@skip=\dimen258
|
||||
\pgffor@stack=\toks32
|
||||
\pgffor@toks=\toks33
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
|
||||
Package: tikz 2023-01-15 v3.1.10 (3.1.10)
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
|
||||
x
|
||||
File: pgflibraryplothandlers.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgf@plot@mark@count=\count294
|
||||
\pgfplotmarksize=\dimen259
|
||||
)
|
||||
\tikz@lastx=\dimen260
|
||||
\tikz@lasty=\dimen261
|
||||
\tikz@lastxsaved=\dimen262
|
||||
\tikz@lastysaved=\dimen263
|
||||
\tikz@lastmovetox=\dimen264
|
||||
\tikz@lastmovetoy=\dimen265
|
||||
\tikzleveldistance=\dimen266
|
||||
\tikzsiblingdistance=\dimen267
|
||||
\tikz@figbox=\box59
|
||||
\tikz@figbox@bg=\box60
|
||||
\tikz@tempbox=\box61
|
||||
\tikz@tempbox@bg=\box62
|
||||
\tikztreelevel=\count295
|
||||
\tikznumberofchildren=\count296
|
||||
\tikznumberofcurrentchild=\count297
|
||||
\tikz@fig@count=\count298
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
|
||||
File: pgfmodulematrix.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
\pgfmatrixcurrentrow=\count299
|
||||
\pgfmatrixcurrentcolumn=\count300
|
||||
\pgf@matrix@numberofcolumns=\count301
|
||||
)
|
||||
\tikz@expandcount=\count302
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
|
||||
topaths.code.tex
|
||||
File: tikzlibrarytopaths.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
))) (/usr/share/texmf-dist/tex/latex/amsmath/amsmath.sty
|
||||
Package: amsmath 2024/11/05 v2.17t AMS math features
|
||||
\@mathmargin=\skip51
|
||||
|
||||
For additional information on amsmath, use the `?' option.
|
||||
(/usr/share/texmf-dist/tex/latex/amsmath/amstext.sty
|
||||
Package: amstext 2021/08/26 v2.01 AMS text
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/amsmath/amsgen.sty
|
||||
File: amsgen.sty 1999/11/30 v2.0 generic functions
|
||||
\@emptytoks=\toks34
|
||||
\ex@=\dimen268
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/latex/amsmath/amsbsy.sty
|
||||
Package: amsbsy 1999/11/29 v1.2d Bold Symbols
|
||||
\pmbraise@=\dimen269
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/amsmath/amsopn.sty
|
||||
Package: amsopn 2022/04/08 v2.04 operator names
|
||||
)
|
||||
\inf@bad=\count303
|
||||
LaTeX Info: Redefining \frac on input line 233.
|
||||
\uproot@=\count304
|
||||
\leftroot@=\count305
|
||||
LaTeX Info: Redefining \overline on input line 398.
|
||||
LaTeX Info: Redefining \colon on input line 409.
|
||||
\classnum@=\count306
|
||||
\DOTSCASE@=\count307
|
||||
LaTeX Info: Redefining \ldots on input line 495.
|
||||
LaTeX Info: Redefining \dots on input line 498.
|
||||
LaTeX Info: Redefining \cdots on input line 619.
|
||||
\Mathstrutbox@=\box63
|
||||
\strutbox@=\box64
|
||||
LaTeX Info: Redefining \big on input line 721.
|
||||
LaTeX Info: Redefining \Big on input line 722.
|
||||
LaTeX Info: Redefining \bigg on input line 723.
|
||||
LaTeX Info: Redefining \Bigg on input line 724.
|
||||
\big@size=\dimen270
|
||||
LaTeX Font Info: Redeclaring font encoding OML on input line 742.
|
||||
LaTeX Font Info: Redeclaring font encoding OMS on input line 743.
|
||||
\macc@depth=\count308
|
||||
LaTeX Info: Redefining \bmod on input line 904.
|
||||
LaTeX Info: Redefining \pmod on input line 909.
|
||||
LaTeX Info: Redefining \smash on input line 939.
|
||||
LaTeX Info: Redefining \relbar on input line 969.
|
||||
LaTeX Info: Redefining \Relbar on input line 970.
|
||||
\c@MaxMatrixCols=\count309
|
||||
\dotsspace@=\muskip17
|
||||
\c@parentequation=\count310
|
||||
\dspbrk@lvl=\count311
|
||||
\tag@help=\toks35
|
||||
\row@=\count312
|
||||
\column@=\count313
|
||||
\maxfields@=\count314
|
||||
\andhelp@=\toks36
|
||||
\eqnshift@=\dimen271
|
||||
\alignsep@=\dimen272
|
||||
\tagshift@=\dimen273
|
||||
\tagwidth@=\dimen274
|
||||
\totwidth@=\dimen275
|
||||
\lineht@=\dimen276
|
||||
\@envbody=\toks37
|
||||
\multlinegap=\skip52
|
||||
\multlinetaggap=\skip53
|
||||
\mathdisplay@stack=\toks38
|
||||
LaTeX Info: Redefining \[ on input line 2953.
|
||||
LaTeX Info: Redefining \] on input line 2954.
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/amsfonts/amssymb.sty
|
||||
Package: amssymb 2013/01/14 v3.01 AMS font symbols
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/amsfonts/amsfonts.sty
|
||||
Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support
|
||||
\symAMSa=\mathgroup4
|
||||
\symAMSb=\mathgroup5
|
||||
LaTeX Font Info: Redeclaring math symbol \hbar on input line 98.
|
||||
LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold'
|
||||
(Font) U/euf/m/n --> U/euf/b/n on input line 106.
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/latex/enumitem/enumitem.sty
|
||||
Package: enumitem 2025/02/06 v3.11 Customized lists
|
||||
\labelindent=\skip54
|
||||
\enit@outerparindent=\dimen277
|
||||
\enit@toks=\toks39
|
||||
\enit@inbox=\box65
|
||||
\enit@count@id=\count315
|
||||
\enitdp@description=\count316
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/tcolorbox/tcolorbox.sty
|
||||
Package: tcolorbox 2024/10/22 version 6.4.1 text color boxes
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/tools/verbatim.sty
|
||||
Package: verbatim 2024-01-22 v1.5x LaTeX2e package for verbatim enhancements
|
||||
\every@verbatim=\toks40
|
||||
\verbatim@line=\toks41
|
||||
\verbatim@in@stream=\read4
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/environ/environ.sty
|
||||
Package: environ 2014/05/04 v0.3 A new way to define environments
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/trimspaces/trimspaces.sty
|
||||
Package: trimspaces 2009/09/17 v1.1 Trim spaces around a token list
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/latex/etoolbox/etoolbox.sty
|
||||
Package: etoolbox 2025/02/11 v2.5l e-TeX tools for LaTeX (JAW)
|
||||
\etb@tempcnta=\count317
|
||||
)
|
||||
\tcb@titlebox=\box66
|
||||
\tcb@upperbox=\box67
|
||||
\tcb@lowerbox=\box68
|
||||
\tcb@phantombox=\box69
|
||||
\c@tcbbreakpart=\count318
|
||||
\c@tcblayer=\count319
|
||||
\c@tcolorbox@number=\count320
|
||||
\l__tcobox_tmpa_box=\box70
|
||||
\l__tcobox_tmpa_dim=\dimen278
|
||||
\tcb@temp=\box71
|
||||
\tcb@temp=\box72
|
||||
\tcb@temp=\box73
|
||||
\tcb@temp=\box74
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty
|
||||
Package: fancyhdr 2025/02/07 v5.2 Extensive control of page headers and footers
|
||||
|
||||
\f@nch@headwidth=\skip55
|
||||
\f@nch@offset@elh=\skip56
|
||||
\f@nch@offset@erh=\skip57
|
||||
\f@nch@offset@olh=\skip58
|
||||
\f@nch@offset@orh=\skip59
|
||||
\f@nch@offset@elf=\skip60
|
||||
\f@nch@offset@erf=\skip61
|
||||
\f@nch@offset@olf=\skip62
|
||||
\f@nch@offset@orf=\skip63
|
||||
\f@nch@height=\skip64
|
||||
\f@nch@footalignment=\skip65
|
||||
\f@nch@widthL=\skip66
|
||||
\f@nch@widthC=\skip67
|
||||
\f@nch@widthR=\skip68
|
||||
\@temptokenb=\toks42
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/latex/tools/multicol.sty
|
||||
Package: multicol 2024/09/14 v1.9i multicolumn formatting (FMi)
|
||||
\c@tracingmulticols=\count321
|
||||
\mult@box=\box75
|
||||
\multicol@leftmargin=\dimen279
|
||||
\c@unbalance=\count322
|
||||
\c@collectmore=\count323
|
||||
\doublecol@number=\count324
|
||||
\multicoltolerance=\count325
|
||||
\multicolpretolerance=\count326
|
||||
\full@width=\dimen280
|
||||
\page@free=\dimen281
|
||||
\premulticols=\dimen282
|
||||
\postmulticols=\dimen283
|
||||
\multicolsep=\skip69
|
||||
\multicolbaselineskip=\skip70
|
||||
\partial@page=\box76
|
||||
\last@line=\box77
|
||||
\mc@boxedresult=\box78
|
||||
\maxbalancingoverflow=\dimen284
|
||||
\mult@rightbox=\box79
|
||||
\mult@grightbox=\box80
|
||||
\mult@firstbox=\box81
|
||||
\mult@gfirstbox=\box82
|
||||
\@tempa=\box83
|
||||
\@tempa=\box84
|
||||
\@tempa=\box85
|
||||
\@tempa=\box86
|
||||
\@tempa=\box87
|
||||
\@tempa=\box88
|
||||
\@tempa=\box89
|
||||
\@tempa=\box90
|
||||
\@tempa=\box91
|
||||
\@tempa=\box92
|
||||
\@tempa=\box93
|
||||
\@tempa=\box94
|
||||
\@tempa=\box95
|
||||
\@tempa=\box96
|
||||
\@tempa=\box97
|
||||
\@tempa=\box98
|
||||
\@tempa=\box99
|
||||
\@tempa=\box100
|
||||
\@tempa=\box101
|
||||
\@tempa=\box102
|
||||
\@tempa=\box103
|
||||
\@tempa=\box104
|
||||
\@tempa=\box105
|
||||
\@tempa=\box106
|
||||
\@tempa=\box107
|
||||
\@tempa=\box108
|
||||
\@tempa=\box109
|
||||
\@tempa=\box110
|
||||
\@tempa=\box111
|
||||
\@tempa=\box112
|
||||
\@tempa=\box113
|
||||
\@tempa=\box114
|
||||
\@tempa=\box115
|
||||
\@tempa=\box116
|
||||
\@tempa=\box117
|
||||
\@tempa=\box118
|
||||
\c@minrows=\count327
|
||||
\c@columnbadness=\count328
|
||||
\c@finalcolumnbadness=\count329
|
||||
\last@try=\dimen285
|
||||
\multicolovershoot=\dimen286
|
||||
\multicolundershoot=\dimen287
|
||||
\mult@nat@firstbox=\box119
|
||||
\colbreak@box=\box120
|
||||
\mc@col@check@num=\count330
|
||||
)
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
|
||||
shapes.geometric.code.tex
|
||||
File: tikzlibraryshapes.geometric.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geomet
|
||||
ric.code.tex
|
||||
File: pgflibraryshapes.geometric.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
))
|
||||
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
|
||||
calc.code.tex
|
||||
File: tikzlibrarycalc.code.tex 2023-01-15 v3.1.10 (3.1.10)
|
||||
) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
|
||||
File: l3backend-pdftex.def 2024-05-08 L3 backend support: PDF output (pdfTeX)
|
||||
\l__color_backend_stack_int=\count331
|
||||
\l__pdf_internal_box=\box121
|
||||
) (./activite1.aux
|
||||
(/usr/share/texmf-dist/tex/generic/babel/locale/fr/babel-french.tex
|
||||
Package babel Info: Importing font and identification data for french
|
||||
(babel) from babel-fr.ini. Reported on input line 11.
|
||||
))
|
||||
\openout1 = `activite1.aux'.
|
||||
|
||||
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 33.
|
||||
LaTeX Font Info: ... okay on input line 33.
|
||||
LaTeX Info: Redefining \degres on input line 33.
|
||||
Package french.ldf Info: Setting StandardItemizeEnv=true for
|
||||
(french.ldf) compatibility with enumitem package,
|
||||
(french.ldf) reported on input line 33.
|
||||
Package french.ldf Info: Setting StandardEnumerateEnv=true for
|
||||
(french.ldf) compatibility with enumitem package,
|
||||
(french.ldf) reported on input line 33.
|
||||
LaTeX Info: Redefining \up on input line 33.
|
||||
|
||||
*geometry* driver: auto-detecting
|
||||
*geometry* detected driver: pdftex
|
||||
*geometry* verbose mode - [ preamble ] result:
|
||||
* driver: pdftex
|
||||
* paper: a4paper
|
||||
* layout: <same size as paper>
|
||||
* layoutoffset:(h,v)=(0.0pt,0.0pt)
|
||||
* modes:
|
||||
* h-part:(L,W,R)=(56.9055pt, 483.69687pt, 56.9055pt)
|
||||
* v-part:(T,H,B)=(56.9055pt, 731.23584pt, 56.9055pt)
|
||||
* \paperwidth=597.50787pt
|
||||
* \paperheight=845.04684pt
|
||||
* \textwidth=483.69687pt
|
||||
* \textheight=731.23584pt
|
||||
* \oddsidemargin=-15.36449pt
|
||||
* \evensidemargin=-15.36449pt
|
||||
* \topmargin=-52.36449pt
|
||||
* \headheight=12.0pt
|
||||
* \headsep=25.0pt
|
||||
* \topskip=11.0pt
|
||||
* \footskip=30.0pt
|
||||
* \marginparwidth=50.0pt
|
||||
* \marginparsep=10.0pt
|
||||
* \columnsep=10.0pt
|
||||
* \skip\footins=10.0pt plus 4.0pt minus 2.0pt
|
||||
* \hoffset=0.0pt
|
||||
* \voffset=0.0pt
|
||||
* \mag=1000
|
||||
* \@twocolumnfalse
|
||||
* \@twosidefalse
|
||||
* \@mparswitchfalse
|
||||
* \@reversemarginfalse
|
||||
* (1in=72.27pt=25.4mm, 1cm=28.453pt)
|
||||
|
||||
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
|
||||
[Loading MPS to PDF converter (version 2006.09.02).]
|
||||
\scratchcounter=\count332
|
||||
\scratchdimen=\dimen288
|
||||
\scratchbox=\box122
|
||||
\nofMPsegments=\count333
|
||||
\nofMParguments=\count334
|
||||
\everyMPshowfont=\toks43
|
||||
\MPscratchCnt=\count335
|
||||
\MPscratchDim=\dimen289
|
||||
\MPnumerator=\count336
|
||||
\makeMPintoPDFobject=\count337
|
||||
\everyMPtoPDFconversion=\toks44
|
||||
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
|
||||
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
|
||||
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
|
||||
85.
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
|
||||
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
|
||||
e
|
||||
))
|
||||
LaTeX Font Info: Trying to load font information for U+msa on input line 59.
|
||||
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/amsfonts/umsa.fd
|
||||
File: umsa.fd 2013/01/14 v3.01 AMS symbols A
|
||||
)
|
||||
LaTeX Font Info: Trying to load font information for U+msb on input line 59.
|
||||
|
||||
|
||||
(/usr/share/texmf-dist/tex/latex/amsfonts/umsb.fd
|
||||
File: umsb.fd 2013/01/14 v3.01 AMS symbols B
|
||||
)
|
||||
|
||||
[1
|
||||
|
||||
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}{/usr/share/texmf-dist/fonts
|
||||
/enc/dvips/cm-super/cm-super-t1.enc}]
|
||||
|
||||
[2]
|
||||
|
||||
[3]
|
||||
|
||||
[4] (./activite1.aux)
|
||||
***********
|
||||
LaTeX2e <2024-11-01> patch level 2
|
||||
L3 programming layer <2025-01-18>
|
||||
***********
|
||||
)
|
||||
Here is how much of TeX's memory you used:
|
||||
19631 strings out of 475142
|
||||
396084 string characters out of 5765947
|
||||
784639 words of memory out of 5000000
|
||||
42341 multiletter control sequences out of 15000+600000
|
||||
571118 words of font info for 64 fonts, out of 8000000 for 9000
|
||||
14 hyphenation exceptions out of 8191
|
||||
102i,11n,107p,410b,737s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb></usr/share/
|
||||
texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/texmf-dist/font
|
||||
s/type1/public/amsfonts/cm/cmsy10.pfb></usr/share/texmf-dist/fonts/type1/public
|
||||
/amsfonts/symbols/msam10.pfb></usr/share/texmf-dist/fonts/type1/public/cm-super
|
||||
/sfbx1095.pfb></usr/share/texmf-dist/fonts/type1/public/cm-super/sfbx1200.pfb><
|
||||
/usr/share/texmf-dist/fonts/type1/public/cm-super/sfbx1440.pfb></usr/share/texm
|
||||
f-dist/fonts/type1/public/cm-super/sfbx2488.pfb></usr/share/texmf-dist/fonts/ty
|
||||
pe1/public/cm-super/sfrm1000.pfb></usr/share/texmf-dist/fonts/type1/public/cm-s
|
||||
uper/sfrm1095.pfb></usr/share/texmf-dist/fonts/type1/public/cm-super/sfti1095.p
|
||||
fb>
|
||||
Output written on activite1.pdf (4 pages, 182024 bytes).
|
||||
PDF statistics:
|
||||
76 PDF objects out of 1000 (max. 8388607)
|
||||
47 compressed objects within 1 object stream
|
||||
0 named destinations out of 1000 (max. 500000)
|
||||
13 words of extra memory for PDF output out of 10000 (max. 10000000)
|
||||
|
||||
BIN
activite1/activite1.pdf
Normal file
BIN
activite1/activite1.pdf
Normal file
Binary file not shown.
BIN
activite1/activite1.synctex.gz
Normal file
BIN
activite1/activite1.synctex.gz
Normal file
Binary file not shown.
301
activite1/activite1.tex
Normal file
301
activite1/activite1.tex
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
\documentclass[a4paper,11pt]{article}
|
||||
|
||||
% Packages
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[francais]{babel}
|
||||
\usepackage{geometry}
|
||||
\usepackage{tikz}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{tcolorbox}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{multicol}
|
||||
|
||||
% Configuration de la page
|
||||
\geometry{margin=2cm}
|
||||
\pagestyle{fancy}
|
||||
\fancyhf{}
|
||||
\fancyfoot[C]{Page \thepage/4 -- Activité k-NN}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
|
||||
% Couleurs personnalisées
|
||||
\definecolor{classA}{RGB}{231,76,60}
|
||||
\definecolor{classB}{RGB}{52,152,219}
|
||||
\definecolor{lightblue}{RGB}{227,242,253}
|
||||
\definecolor{lightyellow}{RGB}{255,243,205}
|
||||
|
||||
% Configuration TikZ
|
||||
\usetikzlibrary{shapes.geometric,calc}
|
||||
|
||||
\begin{document}
|
||||
|
||||
% ============= PAGE 1 =============
|
||||
\begin{center}
|
||||
{\Huge \textbf{Activité débranchée : k-NN}}\\[0.3cm]
|
||||
{\Large \textbf{Les k Plus Proches Voisins}}
|
||||
\end{center}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\textbf{Objectif :} Comprendre comment l'algorithme k-NN classifie un nouveau point en utilisant la proximité avec les points d'apprentissage.
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{tcolorbox}[colback=lightblue,colframe=blue!75!black,title=\textbf{Principe}]
|
||||
\begin{itemize}[leftmargin=*]
|
||||
\item On dispose d'un ensemble de points déjà classifiés (points d'apprentissage)
|
||||
\item Pour classifier un nouveau point, on cherche ses \textit{k} plus proches voisins
|
||||
\item La classe majoritaire parmi ces \textit{k} voisins devient la classe du nouveau point
|
||||
\end{itemize}
|
||||
\end{tcolorbox}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\section*{Exercice 1 : Classification avec k = 3}
|
||||
|
||||
Voici un ensemble de points sur un plan. Les cercles rouges (\textcolor{classA}{$\bullet$}) sont de classe A, les carrés bleus (\textcolor{classB}{$\blacksquare$}) sont de classe B. Le point noir ($\star$) est à classifier.
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[scale=1.2]
|
||||
% Grille
|
||||
\draw[gray!30, step=1] (0,0) grid (8,8);
|
||||
|
||||
% Axes et cadre
|
||||
\draw[thick] (0,0) rectangle (8,8);
|
||||
|
||||
% Points de classe A (cercles rouges)
|
||||
\foreach \point in {(1,7), (1.5,6), (2,7.5), (2.5,6.5), (1.5,5), (2.5,5.5), (3,7)} {
|
||||
\fill[classA] \point circle (0.15);
|
||||
}
|
||||
|
||||
% Points de classe B (carrés bleus)
|
||||
\fill[classB] (5,2) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]5,2) rectangle ++(0.24,0.24);
|
||||
|
||||
\fill[classB] (5.5,3) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]5.5,3) rectangle ++(0.24,0.24);
|
||||
|
||||
\fill[classB] (6,1.5) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]6,1.5) rectangle ++(0.24,0.24);
|
||||
|
||||
\fill[classB] (6.5,2.5) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]6.5,2.5) rectangle ++(0.24,0.24);
|
||||
|
||||
\fill[classB] (5.5,1) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]5.5,1) rectangle ++(0.24,0.24);
|
||||
|
||||
\fill[classB] (7,2) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]7,2) rectangle ++(0.24,0.24);
|
||||
|
||||
\fill[classB] (6.5,3.5) circle (0.15);
|
||||
\fill[classB] ([shift={(-0.12,-0.12)}]6.5,3.5) rectangle ++(0.24,0.24);
|
||||
|
||||
% Point à classifier (étoile noire)
|
||||
\node[star,star points=5,star point ratio=2.5,fill=black,draw=black,thick,minimum size=0.6cm] at (4,4.5) {};
|
||||
|
||||
\end{tikzpicture}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
% Légende
|
||||
\begin{tabular}{ccccc}
|
||||
\textcolor{classA}{$\bullet$} Classe A & \quad &
|
||||
\textcolor{classB}{$\blacksquare$} Classe B & \quad &
|
||||
$\star$ À classifier
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\subsection*{Questions :}
|
||||
|
||||
\begin{enumerate}
|
||||
\item Identifiez les 3 points les plus proches du point noir $\star$. Tracez les distances sur le graphique ci-dessus.
|
||||
|
||||
\vspace{0.3cm}
|
||||
Distances : \underline{\hspace{3cm}} \underline{\hspace{3cm}} \underline{\hspace{3cm}}
|
||||
|
||||
\vspace{0.3cm}
|
||||
\item Parmi ces 3 points, combien sont de classe A ? \underline{\hspace{1.5cm}} De classe B ? \underline{\hspace{1.5cm}}
|
||||
|
||||
\vspace{0.3cm}
|
||||
\item À quelle classe appartient donc le point noir avec $k = 3$ ? \underline{\hspace{3cm}}
|
||||
\end{enumerate}
|
||||
|
||||
% ============= PAGE 2 =============
|
||||
%\newpage
|
||||
|
||||
\section*{Exercice 2 : Influence du paramètre k}
|
||||
|
||||
Reprenez le même graphique que l'exercice 1.
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\subsection*{a) Avec k = 1 (1 seul voisin)}
|
||||
|
||||
\begin{itemize}
|
||||
\item Quel est le point le plus proche ? \underline{\hspace{5cm}}
|
||||
\item Classification du point noir : \underline{\hspace{5cm}}
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\subsection*{b) Avec k = 5 (5 voisins)}
|
||||
|
||||
\begin{itemize}
|
||||
\item Listez les 5 points les plus proches : \underline{\hspace{7cm}}
|
||||
|
||||
\vspace{0.2cm}
|
||||
\underline{\hspace{10cm}}
|
||||
|
||||
\item Nombre de classe A : \underline{\hspace{2cm}} \quad Nombre de classe B : \underline{\hspace{2cm}}
|
||||
\item Classification du point noir : \underline{\hspace{5cm}}
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\subsection*{c) Avec k = 7 (tous les voisins de chaque classe)}
|
||||
|
||||
\begin{itemize}
|
||||
\item Nombre de classe A : \underline{\hspace{2cm}} \quad Nombre de classe B : \underline{\hspace{2cm}}
|
||||
\item Classification du point noir : \underline{\hspace{5cm}}
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.8cm}
|
||||
|
||||
\begin{tcolorbox}[colback=lightyellow,colframe=orange!75!black,title=\textbf{Réflexion}]
|
||||
|
||||
\textbf{1. La classification change-t-elle selon la valeur de k ? Pourquoi ?}
|
||||
|
||||
\vspace{0.8cm}
|
||||
\underline{\hspace{14cm}}
|
||||
|
||||
\vspace{0.4cm}
|
||||
\underline{\hspace{14cm}}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\textbf{2. Que se passe-t-il si k est trop petit (k = 1) ?}
|
||||
|
||||
\vspace{0.8cm}
|
||||
\underline{\hspace{14cm}}
|
||||
|
||||
\vspace{0.4cm}
|
||||
\underline{\hspace{14cm}}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\textbf{3. Que se passe-t-il si k est trop grand (k = nombre total de points) ?}
|
||||
|
||||
\vspace{0.8cm}
|
||||
\underline{\hspace{14cm}}
|
||||
|
||||
\vspace{0.4cm}
|
||||
\underline{\hspace{14cm}}
|
||||
|
||||
\end{tcolorbox}
|
||||
|
||||
% ============= PAGE 3 =============
|
||||
\newpage
|
||||
|
||||
\section*{Exercice 3 : À vous de jouer !}
|
||||
|
||||
Créez votre propre situation de classification avec $k = 3$.
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[scale=1.3]
|
||||
% Grille fine
|
||||
\draw[gray!20, step=1] (0,0) grid (12,12);
|
||||
|
||||
% Axes et cadre
|
||||
\draw[very thick] (0,0) rectangle (12,12);
|
||||
|
||||
% Graduations
|
||||
\foreach \x in {0,2,4,6,8,10,12} {
|
||||
\node[below] at (\x,-0.2) {\small \x};
|
||||
}
|
||||
\foreach \y in {0,2,4,6,8,10,12} {
|
||||
\node[left] at (-0.2,\y) {\small \y};
|
||||
}
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\begin{tcolorbox}[colback=lightblue,colframe=blue!75!black,title=\textbf{Instructions}]
|
||||
\begin{itemize}
|
||||
\item Placez au moins 10 points : 5 cercles rouges (classe A) et 5 carrés bleus (classe B)
|
||||
\item Placez une étoile noire (point à classifier)
|
||||
\item Tracez les 3 distances les plus courtes
|
||||
\item Déterminez la classe du point noir
|
||||
\end{itemize}
|
||||
\end{tcolorbox}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\textbf{Classification finale :} \underline{\hspace{5cm}}
|
||||
|
||||
% ============= PAGE 4 =============
|
||||
\newpage
|
||||
|
||||
\section*{Pour aller plus loin}
|
||||
|
||||
\subsection*{Applications réelles de k-NN}
|
||||
|
||||
\begin{itemize}
|
||||
\item Reconnaissance d'écriture manuscrite
|
||||
\item Systèmes de recommandation (films, musique, produits)
|
||||
\item Diagnostic médical (classification de maladies)
|
||||
\item Détection de spam dans les emails
|
||||
\item Reconnaissance faciale
|
||||
\item Prévision météorologique
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\subsection*{Questions de réflexion}
|
||||
|
||||
\textbf{1. Avantages de k-NN :}
|
||||
\begin{itemize}
|
||||
\item Simple à comprendre et à implémenter
|
||||
\item Pas besoin d'entraînement complexe
|
||||
\item Fonctionne bien pour des frontières non linéaires
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\textbf{2. Limitations de k-NN :}
|
||||
\begin{itemize}
|
||||
\item Lent avec beaucoup de données (doit calculer toutes les distances)
|
||||
\item Sensible aux données aberrantes
|
||||
\item Nécessite de choisir la bonne valeur de $k$
|
||||
\item Ne fonctionne pas bien si les classes sont déséquilibrées
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\textbf{3. Comment choisir k ?}
|
||||
\begin{itemize}
|
||||
\item $k$ trop petit ($k=1$) : sensible au bruit, risque de sur-apprentissage
|
||||
\item $k$ trop grand : perd les détails, risque de sous-apprentissage
|
||||
\item Conseil : tester plusieurs valeurs et choisir celle qui donne les meilleurs résultats
|
||||
\item Souvent, $k$ impair pour éviter les égalités ($k = 3, 5, 7...$)
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\begin{tcolorbox}[colback=lightyellow,colframe=orange!75!black,title=\textbf{Défi bonus}]
|
||||
Imaginez une situation où k-NN pourrait donner un mauvais résultat. Dessinez cette situation sur une feuille séparée et expliquez pourquoi.
|
||||
|
||||
\vspace{0.5cm}
|
||||
\textit{Indices : Pensez aux données bruitées, aux classes déséquilibrées, ou aux points isolés...}
|
||||
\end{tcolorbox}
|
||||
|
||||
\end{document}
|
||||
388
activite1/communes-de-corse-en-corse-et-francais.csv
Normal file
388
activite1/communes-de-corse-en-corse-et-francais.csv
Normal file
File diff suppressed because one or more lines are too long
693
activite1/generate_interactive_map.py
Normal file
693
activite1/generate_interactive_map.py
Normal file
|
|
@ -0,0 +1,693 @@
|
|||
"""
|
||||
Script pour générer une page HTML interactive avec les données des villages corses embarquées.
|
||||
Usage: python generate_interactive_map.py villages_corse.csv
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import sys
|
||||
|
||||
def parse_coordinates(point_geo_str):
|
||||
"""Parse la colonne Point_Geo"""
|
||||
try:
|
||||
parts = point_geo_str.split(',')
|
||||
lat = float(parts[0].strip())
|
||||
lon = float(parts[1].strip())
|
||||
return lat, lon
|
||||
except:
|
||||
return None, None
|
||||
|
||||
def load_villages_from_csv(csv_file):
|
||||
"""Charge les villages depuis le CSV"""
|
||||
villages = []
|
||||
|
||||
with open(csv_file, 'r', encoding='utf-8') as f:
|
||||
reader = csv.reader(f, delimiter=';')
|
||||
next(reader) # Skip header
|
||||
|
||||
for row in reader:
|
||||
if len(row) >= 18:
|
||||
lat, lon = parse_coordinates(row[17])
|
||||
if lat and lon:
|
||||
villages.append({
|
||||
'name': row[0],
|
||||
'nameCorse': row[1],
|
||||
'lat': lat,
|
||||
'lon': lon,
|
||||
'dept': row[9],
|
||||
'altitude': float(row[15]) if row[15] else 0
|
||||
})
|
||||
|
||||
return villages
|
||||
|
||||
def generate_html(villages, output_file='knn_interactive_full.html'):
|
||||
"""Génère le fichier HTML avec les données embarquées"""
|
||||
|
||||
villages_json = json.dumps(villages, ensure_ascii=False)
|
||||
|
||||
html_content = f'''<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Classification k-NN Interactive - Corse</title>
|
||||
|
||||
<!-- Leaflet CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
|
||||
<style>
|
||||
* {{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}}
|
||||
|
||||
body {{
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}}
|
||||
|
||||
.container {{
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
||||
}}
|
||||
|
||||
.header {{
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}}
|
||||
|
||||
.header h1 {{
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
|
||||
.header p {{
|
||||
font-size: 1.2em;
|
||||
opacity: 0.9;
|
||||
}}
|
||||
|
||||
.main-content {{
|
||||
display: grid;
|
||||
grid-template-columns: 350px 1fr;
|
||||
gap: 0;
|
||||
}}
|
||||
|
||||
.sidebar {{
|
||||
background: #f5f5f5;
|
||||
padding: 30px;
|
||||
border-right: 2px solid #ddd;
|
||||
max-height: 900px;
|
||||
overflow-y: auto;
|
||||
}}
|
||||
|
||||
.controls {{
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}}
|
||||
|
||||
.controls h3 {{
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
font-size: 1.2em;
|
||||
}}
|
||||
|
||||
.control-group {{
|
||||
margin-bottom: 20px;
|
||||
}}
|
||||
|
||||
.control-group label {{
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
margin-bottom: 8px;
|
||||
}}
|
||||
|
||||
.slider-container {{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}}
|
||||
|
||||
input[type="range"] {{
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
border-radius: 5px;
|
||||
background: #ddd;
|
||||
outline: none;
|
||||
}}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {{
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #667eea;
|
||||
cursor: pointer;
|
||||
}}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #667eea;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}}
|
||||
|
||||
.k-value {{
|
||||
background: #667eea;
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
border-radius: 20px;
|
||||
font-weight: bold;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}}
|
||||
|
||||
.result-box {{
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}}
|
||||
|
||||
.result-box h3 {{
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}}
|
||||
|
||||
.prediction {{
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}}
|
||||
|
||||
.prediction.corse-sud {{
|
||||
background: #ffebee;
|
||||
color: #c62828;
|
||||
}}
|
||||
|
||||
.prediction.haute-corse {{
|
||||
background: #e3f2fd;
|
||||
color: #1565c0;
|
||||
}}
|
||||
|
||||
.prediction.no-result {{
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}}
|
||||
|
||||
.coords {{
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
|
||||
.votes {{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 15px;
|
||||
}}
|
||||
|
||||
.vote-item {{
|
||||
text-align: center;
|
||||
}}
|
||||
|
||||
.vote-count {{
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
}}
|
||||
|
||||
.vote-count.red {{
|
||||
color: #c62828;
|
||||
}}
|
||||
|
||||
.vote-count.blue {{
|
||||
color: #1565c0;
|
||||
}}
|
||||
|
||||
.neighbors-list {{
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
background: #fafafa;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}}
|
||||
|
||||
.neighbor-item {{
|
||||
padding: 10px;
|
||||
margin-bottom: 8px;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
border-left: 4px solid #ddd;
|
||||
}}
|
||||
|
||||
.neighbor-item.dept-2a {{
|
||||
border-left-color: #c62828;
|
||||
}}
|
||||
|
||||
.neighbor-item.dept-2b {{
|
||||
border-left-color: #1565c0;
|
||||
}}
|
||||
|
||||
.neighbor-name {{
|
||||
font-weight: bold;
|
||||
margin-bottom: 3px;
|
||||
}}
|
||||
|
||||
.neighbor-corse {{
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}}
|
||||
|
||||
.neighbor-distance {{
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}}
|
||||
|
||||
.instructions {{
|
||||
background: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}}
|
||||
|
||||
.instructions h4 {{
|
||||
margin-bottom: 10px;
|
||||
color: #856404;
|
||||
}}
|
||||
|
||||
.instructions ul {{
|
||||
margin-left: 20px;
|
||||
color: #856404;
|
||||
}}
|
||||
|
||||
.instructions li {{
|
||||
margin-bottom: 5px;
|
||||
}}
|
||||
|
||||
.legend {{
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}}
|
||||
|
||||
.legend h4 {{
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}}
|
||||
|
||||
.legend-item {{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}}
|
||||
|
||||
.legend-circle {{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #333;
|
||||
}}
|
||||
|
||||
.legend-circle.red {{
|
||||
background: #e74c3c;
|
||||
}}
|
||||
|
||||
.legend-circle.blue {{
|
||||
background: #3498db;
|
||||
}}
|
||||
|
||||
.legend-circle.marker {{
|
||||
background: #95a5a6;
|
||||
border: 3px solid #000;
|
||||
}}
|
||||
|
||||
#map {{
|
||||
height: 900px;
|
||||
cursor: crosshair;
|
||||
}}
|
||||
|
||||
.leaflet-popup-content {{
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}}
|
||||
|
||||
button {{
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}}
|
||||
|
||||
button:hover {{
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}}
|
||||
|
||||
.btn-reset {{
|
||||
background: #f44336;
|
||||
color: white;
|
||||
}}
|
||||
|
||||
.btn-random {{
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
}}
|
||||
|
||||
.stats {{
|
||||
background: #e3f2fd;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
color: #1565c0;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🗺️ Classification k-NN Interactive</h1>
|
||||
<p>Haute-Corse ou Corse du Sud ?</p>
|
||||
<div class="stats">
|
||||
<strong>{len(villages)}</strong> villages chargés
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="sidebar">
|
||||
<div class="instructions">
|
||||
<h4>📋 Instructions</h4>
|
||||
<ul>
|
||||
<li><strong>Cliquez</strong> n'importe où sur la carte</li>
|
||||
<li>Ajustez la valeur de <strong>k</strong> avec le curseur</li>
|
||||
<li>Observez les <strong>k plus proches villages</strong></li>
|
||||
<li>La <strong>classification</strong> se fait par vote majoritaire</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<h3>⚙️ Paramètres</h3>
|
||||
<div class="control-group">
|
||||
<label>Nombre de voisins (k) :</label>
|
||||
<div class="slider-container">
|
||||
<input type="range" id="kSlider" min="1" max="21" value="5" step="2">
|
||||
<span class="k-value" id="kValue">5</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-random" onclick="placeRandomPoint()">🎲 Point Aléatoire</button>
|
||||
<button class="btn-reset" onclick="resetClassification()">🔄 Réinitialiser</button>
|
||||
</div>
|
||||
|
||||
<div class="result-box" id="resultBox">
|
||||
<h3>🎯 Résultat</h3>
|
||||
<div class="prediction no-result" id="prediction">
|
||||
Cliquez sur la carte
|
||||
</div>
|
||||
<div class="coords" id="coords"></div>
|
||||
<div id="votesContainer" style="display: none;">
|
||||
<div class="votes">
|
||||
<div class="vote-item">
|
||||
<div class="vote-count red" id="votes2A">0</div>
|
||||
<div>Corse du Sud</div>
|
||||
</div>
|
||||
<div class="vote-item">
|
||||
<div class="vote-count blue" id="votes2B">0</div>
|
||||
<div>Haute-Corse</div>
|
||||
</div>
|
||||
</div>
|
||||
<h4>🏘️ Plus proches voisins :</h4>
|
||||
<div class="neighbors-list" id="neighborsList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<h4>Légende</h4>
|
||||
<div class="legend-item">
|
||||
<div class="legend-circle red"></div>
|
||||
<span>Corse du Sud (2A)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-circle blue"></div>
|
||||
<span>Haute-Corse (2B)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-circle marker"></div>
|
||||
<span>Point à classifier</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leaflet JS -->
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
|
||||
<script>
|
||||
// Données des villages (embarquées depuis le CSV)
|
||||
const villages = {villages_json};
|
||||
|
||||
let map, testMarker, neighborMarkers = [], neighborLines = [];
|
||||
let currentK = 5;
|
||||
|
||||
// Initialiser la carte
|
||||
function initMap() {{
|
||||
map = L.map('map').setView([42.15, 9.05], 9);
|
||||
|
||||
L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
}}).addTo(map);
|
||||
|
||||
// Ajouter tous les villages
|
||||
villages.forEach(village => {{
|
||||
const color = village.dept === '2A' ? '#e74c3c' : '#3498db';
|
||||
L.circleMarker([village.lat, village.lon], {{
|
||||
radius: 3,
|
||||
fillColor: color,
|
||||
color: '#333',
|
||||
weight: 1,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.5
|
||||
}}).bindPopup(`<b>${{village.name}}</b><br>${{village.nameCorse}}<br>${{village.dept}}`)
|
||||
.addTo(map);
|
||||
}});
|
||||
|
||||
// Ajouter le gestionnaire de clic
|
||||
map.on('click', onMapClick);
|
||||
}}
|
||||
|
||||
// Calcul de distance Haversine
|
||||
function haversineDistance(lat1, lon1, lat2, lon2) {{
|
||||
const R = 6371; // Rayon de la Terre en km
|
||||
const dLat = (lat2 - lat1) * Math.PI / 180;
|
||||
const dLon = (lon2 - lon1) * Math.PI / 180;
|
||||
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
||||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
||||
Math.sin(dLon/2) * Math.sin(dLon/2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
return R * c;
|
||||
}}
|
||||
|
||||
// Classification k-NN
|
||||
function knnClassify(lat, lon, k) {{
|
||||
// Calculer distances
|
||||
const distances = villages.map(village => ({{
|
||||
...village,
|
||||
distance: haversineDistance(lat, lon, village.lat, village.lon)
|
||||
}}));
|
||||
|
||||
// Trier par distance
|
||||
distances.sort((a, b) => a.distance - b.distance);
|
||||
|
||||
// Prendre les k plus proches
|
||||
const neighbors = distances.slice(0, k);
|
||||
|
||||
// Voter
|
||||
const votes = {{ '2A': 0, '2B': 0 }};
|
||||
neighbors.forEach(n => votes[n.dept]++);
|
||||
|
||||
const prediction = votes['2A'] > votes['2B'] ? '2A' : '2B';
|
||||
|
||||
return {{ prediction, neighbors, votes }};
|
||||
}}
|
||||
|
||||
// Gestionnaire de clic sur la carte
|
||||
function onMapClick(e) {{
|
||||
const lat = e.latlng.lat;
|
||||
const lon = e.latlng.lng;
|
||||
|
||||
classifyPoint(lat, lon);
|
||||
}}
|
||||
|
||||
// Classifier un point
|
||||
function classifyPoint(lat, lon) {{
|
||||
// Supprimer les anciens marqueurs
|
||||
if (testMarker) map.removeLayer(testMarker);
|
||||
neighborMarkers.forEach(m => map.removeLayer(m));
|
||||
neighborLines.forEach(l => map.removeLayer(l));
|
||||
neighborMarkers = [];
|
||||
neighborLines = [];
|
||||
|
||||
// Classifier
|
||||
const result = knnClassify(lat, lon, currentK);
|
||||
|
||||
// Ajouter le marqueur du point test
|
||||
testMarker = L.circleMarker([lat, lon], {{
|
||||
radius: 12,
|
||||
fillColor: '#95a5a6',
|
||||
color: '#000',
|
||||
weight: 3,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.8
|
||||
}}).addTo(map);
|
||||
|
||||
// Ajouter les marqueurs et lignes des voisins
|
||||
result.neighbors.forEach(neighbor => {{
|
||||
const nColor = neighbor.dept === '2A' ? '#e74c3c' : '#3498db';
|
||||
|
||||
// Ligne
|
||||
const line = L.polyline(
|
||||
[[lat, lon], [neighbor.lat, neighbor.lon]],
|
||||
{{color: nColor, weight: 2, opacity: 0.5}}
|
||||
).addTo(map);
|
||||
neighborLines.push(line);
|
||||
|
||||
// Marqueur
|
||||
const marker = L.circleMarker([neighbor.lat, neighbor.lon], {{
|
||||
radius: 8,
|
||||
fillColor: nColor,
|
||||
color: '#333',
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.8
|
||||
}}).bindPopup(`<b>${{neighbor.name}}</b><br>${{neighbor.nameCorse}}<br>${{neighbor.dept}}<br>Distance: ${{neighbor.distance.toFixed(2)}} km`)
|
||||
.addTo(map);
|
||||
neighborMarkers.push(marker);
|
||||
}});
|
||||
|
||||
// Afficher les résultats
|
||||
displayResults(lat, lon, result);
|
||||
}}
|
||||
|
||||
// Afficher les résultats
|
||||
function displayResults(lat, lon, result) {{
|
||||
const predictionDiv = document.getElementById('prediction');
|
||||
const coordsDiv = document.getElementById('coords');
|
||||
const votesContainer = document.getElementById('votesContainer');
|
||||
const votes2A = document.getElementById('votes2A');
|
||||
const votes2B = document.getElementById('votes2B');
|
||||
const neighborsList = document.getElementById('neighborsList');
|
||||
|
||||
// Coordonnées
|
||||
coordsDiv.textContent = `(${{lat.toFixed(4)}}, ${{lon.toFixed(4)}})`;
|
||||
|
||||
// Prédiction
|
||||
const deptName = result.prediction === '2A' ? 'Corse du Sud (2A)' : 'Haute-Corse (2B)';
|
||||
const cssClass = result.prediction === '2A' ? 'corse-sud' : 'haute-corse';
|
||||
predictionDiv.textContent = deptName;
|
||||
predictionDiv.className = 'prediction ' + cssClass;
|
||||
|
||||
// Votes
|
||||
votes2A.textContent = result.votes['2A'];
|
||||
votes2B.textContent = result.votes['2B'];
|
||||
votesContainer.style.display = 'block';
|
||||
|
||||
// Liste des voisins
|
||||
neighborsList.innerHTML = '';
|
||||
result.neighbors.forEach(neighbor => {{
|
||||
const div = document.createElement('div');
|
||||
div.className = `neighbor-item dept-${{neighbor.dept.toLowerCase()}}`;
|
||||
div.innerHTML = `
|
||||
<div class="neighbor-name">${{neighbor.name}} (${{neighbor.dept}})</div>
|
||||
<div class="neighbor-corse">${{neighbor.nameCorse}}</div>
|
||||
<div class="neighbor-distance">Distance: ${{neighbor.distance.toFixed(2)}} km</div>
|
||||
`;
|
||||
neighborsList.appendChild(div);
|
||||
}});
|
||||
}}
|
||||
|
||||
// Point aléatoire
|
||||
function placeRandomPoint() {{
|
||||
const lat = 41.3 + Math.random() * (43.0 - 41.3);
|
||||
const lon = 8.5 + Math.random() * (9.6 - 8.5);
|
||||
classifyPoint(lat, lon);
|
||||
map.setView([lat, lon], 10);
|
||||
}}
|
||||
|
||||
// Réinitialiser
|
||||
function resetClassification() {{
|
||||
if (testMarker) map.removeLayer(testMarker);
|
||||
neighborMarkers.forEach(m => map.removeLayer(m));
|
||||
neighborLines.forEach(l => map.removeLayer(l));
|
||||
neighborMarkers = [];
|
||||
neighborLines = [];
|
||||
testMarker = null;
|
||||
|
||||
document.getElementById('prediction').textContent = 'Cliquez sur la carte';
|
||||
document.getElementById('prediction').className = 'prediction no-result';
|
||||
document.getElementById('coords').textContent = '';
|
||||
document.getElementById('votesContainer').style.display = 'none';
|
||||
|
||||
map.setView([42.15, 9.05], 9);
|
||||
}}
|
||||
|
||||
// Gestionnaire du slider k
|
||||
document.getElementById('kSlider').addEventListener('input', function(e) {{
|
||||
currentK = parseInt(e.target.value);
|
||||
document.getElementById('kValue').textContent = currentK;
|
||||
|
||||
// Reclassifier si un point existe
|
||||
if (testMarker) {{
|
||||
const latlng = testMarker.getLatLng();
|
||||
classifyPoint(latlng.lat, latlng.lng);
|
||||
}}
|
||||
}});
|
||||
|
||||
// Initialisation
|
||||
initMap();
|
||||
</script>
|
||||
</body>
|
||||
</html>'''
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(html_content)
|
||||
|
||||
print(f"✅ Fichier HTML généré : {output_file}")
|
||||
print(f"📊 {len(villages)} villages inclus")
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python generate_interactive_map.py villages_corse.csv")
|
||||
sys.exit(1)
|
||||
|
||||
csv_file = sys.argv[1]
|
||||
villages = load_villages_from_csv(csv_file)
|
||||
generate_html(villages)
|
||||
137
activite1/knn_activite_debranchee.pdf
Normal file
137
activite1/knn_activite_debranchee.pdf
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R /F3 4 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/BaseFont /ZapfDingbats /Name /F3 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 11 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Author (anonymous) /CreationDate (D:20251023100834+00'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20251023100834+00'00') /Producer (ReportLab PDF Library - www.reportlab.com)
|
||||
/Subject (unspecified) /Title (untitled) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
11 0 obj
|
||||
<<
|
||||
/Count 4 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
12 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2409
|
||||
>>
|
||||
stream
|
||||
Gat%$CQI5[(&dQ-EPH@r8@a,3>+d9%qe-??]@1rM:BM;gX<^DSfp1pgU;+EtPXs3qfM45Ya)c3FmZX!('ZBMdib,SDHM^7jr?=d'HMg#p1JNr!ki]ud`$ItrE*8iC-8:b=8lgP62b[2ZU;nk!qOookce[PO)+!TXq0o:>k#;'+deECq&&8,?RN=]Bj0s^TTqcYOr%0d,1*\7be\#ZfqEb^^j%']uY5*`8s1.I8Xrl!CiL5ShMTK)hdPJekjMZ!*5I^.B!t>0K\(qR3^E-QnG2$lg+)(7?^%o/W]#1F_)]bYb;ibG_TkioF;nkCX\+PmW]<4Ag>'$ZC#c9+&U=QcNj.Y4L@i^f`ZlaJ+GM>-DolJm7#&ZoaOk/F>qdA'M)oqebbO-da'f,*g%#SqZ3<q:AU-0#Sf/MM,BmZ9'ETXAlm]>MD]20_Sod$'&hg/IMj*iqq0GH6Uqm"tkhr([r3tU3Dr1l0qi=!^t:Tn)Jn`f^01ULWFC:fl'YWE=fEe]M=]V/V,.%UJ>BoF@G%jeWU9Bu>LP.K-n/^q_N7IRj7db<-XrZ!DamSH.FHJ!Dj6o?bVipkg3;nD-,lf5)SMRu<rH8a"(W+T$H-CpQVnal3gZ"mMKTVCUo2"dPpqm!B>clYimY]Jgd(Us*tEJA2(V@(<YCcZl&hr?H2J%qt07Uc0PYUL:YYWI;M6e\=7ms\+]C0tgipsba6:TnKf+`L73#O[@+GP6QlJk8o:BPPoLeAEuj*)&M%2kg7T'A`r>-AR`%DRdjaQ`H)4GbC6pd159pk0^0Cs*5pS5HCC&H-rs_,K25J=u_Zn7d%oIW4[V]R$URCNL^"Q'/PbCTKt&Gr'Ies0V]noK[R4.=/f6FD.Q?IXj!QjAnVe\'6?dG'AA+FV+%HZ$9Pek:!cRlh8c(1-aX2l8m;TK!a.gYTUE7Z9M6U+BG5QK<f;hQkt5_I1l:@"'XSai8uj!(:7&%<16WUr]<M6e1gsHWSRk]>;mI``VY,!H/SPlANRi'nN5Z(,ct4\^MPY.,Qf6ssL8%lYiL!Wj(Bc$rTaZJgk]KYb_!/]&X.h9]dU8Vr29ln6]OF4d9OF/&ACcNY$RV8UhGV2.R1cAr2ZZ3K[OK=/ZSTk:F3X";\%uj&(AVeJ@19EtUE`a>i^btfj6:LtdP57Ya"d]H,eCYH^6F5Kgmp-1A6'MtAPAen,U/G?S7%WTVU1ZkYV'(DV6]':khN`0Zo5#V)Ok&XjtrF@l\nD'g]omBnV*ad,V#pZU1b;KJJkIS5'.sDk.,%Hb",DHQ=\8qm!Mc'+qetG>-oRYbHaj#<;%UM?$M^k`bZo>8<U,P-Xj2p92%YhI)b2W;@#J+1olT<!^:MGK"qkkO:Wd4.D`)_,uFkYD.-Sa]OtIge4:qWo+k]_,p]Qb7V/Q8G@nGUm_$J:'3c$c;&>&X;G0J7+`1(q1JlYHQ:ZeBcm3.&<E;&a@UoaDk/!!iA%<sm1JlYGP0#m5Q8>Ek;N*Dh@i(`4fP1NnO^5h<Yq](_7F2]Gk'3"\l!'N11@$HSCatB*U%r7Hj-Zm+.eG$_9QHHAQ=m'&s,uCP(*V$Ph0Zt9cZKl`V^pJA5,X>$D4t9U>tA+N+iTbf^k+,^HbiV3?'sco]Vs#u4.DI&ZI+Nj_R8V!1f<6PS%df#$\pgDc#K8k#Zr(#EKB442Z:Vu(?.aqrPq0TP_Y6Sp2rs&e%mt@W4b0s?Bkp"n.8E0^m83t("u[OOk*j*`*j:GmQ6YtCQ@GjaD5DMWKj(qs3#)P2<'YEMW:!3(cYO*<Zt*T@)u8+O9;AU[h0"O%LB\d9O`!uNYpmmM45>G:_k6A8VQ0QL>%aM5tmPQ&e<h<X%G:,_DCB_V,iG1d1VX>@GRhX)=rieMUO]6%iKZLku[ATdcd4E1pnni_\@2+][B\W)MjCI97fqdAg&DDWX@<l34_ZNfV;H_Zll\=XBRBpN&MId9N0@IFVB:*MbUP%@WnKVE%^UGk^ILn'7B3lJ^#AbnNQ[,I#0qslca\agOFO?8J^*]ZSQ`<qnJrpmD[UjhU:X$jlb;Nbj45^)gP+0,YT0"<E&a)fWP=ni@K()'kag@CYcoETlE2X`),UP"D&<WH9D@b&f'V8V3GVXo_O4L'e1b<rJ=Do<nc!3<0esE;Y-!Fl80&JMO/P3(LA'K^B$lb4qWr"#lf(d't2lij8$%/=$A#8pU'(HFb8"JfAQl=[f&-KJ#W(3&Dt@Ia-u\+]QsNapMNH]6f#s)f%kSLqkbD&]rBt`?t_IdcC5aK)WR)@d30_1.Ikm]oWRg-h>Hl)0fUpc.72iF7Q=XWoTUfHkTN/^V7=5Gc^aI9[_I&nTg)6Ud8.r48&F[3B@/&<[C=(.=5bEmhQ.Ed=I]=S[[U"B=RZ"WE/7;%lWkT=7*DAZDct2dF6dN45:6%N)?~>endstream
|
||||
endobj
|
||||
13 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 845
|
||||
>>
|
||||
stream
|
||||
GauHJD+iYL&H;+$QuCMAVjVs?TnjHl"Y$S7j$ANA:K)>VR2;"bqG&q]c^/D5J[`[Rc#/n]k':QNQQY6/`Flmb3(i/i(BP52c@HZZ85J/'h21G!8B,e3B"^qi'AB,L_@p%LOhC4G/n"GK2Df]A#8"*M?`SJ*LPk!7HWXlS[[9XWFA7:d4\1&gqYi,`qiF2snIgusW6*eMF;gH>W5<I$!t9@l,/?X2/ME%J5/?Y@hh=.1#Tj/+<rn1(@V)l/34oPX>eqfETC!8uTeGB%0&!%gmJTg;2F1sa.@ph;?;O&bd>&?$gC;O]l_5:FA`(A1WUn:5#Oq?TG_l47^2XW'\P\^>A_+</]Z@F^W2lje7&fIG@ATPOqF[?<8PiQ`,1P:oZ8@cA=$3l0&nR:qAX&&&:PuT=U:4r#`mD_lHX*rh!d\HPOXE83A27VCWu#E=[-%J\J]7\FG6l"EamRR4mM#jA(6P@D#c+"WCMBl?r4YF^"mYB%,]TYZN9)f6C1n#i6fGEBBT7+P8YXcS[85")80j%K9u_cGjd&:*1>E@)q\uq!jWt8m`Z-4VPACYV*<;!o?%`aELJW"QBLBmWAHk(r)>[qfl\bSVpUY:Gqmq<1IoEbpF6b<(kqchsP0qZ/Q=dFe2Cl0mSlY("W8L=@.=i[?G&(u8#>iRR.CWu\7CNC$2f][k:HM<\(9T'Z/QemD[<G?YKOsA)Nb^8=,[?.hU*UGcZkHb::WT)[[RH4`%[GCMat:M4#NOpsgsUSGP+9[6W=gIn`se<`Rdme[3aZT%NK's=];uu%E'/_Y+f#ef<C0Vu0)68pPhoT4bCn5&5F(BJGpFj;>IXhuIfKgqcK"~>endstream
|
||||
endobj
|
||||
14 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 832
|
||||
>>
|
||||
stream
|
||||
GasbY95i9E'Lqim'pi4Li\Ksm,o.1%6=PM]b=K^a;rj+oOWT,Koit;jRT=;tUt@\rSF=a^kCs)62ijTM&`>^&=78.g!SJK4KaP+]E0KZ5(VjVC)U6.pW6ba.(S68%)S%dJZ6NW0r!?(p(',[c_-Sl$^ZrOegAptj*u]BD$/%IP0BWlRB/)e:'k%5[mVRo-g[(d!i@gV2-GZU[:NU<oY0X_cY(j%;OLK(P<u,G3rsAr-K\%>d(!g@3QOc.f^N\3@-4S5OGR78&jP&n\L2t#rcj?a0LPJqdTo:I)&15)tMs@f+b;B173&m"8*U=Fq8tiN<)g%OJKq68)/l=[sf%ug3H'JphN=]mobXSeqBVgfIG@I%Jc-S54e2t"aCDennP'*/FVUp-)RauQIS_30!%[GBbnP3oDX-lDq\64/kFri/e6#1aDn'!]';PR5Ed>hnIC-!Hln\dJn9rjtYpIZgeM2bgh;&NpXn&.,t?D(3?-f0]Na%FY)nYFqHD-;jkI<S_oXYUouo3kg:-ar8>*k@M,[5/c8`hB8nQ$=[bl*[neNlf4$Z!m"Ks.4.';@2u%nCX0n<c-I\`R&^1\9crqI+0lm:C2_En!&dJ4gu1j+7b$;?/EK(/kJ`:S`lrG#gcCE_``YgR_&MbQi0?hIKA<l0-PGTJ@p^ZZ\@k'd'#oDmk<-&+4i:9qnl>.._q_DlLGLZW_7)cqhnPjEQ.&E*1CLrKXYWjF\>9V`/nD*&oPn$bH$6_L[V,C9q\H^V@2d4hWZH4hKQ6cclC5Dd-!*c_ejm0cT%&BS+^2"JM?,j*rhk\5G,Qnb%[!@i7n05A\d^UQPRA0~>endstream
|
||||
endobj
|
||||
15 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1422
|
||||
>>
|
||||
stream
|
||||
Gau0B?#SIW&:ErMfXGo3#pY<?,o%g<\b6(E&-TPE+8nN![#\jJKbTMOIslMi\$G%iDK7<1,dq1./8&U.aW\MC*e'Fb_!c?S]]R">R'2#f/:mU+r%W[7T#^UO4I7UkT(<sj.[_jIJjYj%N#/dMGltCbc<F+fXY'1QG>NL\4bNRF0`Q8;QNpd3ic!f8X#u@S_S*StO1mFi@?8fJE7O]j.Fhh;eq%+_P\Vhk6)A"2EI7Sm*'AZa!1kScMc_SXb)hI5nP$m[O:.Xkn@($^[T1=-eWq,R?\kYfB(/hgUK[-MNBUl7`f6`"o&G4;5a82bMm^XQ+Arr7!fPaSe;ZPF`]J81of;ZQX1T>*ip7@PPd3E;q*anlW--k*_3E;Ci=u]#,=P`ckKIZg+'$^e>#e%a-BSIE1)9$p#pBhjquYu+eNft8/(>'b-r[`VTNT9t4;-!*^/Ce)odQ<*'MLn4T4%)=0t,)!D*\5dSp.WD]/@_-9%V3fZ6Y^gMd?4Hf5@rH[@V,9ebT:gI#hV:i3P&-MMhV\Z=7V^B5k7Ym@`%ZCr"el9Eu&V,RTZ"RMSXeEpOD=N,COJ?Y`c""LFt@HE5d-7mdgI,Pj'aZI/'4mF2$'#dT[@6LY)/469kL*@;1i+/2kkGd>kQ(`6,bB8ePG9?uP(a:Eg)]/FoBeZ^mAKq:W56BtT[CBm3OC@PSfcDaN2LJS0s4Q2[V/P2.b7;,RKi!4Bb'eq5K#f/NZ);LV!%-jn&r_K/3cE\^Q&(J06\^a`IfF6lbcM,7TTmS#0*MuHZ)=30Z92CISkkq*o%&f+(BT/QXQ\daq==SN!k,ZIClc]u9VYn=qUKVDJdFsY.EeKkq&IU8c;/\gbCWsU\do"8;3fF@Q[=VsD`>/P'3!)"9n2&2G3(a_O0sr?`p#KS&R57-lOMLYCU",g1;B5YHpSKjlELXYeBo.dOP[s:pFBX:Yc*Pf2K#)e==(1fGJo0GT=4n!0SRkZ?30Y@!CtD%X#P?GR[X#n0BCH\lA-#e9gkP!"NPkZ($ch!0ePl#IZnL".dNIKP9B/]07D))W<CkbX>E.JK97qE?!G1HMb;aK#M_mH<cM'<!g=/ZK*ZfKk.>+L=47bJTd4[PIFM@iu8=0DuWb_+iis13LS?FXK:5snA(!^lL(pb;G^W"*\UC`gGQulgZNpDmD,<C.[<j`6,PX^*(mm*C1H4Cg&m.#=MpGs%&g0>oo`ToA94][:aefW/[[p1mi-Q&UI4GrZAUXl<mT>759(^tl++*@sBQ\pm',`5Ucc-QQN+2k4Dhr7(9^B<ie%SW[<c>oM;1&$5SLe(0%%/[EqlVPYpHg3A>K]psR%ce&sJ6--#jJPi*^$c@kefZ=HE,ct8&BfZ62$1!#'Ru?sG)'Z6I[RtT>[_qqa(@PWVg]A*r%l1.cNI$=J]!u'N7!,N~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 16
|
||||
0000000000 65535 f
|
||||
0000000073 00000 n
|
||||
0000000124 00000 n
|
||||
0000000231 00000 n
|
||||
0000000343 00000 n
|
||||
0000000426 00000 n
|
||||
0000000631 00000 n
|
||||
0000000836 00000 n
|
||||
0000001041 00000 n
|
||||
0000001246 00000 n
|
||||
0000001315 00000 n
|
||||
0000001612 00000 n
|
||||
0000001690 00000 n
|
||||
0000004191 00000 n
|
||||
0000005127 00000 n
|
||||
0000006050 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<2232646b4bd42a525eb8afd576232bc1><2232646b4bd42a525eb8afd576232bc1>]
|
||||
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
|
||||
|
||||
/Info 10 0 R
|
||||
/Root 9 0 R
|
||||
/Size 16
|
||||
>>
|
||||
startxref
|
||||
7564
|
||||
%%EOF
|
||||
BIN
activite1/knn_activite_latex.pdf
Normal file
BIN
activite1/knn_activite_latex.pdf
Normal file
Binary file not shown.
561
activite1/knn_corse_interactive.ipynb
Normal file
561
activite1/knn_corse_interactive.ipynb
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🗺️ Classification k-NN : Haute-Corse ou Corse du Sud ?\n",
|
||||
"\n",
|
||||
"## Objectif\n",
|
||||
"Utiliser l'algorithme des **k plus proches voisins (k-NN)** pour déterminer si un point de la carte de Corse se situe en **Haute-Corse (2B)** ou en **Corse du Sud (2A)**, en se basant sur les villages les plus proches.\n",
|
||||
"\n",
|
||||
"## Principe\n",
|
||||
"1. On charge les données des villages corses avec leurs coordonnées GPS et leur département\n",
|
||||
"2. On choisit un point sur la carte\n",
|
||||
"3. On calcule les distances entre ce point et tous les villages\n",
|
||||
"4. On identifie les k villages les plus proches\n",
|
||||
"5. On vote : le département majoritaire parmi ces k villages devient la prédiction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📦 Installation et imports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Installation des bibliothèques nécessaires (si besoin)\n",
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install folium pandas numpy -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import folium\n",
|
||||
"from folium.plugins import MarkerCluster\n",
|
||||
"import math\n",
|
||||
"from collections import Counter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📊 Chargement des données"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Charger le fichier CSV\n",
|
||||
"# Remplacez 'villages_corse.csv' par le chemin de votre fichier\n",
|
||||
"df = pd.read_csv('villages_corse.csv', sep=';', encoding='utf-8')\n",
|
||||
"\n",
|
||||
"# Afficher les premières lignes\n",
|
||||
"print(f\"Nombre de villages : {len(df)}\")\n",
|
||||
"df.head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🔧 Préparation des données"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def parse_coordinates(point_geo_str):\n",
|
||||
" \"\"\"\n",
|
||||
" Parse la colonne Point_Geo pour extraire latitude et longitude.\n",
|
||||
" Format attendu : \"POINT(longitude latitude)\" ou similaire\n",
|
||||
" \"\"\"\n",
|
||||
" try:\n",
|
||||
" # Supprimer 'POINT(' et ')'\n",
|
||||
" coords = point_geo_str.replace('POINT(', '').replace(')', '').strip()\n",
|
||||
" lon, lat = coords.split()\n",
|
||||
" return float(lat), float(lon)\n",
|
||||
" except:\n",
|
||||
" return None, None\n",
|
||||
"\n",
|
||||
"# Extraire les coordonnées\n",
|
||||
"df[['latitude', 'longitude']] = df['Point_Geo'].apply(\n",
|
||||
" lambda x: pd.Series(parse_coordinates(x))\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Supprimer les lignes sans coordonnées valides\n",
|
||||
"df = df.dropna(subset=['latitude', 'longitude'])\n",
|
||||
"\n",
|
||||
"# Simplifier les noms de départements\n",
|
||||
"df['dept_simple'] = df['Code Département'].apply(lambda x: '2A' if x == '2A' else '2B')\n",
|
||||
"\n",
|
||||
"print(f\"Villages avec coordonnées valides : {len(df)}\")\n",
|
||||
"print(f\"\\nRépartition par département :\")\n",
|
||||
"print(df['dept_simple'].value_counts())\n",
|
||||
"\n",
|
||||
"df[['Nom français', 'dept_simple', 'latitude', 'longitude']].head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📏 Fonction de calcul de distance\n",
|
||||
"\n",
|
||||
"Nous utilisons la **formule de Haversine** pour calculer la distance entre deux points GPS sur la surface de la Terre."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def haversine_distance(lat1, lon1, lat2, lon2):\n",
|
||||
" \"\"\"\n",
|
||||
" Calcule la distance en kilomètres entre deux points GPS.\n",
|
||||
" Formule de Haversine.\n",
|
||||
" \"\"\"\n",
|
||||
" R = 6371 # Rayon de la Terre en km\n",
|
||||
" \n",
|
||||
" # Conversion en radians\n",
|
||||
" lat1_rad = math.radians(lat1)\n",
|
||||
" lat2_rad = math.radians(lat2)\n",
|
||||
" delta_lat = math.radians(lat2 - lat1)\n",
|
||||
" delta_lon = math.radians(lon2 - lon1)\n",
|
||||
" \n",
|
||||
" # Formule de Haversine\n",
|
||||
" a = math.sin(delta_lat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2\n",
|
||||
" c = 2 * math.asin(math.sqrt(a))\n",
|
||||
" \n",
|
||||
" return R * c\n",
|
||||
"\n",
|
||||
"# Test de la fonction\n",
|
||||
"# Distance entre Ajaccio et Bastia (environ 100 km)\n",
|
||||
"dist_test = haversine_distance(41.9267, 8.7369, 42.7028, 9.4500)\n",
|
||||
"print(f\"Distance Ajaccio-Bastia : {dist_test:.1f} km\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎯 Algorithme k-NN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def knn_classify(test_lat, test_lon, df, k=5):\n",
|
||||
" \"\"\"\n",
|
||||
" Classifie un point (test_lat, test_lon) en utilisant k-NN.\n",
|
||||
" \n",
|
||||
" Retourne :\n",
|
||||
" - prediction : le département prédit ('2A' ou '2B')\n",
|
||||
" - neighbors : DataFrame des k plus proches voisins\n",
|
||||
" - votes : dictionnaire des votes\n",
|
||||
" \"\"\"\n",
|
||||
" # Calculer les distances pour tous les villages\n",
|
||||
" distances = []\n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" dist = haversine_distance(test_lat, test_lon, row['latitude'], row['longitude'])\n",
|
||||
" distances.append({\n",
|
||||
" 'village': row['Nom français'],\n",
|
||||
" 'departement': row['dept_simple'],\n",
|
||||
" 'latitude': row['latitude'],\n",
|
||||
" 'longitude': row['longitude'],\n",
|
||||
" 'distance': dist\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" # Créer un DataFrame et trier par distance\n",
|
||||
" dist_df = pd.DataFrame(distances)\n",
|
||||
" dist_df = dist_df.sort_values('distance')\n",
|
||||
" \n",
|
||||
" # Sélectionner les k plus proches\n",
|
||||
" neighbors = dist_df.head(k)\n",
|
||||
" \n",
|
||||
" # Voter\n",
|
||||
" votes = Counter(neighbors['departement'])\n",
|
||||
" prediction = votes.most_common(1)[0][0]\n",
|
||||
" \n",
|
||||
" return prediction, neighbors, votes\n",
|
||||
"\n",
|
||||
"# Test de l'algorithme avec un point au centre de la Corse\n",
|
||||
"test_lat, test_lon = 42.15, 9.05\n",
|
||||
"k = 5\n",
|
||||
"\n",
|
||||
"prediction, neighbors, votes = knn_classify(test_lat, test_lon, df, k=k)\n",
|
||||
"\n",
|
||||
"print(f\"\\n🎯 Point de test : ({test_lat}, {test_lon})\")\n",
|
||||
"print(f\"\\nAvec k={k} :\")\n",
|
||||
"print(f\"Prédiction : {prediction}\")\n",
|
||||
"print(f\"Votes : {dict(votes)}\")\n",
|
||||
"print(f\"\\nLes {k} plus proches voisins :\")\n",
|
||||
"print(neighbors[['village', 'departement', 'distance']])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🗺️ Visualisation avec Folium"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def create_map(test_lat=None, test_lon=None, k=5, show_all_villages=False):\n",
|
||||
" \"\"\"\n",
|
||||
" Crée une carte interactive avec Folium.\n",
|
||||
" \"\"\"\n",
|
||||
" # Centre de la Corse\n",
|
||||
" center_lat = 42.15\n",
|
||||
" center_lon = 9.05\n",
|
||||
" \n",
|
||||
" # Créer la carte\n",
|
||||
" m = folium.Map(\n",
|
||||
" location=[center_lat, center_lon],\n",
|
||||
" zoom_start=9,\n",
|
||||
" tiles='OpenStreetMap'\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Afficher tous les villages (optionnel, peut être lourd)\n",
|
||||
" if show_all_villages:\n",
|
||||
" marker_cluster = MarkerCluster().add_to(m)\n",
|
||||
" \n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" color = 'red' if row['dept_simple'] == '2A' else 'blue'\n",
|
||||
" folium.CircleMarker(\n",
|
||||
" location=[row['latitude'], row['longitude']],\n",
|
||||
" radius=2,\n",
|
||||
" color=color,\n",
|
||||
" fill=True,\n",
|
||||
" fillColor=color,\n",
|
||||
" fillOpacity=0.3,\n",
|
||||
" popup=f\"{row['Nom français']} ({row['dept_simple']})\"\n",
|
||||
" ).add_to(marker_cluster)\n",
|
||||
" \n",
|
||||
" # Si un point de test est fourni\n",
|
||||
" if test_lat is not None and test_lon is not None:\n",
|
||||
" # Classification\n",
|
||||
" prediction, neighbors, votes = knn_classify(test_lat, test_lon, df, k=k)\n",
|
||||
" \n",
|
||||
" # Marqueur pour le point de test\n",
|
||||
" color = 'darkred' if prediction == '2A' else 'darkblue'\n",
|
||||
" folium.Marker(\n",
|
||||
" location=[test_lat, test_lon],\n",
|
||||
" popup=f\"Point à classifier<br>Prédiction : {prediction}<br>Votes : {dict(votes)}\",\n",
|
||||
" icon=folium.Icon(color=color, icon='star', prefix='fa')\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" # Afficher les k plus proches voisins\n",
|
||||
" for idx, neighbor in neighbors.iterrows():\n",
|
||||
" # Marqueur pour chaque voisin\n",
|
||||
" color = 'red' if neighbor['departement'] == '2A' else 'blue'\n",
|
||||
" folium.Marker(\n",
|
||||
" location=[neighbor['latitude'], neighbor['longitude']],\n",
|
||||
" popup=f\"{neighbor['village']}<br>{neighbor['departement']}<br>Distance: {neighbor['distance']:.2f} km\",\n",
|
||||
" icon=folium.Icon(color=color, icon='info-sign')\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" # Ligne entre le point test et le voisin\n",
|
||||
" folium.PolyLine(\n",
|
||||
" locations=[\n",
|
||||
" [test_lat, test_lon],\n",
|
||||
" [neighbor['latitude'], neighbor['longitude']]\n",
|
||||
" ],\n",
|
||||
" color=color,\n",
|
||||
" weight=2,\n",
|
||||
" opacity=0.5\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" # Légende\n",
|
||||
" legend_html = '''\n",
|
||||
" <div style=\"position: fixed; \n",
|
||||
" bottom: 50px; right: 50px; width: 200px; height: 120px; \n",
|
||||
" background-color: white; border:2px solid grey; z-index:9999; \n",
|
||||
" font-size:14px; padding: 10px\">\n",
|
||||
" <p><strong>Légende</strong></p>\n",
|
||||
" <p><i class=\"fa fa-circle\" style=\"color:red\"></i> Corse du Sud (2A)</p>\n",
|
||||
" <p><i class=\"fa fa-circle\" style=\"color:blue\"></i> Haute-Corse (2B)</p>\n",
|
||||
" <p><i class=\"fa fa-star\" style=\"color:darkred\"></i> Point à classifier</p>\n",
|
||||
" </div>\n",
|
||||
" '''\n",
|
||||
" m.get_root().html.add_child(folium.Element(legend_html))\n",
|
||||
" \n",
|
||||
" return m\n",
|
||||
"\n",
|
||||
"# Créer la carte avec le point de test\n",
|
||||
"map_with_test = create_map(test_lat=42.15, test_lon=9.05, k=5, show_all_villages=False)\n",
|
||||
"map_with_test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🔬 Expérimentation : Influence de k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test avec différentes valeurs de k\n",
|
||||
"test_point = (42.15, 9.05) # Point au centre de la Corse\n",
|
||||
"\n",
|
||||
"print(f\"Point testé : {test_point}\\n\")\n",
|
||||
"print(f\"{'k':<5} {'Prédiction':<12} {'Votes 2A':<10} {'Votes 2B':<10}\")\n",
|
||||
"print(\"-\" * 45)\n",
|
||||
"\n",
|
||||
"for k in [1, 3, 5, 7, 9, 15]:\n",
|
||||
" prediction, neighbors, votes = knn_classify(test_point[0], test_point[1], df, k=k)\n",
|
||||
" votes_2a = votes.get('2A', 0)\n",
|
||||
" votes_2b = votes.get('2B', 0)\n",
|
||||
" print(f\"{k:<5} {prediction:<12} {votes_2a:<10} {votes_2b:<10}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎮 Mode interactif : Testez vos propres points !\n",
|
||||
"\n",
|
||||
"Modifiez les coordonnées ci-dessous pour tester différents points de la Corse."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# === MODIFIEZ CES VALEURS ===\n",
|
||||
"test_latitude = 42.5 # Entre 41.3 (sud) et 43.0 (nord)\n",
|
||||
"test_longitude = 9.2 # Entre 8.5 (ouest) et 9.5 (est)\n",
|
||||
"k_value = 7 # Nombre de voisins\n",
|
||||
"# =============================\n",
|
||||
"\n",
|
||||
"prediction, neighbors, votes = knn_classify(test_latitude, test_longitude, df, k=k_value)\n",
|
||||
"\n",
|
||||
"print(f\"📍 Point : ({test_latitude}, {test_longitude})\")\n",
|
||||
"print(f\"🔢 k = {k_value}\")\n",
|
||||
"print(f\"\\n🎯 Prédiction : {'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'}\")\n",
|
||||
"print(f\"\\n📊 Votes : {dict(votes)}\")\n",
|
||||
"print(f\"\\n🏘️ Les {k_value} plus proches villages :\")\n",
|
||||
"print(neighbors[['village', 'departement', 'distance']].to_string(index=False))\n",
|
||||
"\n",
|
||||
"# Afficher la carte\n",
|
||||
"map_interactive = create_map(test_latitude, test_longitude, k=k_value, show_all_villages=False)\n",
|
||||
"map_interactive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🌍 Carte complète avec tous les villages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Afficher tous les villages de Corse (peut être lent)\n",
|
||||
"# Attention : cette cellule peut prendre du temps à s'exécuter\n",
|
||||
"\n",
|
||||
"map_all = create_map(show_all_villages=True)\n",
|
||||
"map_all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📈 Visualisation de la frontière entre départements"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Créer une grille de points et classifier chacun\n",
|
||||
"# Cela permet de visualiser la \"frontière\" selon k-NN\n",
|
||||
"\n",
|
||||
"def create_decision_boundary_map(k=5, grid_resolution=50):\n",
|
||||
" \"\"\"\n",
|
||||
" Crée une carte montrant la frontière de décision de k-NN.\n",
|
||||
" \"\"\"\n",
|
||||
" # Limites de la Corse\n",
|
||||
" lat_min, lat_max = 41.3, 43.0\n",
|
||||
" lon_min, lon_max = 8.5, 9.6\n",
|
||||
" \n",
|
||||
" # Créer une grille\n",
|
||||
" lats = np.linspace(lat_min, lat_max, grid_resolution)\n",
|
||||
" lons = np.linspace(lon_min, lon_max, grid_resolution)\n",
|
||||
" \n",
|
||||
" m = folium.Map(\n",
|
||||
" location=[42.15, 9.05],\n",
|
||||
" zoom_start=8,\n",
|
||||
" tiles='OpenStreetMap'\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Classifier chaque point de la grille\n",
|
||||
" print(\"Classification de la grille en cours...\")\n",
|
||||
" for i, lat in enumerate(lats[::5]): # Réduire la résolution pour la vitesse\n",
|
||||
" for lon in lons[::5]:\n",
|
||||
" prediction, _, _ = knn_classify(lat, lon, df, k=k)\n",
|
||||
" color = 'red' if prediction == '2A' else 'blue'\n",
|
||||
" \n",
|
||||
" folium.CircleMarker(\n",
|
||||
" location=[lat, lon],\n",
|
||||
" radius=3,\n",
|
||||
" color=color,\n",
|
||||
" fill=True,\n",
|
||||
" fillColor=color,\n",
|
||||
" fillOpacity=0.2,\n",
|
||||
" weight=0\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" if (i+1) % 5 == 0:\n",
|
||||
" print(f\" {(i+1)*100//len(lats[::5])}% complété\")\n",
|
||||
" \n",
|
||||
" print(\"Terminé !\")\n",
|
||||
" return m\n",
|
||||
"\n",
|
||||
"# Créer la carte (peut prendre quelques secondes)\n",
|
||||
"print(f\"Création de la carte de frontière avec k=5...\")\n",
|
||||
"boundary_map = create_decision_boundary_map(k=5, grid_resolution=30)\n",
|
||||
"boundary_map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎓 Questions de réflexion\n",
|
||||
"\n",
|
||||
"1. **Influence de k** : Testez différentes valeurs de k (1, 3, 5, 10, 20). Comment la prédiction change-t-elle ?\n",
|
||||
"\n",
|
||||
"2. **Points frontières** : Trouvez des points où la classification change selon la valeur de k.\n",
|
||||
"\n",
|
||||
"3. **Zones ambiguës** : Où se situent les zones les plus difficiles à classifier ?\n",
|
||||
"\n",
|
||||
"4. **Validité** : Cette méthode est-elle toujours fiable ? Dans quels cas pourrait-elle échouer ?\n",
|
||||
"\n",
|
||||
"5. **Amélioration** : Comment pourrait-on améliorer l'algorithme ? (pondération par distance, normalisation, etc.)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 Pour aller plus loin\n",
|
||||
"\n",
|
||||
"### Exercices supplémentaires :\n",
|
||||
"\n",
|
||||
"1. **Pondération par distance** : Modifier l'algorithme pour donner plus de poids aux villages plus proches\n",
|
||||
"2. **Validation croisée** : Tester la précision en utilisant les villages eux-mêmes comme points de test\n",
|
||||
"3. **Autres critères** : Ajouter l'altitude comme dimension supplémentaire\n",
|
||||
"4. **Clustering** : Identifier des groupes de villages similaires"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# BONUS : Validation croisée\n",
|
||||
"# Tester la précision en utilisant les villages eux-mêmes\n",
|
||||
"\n",
|
||||
"def cross_validation(df, k=5, sample_size=100):\n",
|
||||
" \"\"\"\n",
|
||||
" Teste la précision de k-NN en utilisant un échantillon de villages.\n",
|
||||
" \"\"\"\n",
|
||||
" # Prendre un échantillon aléatoire\n",
|
||||
" sample = df.sample(n=min(sample_size, len(df)), random_state=42)\n",
|
||||
" \n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
" \n",
|
||||
" for idx, row in sample.iterrows():\n",
|
||||
" # Créer un dataset sans ce village\n",
|
||||
" df_without = df.drop(idx)\n",
|
||||
" \n",
|
||||
" # Classifier ce village\n",
|
||||
" prediction, _, _ = knn_classify(\n",
|
||||
" row['latitude'], \n",
|
||||
" row['longitude'], \n",
|
||||
" df_without, \n",
|
||||
" k=k\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" if prediction == row['dept_simple']:\n",
|
||||
" correct += 1\n",
|
||||
" total += 1\n",
|
||||
" \n",
|
||||
" accuracy = (correct / total) * 100\n",
|
||||
" return accuracy, correct, total\n",
|
||||
"\n",
|
||||
"print(\"Test de précision de l'algorithme k-NN...\\n\")\n",
|
||||
"\n",
|
||||
"for k in [1, 3, 5, 10]:\n",
|
||||
" accuracy, correct, total = cross_validation(df, k=k, sample_size=100)\n",
|
||||
" print(f\"k={k:2d} : {accuracy:.1f}% de précision ({correct}/{total} corrects)\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
375
activite1/knn_corse_interactive_click(1).ipynb
Normal file
375
activite1/knn_corse_interactive_click(1).ipynb
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🗺️ k-NN Corse : Version Interactive avec Clic sur Carte\n",
|
||||
"\n",
|
||||
"## 🎮 Mode d'emploi\n",
|
||||
"1. Exécutez toutes les cellules\n",
|
||||
"2. **Cliquez sur la carte** pour choisir un point\n",
|
||||
"3. Ajustez k avec le curseur\n",
|
||||
"4. Observez la classification en temps réel !"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📦 Installation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Installation des bibliothèques\n",
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install ipyleaflet ipywidgets pandas numpy -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import math\n",
|
||||
"from collections import Counter\n",
|
||||
"from ipyleaflet import Map, Marker, CircleMarker, Polyline, AwesomeIcon, LayerGroup\n",
|
||||
"from ipywidgets import HTML, VBox, HBox, IntSlider, Output, Label\n",
|
||||
"from IPython.display import display, clear_output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📊 Chargement des données"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Charger les données\n",
|
||||
"df = pd.read_csv('villages_corse.csv', sep='\\t', encoding='utf-8')\n",
|
||||
"\n",
|
||||
"def parse_coordinates(point_geo_str):\n",
|
||||
" try:\n",
|
||||
" parts = str(point_geo_str).split(',')\n",
|
||||
" lat = float(parts[0].strip())\n",
|
||||
" lon = float(parts[1].strip())\n",
|
||||
" return lat, lon\n",
|
||||
" except:\n",
|
||||
" return None, None\n",
|
||||
"\n",
|
||||
"df[['latitude', 'longitude']] = df['Point_Geo'].apply(\n",
|
||||
" lambda x: pd.Series(parse_coordinates(x))\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"df = df.dropna(subset=['latitude', 'longitude'])\n",
|
||||
"df['dept_simple'] = df['Code Département'].apply(lambda x: '2A' if str(x) == '2A' else '2B')\n",
|
||||
"\n",
|
||||
"print(f\"✅ {len(df)} villages chargés\")\n",
|
||||
"print(f\" - Corse du Sud (2A) : {len(df[df['dept_simple']=='2A'])}\")\n",
|
||||
"print(f\" - Haute-Corse (2B) : {len(df[df['dept_simple']=='2B'])}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🧮 Fonctions k-NN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def haversine_distance(lat1, lon1, lat2, lon2):\n",
|
||||
" \"\"\"Calcule la distance en km entre deux points GPS.\"\"\"\n",
|
||||
" R = 6371\n",
|
||||
" lat1_rad = math.radians(lat1)\n",
|
||||
" lat2_rad = math.radians(lat2)\n",
|
||||
" delta_lat = math.radians(lat2 - lat1)\n",
|
||||
" delta_lon = math.radians(lon2 - lon1)\n",
|
||||
" \n",
|
||||
" a = math.sin(delta_lat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2\n",
|
||||
" c = 2 * math.asin(math.sqrt(a))\n",
|
||||
" \n",
|
||||
" return R * c\n",
|
||||
"\n",
|
||||
"def knn_classify(test_lat, test_lon, df, k=5):\n",
|
||||
" \"\"\"Classifie un point avec k-NN.\"\"\"\n",
|
||||
" distances = []\n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" dist = haversine_distance(test_lat, test_lon, row['latitude'], row['longitude'])\n",
|
||||
" distances.append({\n",
|
||||
" 'village': row['Nom français'],\n",
|
||||
" 'nom_corse': row['Nom corse'],\n",
|
||||
" 'departement': row['dept_simple'],\n",
|
||||
" 'latitude': row['latitude'],\n",
|
||||
" 'longitude': row['longitude'],\n",
|
||||
" 'distance': dist\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" dist_df = pd.DataFrame(distances).sort_values('distance')\n",
|
||||
" neighbors = dist_df.head(k)\n",
|
||||
" votes = Counter(neighbors['departement'])\n",
|
||||
" prediction = votes.most_common(1)[0][0]\n",
|
||||
" \n",
|
||||
" return prediction, neighbors, votes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🗺️ Carte Interactive\n",
|
||||
"\n",
|
||||
"**Instructions :**\n",
|
||||
"- 🖱️ **Cliquez sur la carte** pour placer un point\n",
|
||||
"- 🎚️ **Ajustez k** avec le curseur\n",
|
||||
"- 👁️ La classification se met à jour automatiquement"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Créer la carte\n",
|
||||
"m = Map(center=(42.15, 9.05), zoom=9, scroll_wheel_zoom=True)\n",
|
||||
"\n",
|
||||
"# Couches pour les éléments dynamiques\n",
|
||||
"test_point_layer = LayerGroup()\n",
|
||||
"neighbors_layer = LayerGroup()\n",
|
||||
"lines_layer = LayerGroup()\n",
|
||||
"\n",
|
||||
"m.add_layer(test_point_layer)\n",
|
||||
"m.add_layer(neighbors_layer)\n",
|
||||
"m.add_layer(lines_layer)\n",
|
||||
"\n",
|
||||
"# Afficher quelques villages de référence\n",
|
||||
"sample_villages = df.sample(n=min(50, len(df)), random_state=42)\n",
|
||||
"for idx, row in sample_villages.iterrows():\n",
|
||||
" color = 'red' if row['dept_simple'] == '2A' else 'blue'\n",
|
||||
" circle = CircleMarker(\n",
|
||||
" location=(row['latitude'], row['longitude']),\n",
|
||||
" radius=3,\n",
|
||||
" color=color,\n",
|
||||
" fill_color=color,\n",
|
||||
" fill_opacity=0.4,\n",
|
||||
" weight=1\n",
|
||||
" )\n",
|
||||
" m.add_layer(circle)\n",
|
||||
"\n",
|
||||
"# Widget pour k\n",
|
||||
"k_slider = IntSlider(\n",
|
||||
" value=5,\n",
|
||||
" min=1,\n",
|
||||
" max=20,\n",
|
||||
" step=1,\n",
|
||||
" description='k:',\n",
|
||||
" continuous_update=False\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Zone de résultats\n",
|
||||
"result_output = Output()\n",
|
||||
"info_html = HTML(value=\"<p style='font-size:16px; padding:10px; background:#f0f0f0; border-radius:5px;'>👆 <b>Cliquez sur la carte pour classifier un point</b></p>\")\n",
|
||||
"\n",
|
||||
"# Variable globale pour stocker les coordonnées\n",
|
||||
"current_coords = {'lat': None, 'lon': None}\n",
|
||||
"\n",
|
||||
"def update_classification(lat, lon, k):\n",
|
||||
" \"\"\"Met à jour la classification et la visualisation.\"\"\"\n",
|
||||
" # Effacer les couches précédentes\n",
|
||||
" test_point_layer.clear_layers()\n",
|
||||
" neighbors_layer.clear_layers()\n",
|
||||
" lines_layer.clear_layers()\n",
|
||||
" \n",
|
||||
" # Classification\n",
|
||||
" prediction, neighbors, votes = knn_classify(lat, lon, df, k=k)\n",
|
||||
" \n",
|
||||
" # Couleur selon prédiction\n",
|
||||
" color = 'red' if prediction == '2A' else 'blue'\n",
|
||||
" dept_name = 'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'\n",
|
||||
" \n",
|
||||
" # Marqueur du point test\n",
|
||||
" icon = AwesomeIcon(\n",
|
||||
" name='star',\n",
|
||||
" marker_color='darkred' if prediction == '2A' else 'darkblue',\n",
|
||||
" icon_color='white'\n",
|
||||
" )\n",
|
||||
" test_marker = Marker(location=(lat, lon), icon=icon, draggable=False)\n",
|
||||
" test_point_layer.add_layer(test_marker)\n",
|
||||
" \n",
|
||||
" # Afficher les k plus proches voisins\n",
|
||||
" for idx, neighbor in neighbors.iterrows():\n",
|
||||
" n_color = 'red' if neighbor['departement'] == '2A' else 'blue'\n",
|
||||
" \n",
|
||||
" # Marqueur du voisin\n",
|
||||
" n_marker = CircleMarker(\n",
|
||||
" location=(neighbor['latitude'], neighbor['longitude']),\n",
|
||||
" radius=8,\n",
|
||||
" color=n_color,\n",
|
||||
" fill_color=n_color,\n",
|
||||
" fill_opacity=0.7,\n",
|
||||
" weight=2\n",
|
||||
" )\n",
|
||||
" neighbors_layer.add_layer(n_marker)\n",
|
||||
" \n",
|
||||
" # Ligne vers le voisin\n",
|
||||
" line = Polyline(\n",
|
||||
" locations=[\n",
|
||||
" (lat, lon),\n",
|
||||
" (neighbor['latitude'], neighbor['longitude'])\n",
|
||||
" ],\n",
|
||||
" color=n_color,\n",
|
||||
" weight=2,\n",
|
||||
" opacity=0.5\n",
|
||||
" )\n",
|
||||
" lines_layer.add_layer(line)\n",
|
||||
" \n",
|
||||
" # Afficher les résultats\n",
|
||||
" with result_output:\n",
|
||||
" clear_output(wait=True)\n",
|
||||
" print(f\"📍 Coordonnées : ({lat:.4f}, {lon:.4f})\")\n",
|
||||
" print(f\"🔢 k = {k}\")\n",
|
||||
" print(f\"\\n🎯 Prédiction : {dept_name}\")\n",
|
||||
" print(f\"📊 Votes : 2A={votes.get('2A', 0)}, 2B={votes.get('2B', 0)}\")\n",
|
||||
" print(f\"\\n🏘️ Les {k} plus proches villages :\")\n",
|
||||
" print(neighbors[['village', 'nom_corse', 'departement', 'distance']].to_string(index=False))\n",
|
||||
" \n",
|
||||
" # Mettre à jour l'info\n",
|
||||
" info_html.value = f\"<div style='font-size:16px; padding:10px; background:{'#ffebee' if prediction=='2A' else '#e3f2fd'}; border-radius:5px; border-left: 4px solid {color};'><b>Classification : {dept_name}</b><br>Votes : 2A={votes.get('2A', 0)}, 2B={votes.get('2B', 0)}</div>\"\n",
|
||||
"\n",
|
||||
"def handle_click(**kwargs):\n",
|
||||
" \"\"\"Gestionnaire de clic sur la carte.\"\"\"\n",
|
||||
" if kwargs.get('type') == 'click':\n",
|
||||
" coords = kwargs.get('coordinates')\n",
|
||||
" lat, lon = coords\n",
|
||||
" current_coords['lat'] = lat\n",
|
||||
" current_coords['lon'] = lon\n",
|
||||
" update_classification(lat, lon, k_slider.value)\n",
|
||||
"\n",
|
||||
"def on_k_change(change):\n",
|
||||
" \"\"\"Gestionnaire de changement de k.\"\"\"\n",
|
||||
" if current_coords['lat'] is not None:\n",
|
||||
" update_classification(current_coords['lat'], current_coords['lon'], change['new'])\n",
|
||||
"\n",
|
||||
"# Connecter les événements\n",
|
||||
"m.on_interaction(handle_click)\n",
|
||||
"k_slider.observe(on_k_change, names='value')\n",
|
||||
"\n",
|
||||
"# Afficher l'interface\n",
|
||||
"display(VBox([\n",
|
||||
" info_html,\n",
|
||||
" HBox([Label('Nombre de voisins (k):'), k_slider]),\n",
|
||||
" m,\n",
|
||||
" result_output\n",
|
||||
"]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎯 Points d'intérêt à tester\n",
|
||||
"\n",
|
||||
"Essayez de cliquer sur ces zones :\n",
|
||||
"\n",
|
||||
"- **Ajaccio** : (41.9267, 8.7369) - Capitale 2A\n",
|
||||
"- **Bastia** : (42.7028, 9.4500) - Préfecture 2B\n",
|
||||
"- **Corte** : (42.3062, 9.1509) - Centre de la Corse\n",
|
||||
"- **Frontière approximative** : Zone entre 42.0 et 42.3 latitude\n",
|
||||
"\n",
|
||||
"### Questions à explorer :\n",
|
||||
"1. 🤔 Où se situe la \"frontière\" k-NN entre les deux départements ?\n",
|
||||
"2. 📊 Comment k influence-t-il la classification près de cette frontière ?\n",
|
||||
"3. 🏔️ Y a-t-il des zones ambiguës où le résultat change souvent ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 Mode manuel (si la carte ne fonctionne pas)\n",
|
||||
"\n",
|
||||
"Si le clic sur carte ne fonctionne pas, utilisez cette cellule :"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Mode manuel : entrez les coordonnées\n",
|
||||
"test_lat = 42.3 # Modifiez ici\n",
|
||||
"test_lon = 9.15 # Modifiez ici\n",
|
||||
"k = 5\n",
|
||||
"\n",
|
||||
"prediction, neighbors, votes = knn_classify(test_lat, test_lon, df, k=k)\n",
|
||||
"dept_name = 'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'\n",
|
||||
"\n",
|
||||
"print(f\"📍 Point : ({test_lat}, {test_lon})\")\n",
|
||||
"print(f\"🎯 Prédiction : {dept_name}\")\n",
|
||||
"print(f\"📊 Votes : {dict(votes)}\")\n",
|
||||
"print(f\"\\n🏘️ Les {k} plus proches villages :\")\n",
|
||||
"print(neighbors[['village', 'nom_corse', 'departement', 'distance']].to_string(index=False))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📝 Notes techniques\n",
|
||||
"\n",
|
||||
"Cette version utilise **ipyleaflet** qui offre une vraie interactivité bidirectionnelle entre Python et JavaScript dans Jupyter.\n",
|
||||
"\n",
|
||||
"**Avantages :**\n",
|
||||
"- ✅ Clic directement sur la carte\n",
|
||||
"- ✅ Mise à jour en temps réel\n",
|
||||
"- ✅ Curseur interactif pour k\n",
|
||||
"- ✅ Pas besoin de recharger\n",
|
||||
"\n",
|
||||
"**Prérequis :**\n",
|
||||
"- Jupyter Notebook ou JupyterLab\n",
|
||||
"- Extension widgets activée : `jupyter nbextension enable --py widgetsnbextension`"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
375
activite1/knn_corse_interactive_click.ipynb
Normal file
375
activite1/knn_corse_interactive_click.ipynb
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🗺️ k-NN Corse : Version Interactive avec Clic sur Carte\n",
|
||||
"\n",
|
||||
"## 🎮 Mode d'emploi\n",
|
||||
"1. Exécutez toutes les cellules\n",
|
||||
"2. **Cliquez sur la carte** pour choisir un point\n",
|
||||
"3. Ajustez k avec le curseur\n",
|
||||
"4. Observez la classification en temps réel !"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📦 Installation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Installation des bibliothèques\n",
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install ipyleaflet ipywidgets pandas numpy -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import math\n",
|
||||
"from collections import Counter\n",
|
||||
"from ipyleaflet import Map, Marker, CircleMarker, Polyline, AwesomeIcon, LayerGroup\n",
|
||||
"from ipywidgets import HTML, VBox, HBox, IntSlider, Output, Label\n",
|
||||
"from IPython.display import display, clear_output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📊 Chargement des données"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Charger les données\n",
|
||||
"df = pd.read_csv('villages_corse.csv', sep='\\t', encoding='utf-8')\n",
|
||||
"\n",
|
||||
"def parse_coordinates(point_geo_str):\n",
|
||||
" try:\n",
|
||||
" parts = str(point_geo_str).split(',')\n",
|
||||
" lat = float(parts[0].strip())\n",
|
||||
" lon = float(parts[1].strip())\n",
|
||||
" return lat, lon\n",
|
||||
" except:\n",
|
||||
" return None, None\n",
|
||||
"\n",
|
||||
"df[['latitude', 'longitude']] = df['Point_Geo'].apply(\n",
|
||||
" lambda x: pd.Series(parse_coordinates(x))\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"df = df.dropna(subset=['latitude', 'longitude'])\n",
|
||||
"df['dept_simple'] = df['Code Département'].apply(lambda x: '2A' if str(x) == '2A' else '2B')\n",
|
||||
"\n",
|
||||
"print(f\"✅ {len(df)} villages chargés\")\n",
|
||||
"print(f\" - Corse du Sud (2A) : {len(df[df['dept_simple']=='2A'])}\")\n",
|
||||
"print(f\" - Haute-Corse (2B) : {len(df[df['dept_simple']=='2B'])}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🧮 Fonctions k-NN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def haversine_distance(lat1, lon1, lat2, lon2):\n",
|
||||
" \"\"\"Calcule la distance en km entre deux points GPS.\"\"\"\n",
|
||||
" R = 6371\n",
|
||||
" lat1_rad = math.radians(lat1)\n",
|
||||
" lat2_rad = math.radians(lat2)\n",
|
||||
" delta_lat = math.radians(lat2 - lat1)\n",
|
||||
" delta_lon = math.radians(lon2 - lon1)\n",
|
||||
" \n",
|
||||
" a = math.sin(delta_lat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2\n",
|
||||
" c = 2 * math.asin(math.sqrt(a))\n",
|
||||
" \n",
|
||||
" return R * c\n",
|
||||
"\n",
|
||||
"def knn_classify(test_lat, test_lon, df, k=5):\n",
|
||||
" \"\"\"Classifie un point avec k-NN.\"\"\"\n",
|
||||
" distances = []\n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" dist = haversine_distance(test_lat, test_lon, row['latitude'], row['longitude'])\n",
|
||||
" distances.append({\n",
|
||||
" 'village': row['Nom français'],\n",
|
||||
" 'nom_corse': row['Nom corse'],\n",
|
||||
" 'departement': row['dept_simple'],\n",
|
||||
" 'latitude': row['latitude'],\n",
|
||||
" 'longitude': row['longitude'],\n",
|
||||
" 'distance': dist\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" dist_df = pd.DataFrame(distances).sort_values('distance')\n",
|
||||
" neighbors = dist_df.head(k)\n",
|
||||
" votes = Counter(neighbors['departement'])\n",
|
||||
" prediction = votes.most_common(1)[0][0]\n",
|
||||
" \n",
|
||||
" return prediction, neighbors, votes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🗺️ Carte Interactive\n",
|
||||
"\n",
|
||||
"**Instructions :**\n",
|
||||
"- 🖱️ **Cliquez sur la carte** pour placer un point\n",
|
||||
"- 🎚️ **Ajustez k** avec le curseur\n",
|
||||
"- 👁️ La classification se met à jour automatiquement"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Créer la carte\n",
|
||||
"m = Map(center=(42.15, 9.05), zoom=9, scroll_wheel_zoom=True)\n",
|
||||
"\n",
|
||||
"# Couches pour les éléments dynamiques\n",
|
||||
"test_point_layer = LayerGroup()\n",
|
||||
"neighbors_layer = LayerGroup()\n",
|
||||
"lines_layer = LayerGroup()\n",
|
||||
"\n",
|
||||
"m.add_layer(test_point_layer)\n",
|
||||
"m.add_layer(neighbors_layer)\n",
|
||||
"m.add_layer(lines_layer)\n",
|
||||
"\n",
|
||||
"# Afficher quelques villages de référence\n",
|
||||
"sample_villages = df.sample(n=min(50, len(df)), random_state=42)\n",
|
||||
"for idx, row in sample_villages.iterrows():\n",
|
||||
" color = 'red' if row['dept_simple'] == '2A' else 'blue'\n",
|
||||
" circle = CircleMarker(\n",
|
||||
" location=(row['latitude'], row['longitude']),\n",
|
||||
" radius=3,\n",
|
||||
" color=color,\n",
|
||||
" fill_color=color,\n",
|
||||
" fill_opacity=0.4,\n",
|
||||
" weight=1\n",
|
||||
" )\n",
|
||||
" m.add_layer(circle)\n",
|
||||
"\n",
|
||||
"# Widget pour k\n",
|
||||
"k_slider = IntSlider(\n",
|
||||
" value=5,\n",
|
||||
" min=1,\n",
|
||||
" max=20,\n",
|
||||
" step=1,\n",
|
||||
" description='k:',\n",
|
||||
" continuous_update=False\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Zone de résultats\n",
|
||||
"result_output = Output()\n",
|
||||
"info_html = HTML(value=\"<p style='font-size:16px; padding:10px; background:#f0f0f0; border-radius:5px;'>👆 <b>Cliquez sur la carte pour classifier un point</b></p>\")\n",
|
||||
"\n",
|
||||
"# Variable globale pour stocker les coordonnées\n",
|
||||
"current_coords = {'lat': None, 'lon': None}\n",
|
||||
"\n",
|
||||
"def update_classification(lat, lon, k):\n",
|
||||
" \"\"\"Met à jour la classification et la visualisation.\"\"\"\n",
|
||||
" # Effacer les couches précédentes\n",
|
||||
" test_point_layer.clear_layers()\n",
|
||||
" neighbors_layer.clear_layers()\n",
|
||||
" lines_layer.clear_layers()\n",
|
||||
" \n",
|
||||
" # Classification\n",
|
||||
" prediction, neighbors, votes = knn_classify(lat, lon, df, k=k)\n",
|
||||
" \n",
|
||||
" # Couleur selon prédiction\n",
|
||||
" color = 'red' if prediction == '2A' else 'blue'\n",
|
||||
" dept_name = 'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'\n",
|
||||
" \n",
|
||||
" # Marqueur du point test\n",
|
||||
" icon = AwesomeIcon(\n",
|
||||
" name='star',\n",
|
||||
" marker_color='darkred' if prediction == '2A' else 'darkblue',\n",
|
||||
" icon_color='white'\n",
|
||||
" )\n",
|
||||
" test_marker = Marker(location=(lat, lon), icon=icon, draggable=False)\n",
|
||||
" test_point_layer.add_layer(test_marker)\n",
|
||||
" \n",
|
||||
" # Afficher les k plus proches voisins\n",
|
||||
" for idx, neighbor in neighbors.iterrows():\n",
|
||||
" n_color = 'red' if neighbor['departement'] == '2A' else 'blue'\n",
|
||||
" \n",
|
||||
" # Marqueur du voisin\n",
|
||||
" n_marker = CircleMarker(\n",
|
||||
" location=(neighbor['latitude'], neighbor['longitude']),\n",
|
||||
" radius=8,\n",
|
||||
" color=n_color,\n",
|
||||
" fill_color=n_color,\n",
|
||||
" fill_opacity=0.7,\n",
|
||||
" weight=2\n",
|
||||
" )\n",
|
||||
" neighbors_layer.add_layer(n_marker)\n",
|
||||
" \n",
|
||||
" # Ligne vers le voisin\n",
|
||||
" line = Polyline(\n",
|
||||
" locations=[\n",
|
||||
" (lat, lon),\n",
|
||||
" (neighbor['latitude'], neighbor['longitude'])\n",
|
||||
" ],\n",
|
||||
" color=n_color,\n",
|
||||
" weight=2,\n",
|
||||
" opacity=0.5\n",
|
||||
" )\n",
|
||||
" lines_layer.add_layer(line)\n",
|
||||
" \n",
|
||||
" # Afficher les résultats\n",
|
||||
" with result_output:\n",
|
||||
" clear_output(wait=True)\n",
|
||||
" print(f\"📍 Coordonnées : ({lat:.4f}, {lon:.4f})\")\n",
|
||||
" print(f\"🔢 k = {k}\")\n",
|
||||
" print(f\"\\n🎯 Prédiction : {dept_name}\")\n",
|
||||
" print(f\"📊 Votes : 2A={votes.get('2A', 0)}, 2B={votes.get('2B', 0)}\")\n",
|
||||
" print(f\"\\n🏘️ Les {k} plus proches villages :\")\n",
|
||||
" print(neighbors[['village', 'nom_corse', 'departement', 'distance']].to_string(index=False))\n",
|
||||
" \n",
|
||||
" # Mettre à jour l'info\n",
|
||||
" info_html.value = f\"<div style='font-size:16px; padding:10px; background:{'#ffebee' if prediction=='2A' else '#e3f2fd'}; border-radius:5px; border-left: 4px solid {color};'><b>Classification : {dept_name}</b><br>Votes : 2A={votes.get('2A', 0)}, 2B={votes.get('2B', 0)}</div>\"\n",
|
||||
"\n",
|
||||
"def handle_click(**kwargs):\n",
|
||||
" \"\"\"Gestionnaire de clic sur la carte.\"\"\"\n",
|
||||
" if kwargs.get('type') == 'click':\n",
|
||||
" coords = kwargs.get('coordinates')\n",
|
||||
" lat, lon = coords\n",
|
||||
" current_coords['lat'] = lat\n",
|
||||
" current_coords['lon'] = lon\n",
|
||||
" update_classification(lat, lon, k_slider.value)\n",
|
||||
"\n",
|
||||
"def on_k_change(change):\n",
|
||||
" \"\"\"Gestionnaire de changement de k.\"\"\"\n",
|
||||
" if current_coords['lat'] is not None:\n",
|
||||
" update_classification(current_coords['lat'], current_coords['lon'], change['new'])\n",
|
||||
"\n",
|
||||
"# Connecter les événements\n",
|
||||
"m.on_interaction(handle_click)\n",
|
||||
"k_slider.observe(on_k_change, names='value')\n",
|
||||
"\n",
|
||||
"# Afficher l'interface\n",
|
||||
"display(VBox([\n",
|
||||
" info_html,\n",
|
||||
" HBox([Label('Nombre de voisins (k):'), k_slider]),\n",
|
||||
" m,\n",
|
||||
" result_output\n",
|
||||
"]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎯 Points d'intérêt à tester\n",
|
||||
"\n",
|
||||
"Essayez de cliquer sur ces zones :\n",
|
||||
"\n",
|
||||
"- **Ajaccio** : (41.9267, 8.7369) - Capitale 2A\n",
|
||||
"- **Bastia** : (42.7028, 9.4500) - Préfecture 2B\n",
|
||||
"- **Corte** : (42.3062, 9.1509) - Centre de la Corse\n",
|
||||
"- **Frontière approximative** : Zone entre 42.0 et 42.3 latitude\n",
|
||||
"\n",
|
||||
"### Questions à explorer :\n",
|
||||
"1. 🤔 Où se situe la \"frontière\" k-NN entre les deux départements ?\n",
|
||||
"2. 📊 Comment k influence-t-il la classification près de cette frontière ?\n",
|
||||
"3. 🏔️ Y a-t-il des zones ambiguës où le résultat change souvent ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 Mode manuel (si la carte ne fonctionne pas)\n",
|
||||
"\n",
|
||||
"Si le clic sur carte ne fonctionne pas, utilisez cette cellule :"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Mode manuel : entrez les coordonnées\n",
|
||||
"test_lat = 42.3 # Modifiez ici\n",
|
||||
"test_lon = 9.15 # Modifiez ici\n",
|
||||
"k = 5\n",
|
||||
"\n",
|
||||
"prediction, neighbors, votes = knn_classify(test_lat, test_lon, df, k=k)\n",
|
||||
"dept_name = 'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'\n",
|
||||
"\n",
|
||||
"print(f\"📍 Point : ({test_lat}, {test_lon})\")\n",
|
||||
"print(f\"🎯 Prédiction : {dept_name}\")\n",
|
||||
"print(f\"📊 Votes : {dict(votes)}\")\n",
|
||||
"print(f\"\\n🏘️ Les {k} plus proches villages :\")\n",
|
||||
"print(neighbors[['village', 'nom_corse', 'departement', 'distance']].to_string(index=False))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📝 Notes techniques\n",
|
||||
"\n",
|
||||
"Cette version utilise **ipyleaflet** qui offre une vraie interactivité bidirectionnelle entre Python et JavaScript dans Jupyter.\n",
|
||||
"\n",
|
||||
"**Avantages :**\n",
|
||||
"- ✅ Clic directement sur la carte\n",
|
||||
"- ✅ Mise à jour en temps réel\n",
|
||||
"- ✅ Curseur interactif pour k\n",
|
||||
"- ✅ Pas besoin de recharger\n",
|
||||
"\n",
|
||||
"**Prérequis :**\n",
|
||||
"- Jupyter Notebook ou JupyterLab\n",
|
||||
"- Extension widgets activée : `jupyter nbextension enable --py widgetsnbextension`"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
744
activite1/knn_corse_interactive_v2.ipynb
Normal file
744
activite1/knn_corse_interactive_v2.ipynb
Normal file
|
|
@ -0,0 +1,744 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🗺️ Classification k-NN : Haute-Corse ou Corse du Sud ?\n",
|
||||
"\n",
|
||||
"## Objectif\n",
|
||||
"Utiliser l'algorithme des **k plus proches voisins (k-NN)** pour déterminer si un point de la carte de Corse se situe en **Haute-Corse (2B)** ou en **Corse du Sud (2A)**, en se basant sur les villages les plus proches.\n",
|
||||
"\n",
|
||||
"## Principe\n",
|
||||
"1. On charge les données des villages corses avec leurs coordonnées GPS et leur département\n",
|
||||
"2. On choisit un point sur la carte\n",
|
||||
"3. On calcule les distances entre ce point et tous les villages\n",
|
||||
"4. On identifie les k villages les plus proches\n",
|
||||
"5. On vote : le département majoritaire parmi ces k villages devient la prédiction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📦 Installation et imports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Installation des bibliothèques nécessaires (si besoin)\n",
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install folium pandas numpy -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import folium\n",
|
||||
"from folium.plugins import MarkerCluster\n",
|
||||
"import math\n",
|
||||
"import json\n",
|
||||
"from collections import Counter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📊 Chargement des données"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Charger le fichier CSV\n",
|
||||
"# Remplacez 'villages_corse.csv' par le chemin de votre fichier\n",
|
||||
"df = pd.read_csv('villages_corse.csv', sep='\\t', encoding='utf-8')\n",
|
||||
"\n",
|
||||
"# Afficher les premières lignes\n",
|
||||
"print(f\"Nombre de villages : {len(df)}\")\n",
|
||||
"print(f\"\\nColonnes : {list(df.columns)}\")\n",
|
||||
"df.head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🔧 Préparation des données"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def parse_coordinates(point_geo_str):\n",
|
||||
" \"\"\"\n",
|
||||
" Parse la colonne Point_Geo pour extraire latitude et longitude.\n",
|
||||
" Format attendu : \"latitude, longitude\"\n",
|
||||
" Exemple : \"41.984099158, 8.798384636\"\n",
|
||||
" \"\"\"\n",
|
||||
" try:\n",
|
||||
" # Séparer par la virgule\n",
|
||||
" parts = str(point_geo_str).split(',')\n",
|
||||
" lat = float(parts[0].strip())\n",
|
||||
" lon = float(parts[1].strip())\n",
|
||||
" return lat, lon\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Erreur parsing: {point_geo_str} - {e}\")\n",
|
||||
" return None, None\n",
|
||||
"\n",
|
||||
"# Extraire les coordonnées\n",
|
||||
"df[['latitude', 'longitude']] = df['Point_Geo'].apply(\n",
|
||||
" lambda x: pd.Series(parse_coordinates(x))\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Supprimer les lignes sans coordonnées valides\n",
|
||||
"df = df.dropna(subset=['latitude', 'longitude'])\n",
|
||||
"\n",
|
||||
"# Simplifier les noms de départements\n",
|
||||
"df['dept_simple'] = df['Code Département'].apply(lambda x: '2A' if str(x) == '2A' else '2B')\n",
|
||||
"\n",
|
||||
"print(f\"Villages avec coordonnées valides : {len(df)}\")\n",
|
||||
"print(f\"\\nRépartition par département :\")\n",
|
||||
"print(df['dept_simple'].value_counts())\n",
|
||||
"\n",
|
||||
"df[['Nom français', 'dept_simple', 'latitude', 'longitude']].head(10)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📏 Fonction de calcul de distance\n",
|
||||
"\n",
|
||||
"Nous utilisons la **formule de Haversine** pour calculer la distance entre deux points GPS sur la surface de la Terre."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def haversine_distance(lat1, lon1, lat2, lon2):\n",
|
||||
" \"\"\"\n",
|
||||
" Calcule la distance en kilomètres entre deux points GPS.\n",
|
||||
" Formule de Haversine.\n",
|
||||
" \"\"\"\n",
|
||||
" R = 6371 # Rayon de la Terre en km\n",
|
||||
" \n",
|
||||
" # Conversion en radians\n",
|
||||
" lat1_rad = math.radians(lat1)\n",
|
||||
" lat2_rad = math.radians(lat2)\n",
|
||||
" delta_lat = math.radians(lat2 - lat1)\n",
|
||||
" delta_lon = math.radians(lon2 - lon1)\n",
|
||||
" \n",
|
||||
" # Formule de Haversine\n",
|
||||
" a = math.sin(delta_lat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2\n",
|
||||
" c = 2 * math.asin(math.sqrt(a))\n",
|
||||
" \n",
|
||||
" return R * c\n",
|
||||
"\n",
|
||||
"# Test de la fonction\n",
|
||||
"# Distance entre Ajaccio (41.9267, 8.7369) et Bastia (42.7028, 9.4500)\n",
|
||||
"dist_test = haversine_distance(41.9267, 8.7369, 42.7028, 9.4500)\n",
|
||||
"print(f\"Distance Ajaccio-Bastia : {dist_test:.1f} km\")\n",
|
||||
"\n",
|
||||
"# Test avec Afa et Alando (vos exemples)\n",
|
||||
"afa = df[df['Nom français'] == 'Afa'].iloc[0]\n",
|
||||
"alando = df[df['Nom français'] == 'Alando'].iloc[0]\n",
|
||||
"dist_afa_alando = haversine_distance(afa['latitude'], afa['longitude'], \n",
|
||||
" alando['latitude'], alando['longitude'])\n",
|
||||
"print(f\"Distance Afa-Alando : {dist_afa_alando:.1f} km\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎯 Algorithme k-NN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def knn_classify(test_lat, test_lon, df, k=5):\n",
|
||||
" \"\"\"\n",
|
||||
" Classifie un point (test_lat, test_lon) en utilisant k-NN.\n",
|
||||
" \n",
|
||||
" Retourne :\n",
|
||||
" - prediction : le département prédit ('2A' ou '2B')\n",
|
||||
" - neighbors : DataFrame des k plus proches voisins\n",
|
||||
" - votes : dictionnaire des votes\n",
|
||||
" \"\"\"\n",
|
||||
" # Calculer les distances pour tous les villages\n",
|
||||
" distances = []\n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" dist = haversine_distance(test_lat, test_lon, row['latitude'], row['longitude'])\n",
|
||||
" distances.append({\n",
|
||||
" 'village': row['Nom français'],\n",
|
||||
" 'nom_corse': row['Nom corse'],\n",
|
||||
" 'departement': row['dept_simple'],\n",
|
||||
" 'latitude': row['latitude'],\n",
|
||||
" 'longitude': row['longitude'],\n",
|
||||
" 'distance': dist\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" # Créer un DataFrame et trier par distance\n",
|
||||
" dist_df = pd.DataFrame(distances)\n",
|
||||
" dist_df = dist_df.sort_values('distance')\n",
|
||||
" \n",
|
||||
" # Sélectionner les k plus proches\n",
|
||||
" neighbors = dist_df.head(k)\n",
|
||||
" \n",
|
||||
" # Voter\n",
|
||||
" votes = Counter(neighbors['departement'])\n",
|
||||
" prediction = votes.most_common(1)[0][0]\n",
|
||||
" \n",
|
||||
" return prediction, neighbors, votes\n",
|
||||
"\n",
|
||||
"# Test de l'algorithme avec un point au centre de la Corse\n",
|
||||
"test_lat, test_lon = 42.15, 9.05\n",
|
||||
"k = 5\n",
|
||||
"\n",
|
||||
"prediction, neighbors, votes = knn_classify(test_lat, test_lon, df, k=k)\n",
|
||||
"\n",
|
||||
"print(f\"\\n🎯 Point de test : ({test_lat}, {test_lon})\")\n",
|
||||
"print(f\"\\nAvec k={k} :\")\n",
|
||||
"print(f\"Prédiction : {'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'}\")\n",
|
||||
"print(f\"Votes : {dict(votes)}\")\n",
|
||||
"print(f\"\\nLes {k} plus proches voisins :\")\n",
|
||||
"print(neighbors[['village', 'nom_corse', 'departement', 'distance']])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🗺️ Visualisation avec Folium"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def create_map(test_lat=None, test_lon=None, k=5, show_all_villages=False, show_boundaries=False):\n",
|
||||
" \"\"\"\n",
|
||||
" Crée une carte interactive avec Folium.\n",
|
||||
" \n",
|
||||
" Paramètres:\n",
|
||||
" - test_lat, test_lon: coordonnées du point à tester\n",
|
||||
" - k: nombre de voisins\n",
|
||||
" - show_all_villages: afficher tous les villages\n",
|
||||
" - show_boundaries: afficher les frontières des communes (peut être lent)\n",
|
||||
" \"\"\"\n",
|
||||
" # Centre de la Corse\n",
|
||||
" center_lat = 42.15\n",
|
||||
" center_lon = 9.05\n",
|
||||
" \n",
|
||||
" # Créer la carte\n",
|
||||
" m = folium.Map(\n",
|
||||
" location=[center_lat, center_lon],\n",
|
||||
" zoom_start=9,\n",
|
||||
" tiles='OpenStreetMap'\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Afficher les frontières des communes (optionnel)\n",
|
||||
" if show_boundaries:\n",
|
||||
" print(\"Affichage des frontières des communes...\")\n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" try:\n",
|
||||
" zone_geo = json.loads(row['Zone_geo'])\n",
|
||||
" color = 'red' if row['dept_simple'] == '2A' else 'blue'\n",
|
||||
" \n",
|
||||
" folium.GeoJson(\n",
|
||||
" zone_geo,\n",
|
||||
" style_function=lambda x, color=color: {\n",
|
||||
" 'fillColor': color,\n",
|
||||
" 'color': color,\n",
|
||||
" 'weight': 1,\n",
|
||||
" 'fillOpacity': 0.1\n",
|
||||
" },\n",
|
||||
" tooltip=row['Nom français']\n",
|
||||
" ).add_to(m)\n",
|
||||
" except:\n",
|
||||
" pass\n",
|
||||
" \n",
|
||||
" # Afficher tous les villages (optionnel)\n",
|
||||
" if show_all_villages:\n",
|
||||
" marker_cluster = MarkerCluster().add_to(m)\n",
|
||||
" \n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" color = 'red' if row['dept_simple'] == '2A' else 'blue'\n",
|
||||
" folium.CircleMarker(\n",
|
||||
" location=[row['latitude'], row['longitude']],\n",
|
||||
" radius=3,\n",
|
||||
" color=color,\n",
|
||||
" fill=True,\n",
|
||||
" fillColor=color,\n",
|
||||
" fillOpacity=0.4,\n",
|
||||
" popup=f\"<b>{row['Nom français']}</b><br>{row['Nom corse']}<br>({row['dept_simple']})\"\n",
|
||||
" ).add_to(marker_cluster)\n",
|
||||
" \n",
|
||||
" # Si un point de test est fourni\n",
|
||||
" if test_lat is not None and test_lon is not None:\n",
|
||||
" # Classification\n",
|
||||
" prediction, neighbors, votes = knn_classify(test_lat, test_lon, df, k=k)\n",
|
||||
" \n",
|
||||
" # Marqueur pour le point de test\n",
|
||||
" color = 'darkred' if prediction == '2A' else 'darkblue'\n",
|
||||
" dept_name = 'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'\n",
|
||||
" \n",
|
||||
" folium.Marker(\n",
|
||||
" location=[test_lat, test_lon],\n",
|
||||
" popup=f\"<b>Point à classifier</b><br>Prédiction : {dept_name}<br>Votes : {dict(votes)}\",\n",
|
||||
" icon=folium.Icon(color=color, icon='star', prefix='fa')\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" # Afficher les k plus proches voisins\n",
|
||||
" for idx, neighbor in neighbors.iterrows():\n",
|
||||
" # Marqueur pour chaque voisin\n",
|
||||
" color = 'red' if neighbor['departement'] == '2A' else 'blue'\n",
|
||||
" folium.Marker(\n",
|
||||
" location=[neighbor['latitude'], neighbor['longitude']],\n",
|
||||
" popup=f\"<b>{neighbor['village']}</b><br>{neighbor['nom_corse']}<br>{neighbor['departement']}<br>Distance: {neighbor['distance']:.2f} km\",\n",
|
||||
" icon=folium.Icon(color=color, icon='info-sign')\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" # Ligne entre le point test et le voisin\n",
|
||||
" folium.PolyLine(\n",
|
||||
" locations=[\n",
|
||||
" [test_lat, test_lon],\n",
|
||||
" [neighbor['latitude'], neighbor['longitude']]\n",
|
||||
" ],\n",
|
||||
" color=color,\n",
|
||||
" weight=2,\n",
|
||||
" opacity=0.5,\n",
|
||||
" tooltip=f\"{neighbor['distance']:.2f} km\"\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" # Légende\n",
|
||||
" legend_html = '''\n",
|
||||
" <div style=\"position: fixed; \n",
|
||||
" bottom: 50px; right: 50px; width: 220px; height: 130px; \n",
|
||||
" background-color: white; border:2px solid grey; z-index:9999; \n",
|
||||
" font-size:14px; padding: 10px\">\n",
|
||||
" <p><strong>Légende</strong></p>\n",
|
||||
" <p><i class=\"fa fa-circle\" style=\"color:red\"></i> Corse du Sud (2A)</p>\n",
|
||||
" <p><i class=\"fa fa-circle\" style=\"color:blue\"></i> Haute-Corse (2B)</p>\n",
|
||||
" <p><i class=\"fa fa-star\" style=\"color:darkred\"></i> Point à classifier</p>\n",
|
||||
" </div>\n",
|
||||
" '''\n",
|
||||
" m.get_root().html.add_child(folium.Element(legend_html))\n",
|
||||
" \n",
|
||||
" return m\n",
|
||||
"\n",
|
||||
"# Créer la carte avec le point de test\n",
|
||||
"map_with_test = create_map(test_lat=42.15, test_lon=9.05, k=5, show_all_villages=False)\n",
|
||||
"map_with_test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🔬 Expérimentation : Influence de k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test avec différentes valeurs de k\n",
|
||||
"test_point = (42.15, 9.05) # Point au centre de la Corse\n",
|
||||
"\n",
|
||||
"print(f\"Point testé : {test_point}\\n\")\n",
|
||||
"print(f\"{'k':<5} {'Prédiction':<15} {'Votes 2A':<10} {'Votes 2B':<10}\")\n",
|
||||
"print(\"-\" * 50)\n",
|
||||
"\n",
|
||||
"for k in [1, 3, 5, 7, 9, 15, 21]:\n",
|
||||
" prediction, neighbors, votes = knn_classify(test_point[0], test_point[1], df, k=k)\n",
|
||||
" votes_2a = votes.get('2A', 0)\n",
|
||||
" votes_2b = votes.get('2B', 0)\n",
|
||||
" dept_name = 'Corse du Sud' if prediction == '2A' else 'Haute-Corse'\n",
|
||||
" print(f\"{k:<5} {dept_name:<15} {votes_2a:<10} {votes_2b:<10}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎮 Mode interactif : Testez vos propres points !\n",
|
||||
"\n",
|
||||
"Modifiez les coordonnées ci-dessous pour tester différents points de la Corse.\n",
|
||||
"\n",
|
||||
"**Quelques repères géographiques :**\n",
|
||||
"- Ajaccio : (41.9267, 8.7369)\n",
|
||||
"- Bastia : (42.7028, 9.4500)\n",
|
||||
"- Corte : (42.3062, 9.1509)\n",
|
||||
"- Porto-Vecchio : (41.5914, 9.2795)\n",
|
||||
"- Calvi : (42.5679, 8.7575)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# === MODIFIEZ CES VALEURS ===\n",
|
||||
"test_latitude = 42.3 # Entre 41.3 (sud) et 43.0 (nord)\n",
|
||||
"test_longitude = 9.15 # Entre 8.5 (ouest) et 9.5 (est)\n",
|
||||
"k_value = 7 # Nombre de voisins\n",
|
||||
"# =============================\n",
|
||||
"\n",
|
||||
"prediction, neighbors, votes = knn_classify(test_latitude, test_longitude, df, k=k_value)\n",
|
||||
"\n",
|
||||
"print(f\"📍 Point : ({test_latitude}, {test_longitude})\")\n",
|
||||
"print(f\"🔢 k = {k_value}\")\n",
|
||||
"print(f\"\\n🎯 Prédiction : {'Corse du Sud (2A)' if prediction == '2A' else 'Haute-Corse (2B)'}\")\n",
|
||||
"print(f\"\\n📊 Votes : {dict(votes)}\")\n",
|
||||
"print(f\"\\n🏘️ Les {k_value} plus proches villages :\")\n",
|
||||
"print(neighbors[['village', 'nom_corse', 'departement', 'distance']].to_string(index=False))\n",
|
||||
"\n",
|
||||
"# Afficher la carte\n",
|
||||
"map_interactive = create_map(test_latitude, test_longitude, k=k_value, show_all_villages=False)\n",
|
||||
"map_interactive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🌍 Carte complète avec tous les villages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Afficher tous les villages de Corse\n",
|
||||
"# Note : cette cellule peut prendre quelques secondes à s'exécuter\n",
|
||||
"\n",
|
||||
"map_all = create_map(show_all_villages=True, show_boundaries=False)\n",
|
||||
"map_all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🗺️ Carte avec frontières des communes (BONUS)\n",
|
||||
"\n",
|
||||
"Cette cellule affiche les frontières réelles des communes. **Attention : cela peut prendre du temps à charger !**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Carte avec frontières - peut être lent !\n",
|
||||
"# Décommentez la ligne suivante pour l'exécuter\n",
|
||||
"# map_boundaries = create_map(test_lat=42.15, test_lon=9.05, k=5, show_boundaries=True)\n",
|
||||
"# map_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 📈 Visualisation de la frontière de décision"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Créer une grille de points et classifier chacun\n",
|
||||
"# Cela permet de visualiser la \"frontière\" selon k-NN\n",
|
||||
"\n",
|
||||
"def create_decision_boundary_map(k=5, grid_resolution=40):\n",
|
||||
" \"\"\"\n",
|
||||
" Crée une carte montrant la frontière de décision de k-NN.\n",
|
||||
" \"\"\"\n",
|
||||
" # Limites de la Corse\n",
|
||||
" lat_min, lat_max = 41.3, 43.0\n",
|
||||
" lon_min, lon_max = 8.5, 9.6\n",
|
||||
" \n",
|
||||
" # Créer une grille\n",
|
||||
" lats = np.linspace(lat_min, lat_max, grid_resolution)\n",
|
||||
" lons = np.linspace(lon_min, lon_max, grid_resolution)\n",
|
||||
" \n",
|
||||
" m = folium.Map(\n",
|
||||
" location=[42.15, 9.05],\n",
|
||||
" zoom_start=8,\n",
|
||||
" tiles='OpenStreetMap'\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Classifier chaque point de la grille\n",
|
||||
" print(f\"Classification d'une grille de {grid_resolution}x{grid_resolution} points...\")\n",
|
||||
" total = len(lats) * len(lons)\n",
|
||||
" count = 0\n",
|
||||
" \n",
|
||||
" for lat in lats:\n",
|
||||
" for lon in lons:\n",
|
||||
" prediction, _, _ = knn_classify(lat, lon, df, k=k)\n",
|
||||
" color = '#ffcccc' if prediction == '2A' else '#ccccff'\n",
|
||||
" \n",
|
||||
" folium.CircleMarker(\n",
|
||||
" location=[lat, lon],\n",
|
||||
" radius=4,\n",
|
||||
" color=color,\n",
|
||||
" fill=True,\n",
|
||||
" fillColor=color,\n",
|
||||
" fillOpacity=0.3,\n",
|
||||
" weight=0\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" count += 1\n",
|
||||
" if count % 100 == 0:\n",
|
||||
" print(f\" {count}/{total} points traités ({100*count/total:.1f}%)\")\n",
|
||||
" \n",
|
||||
" print(\"Terminé !\")\n",
|
||||
" \n",
|
||||
" # Ajouter les villages\n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" color = 'red' if row['dept_simple'] == '2A' else 'blue'\n",
|
||||
" folium.CircleMarker(\n",
|
||||
" location=[row['latitude'], row['longitude']],\n",
|
||||
" radius=2,\n",
|
||||
" color=color,\n",
|
||||
" fill=True,\n",
|
||||
" fillColor=color,\n",
|
||||
" fillOpacity=0.8,\n",
|
||||
" popup=row['Nom français']\n",
|
||||
" ).add_to(m)\n",
|
||||
" \n",
|
||||
" return m\n",
|
||||
"\n",
|
||||
"# Créer la carte (réduire grid_resolution si c'est trop lent)\n",
|
||||
"print(f\"Création de la carte de frontière avec k=5...\")\n",
|
||||
"print(\"Note : cela peut prendre 1-2 minutes...\")\n",
|
||||
"boundary_map = create_decision_boundary_map(k=5, grid_resolution=30)\n",
|
||||
"boundary_map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 Validation de l'algorithme"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# BONUS : Validation croisée\n",
|
||||
"# Tester la précision en utilisant les villages eux-mêmes\n",
|
||||
"\n",
|
||||
"def cross_validation(df, k=5, sample_size=100):\n",
|
||||
" \"\"\"\n",
|
||||
" Teste la précision de k-NN en utilisant un échantillon de villages.\n",
|
||||
" \"\"\"\n",
|
||||
" # Prendre un échantillon aléatoire\n",
|
||||
" sample = df.sample(n=min(sample_size, len(df)), random_state=42)\n",
|
||||
" \n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
" errors = []\n",
|
||||
" \n",
|
||||
" for idx, row in sample.iterrows():\n",
|
||||
" # Créer un dataset sans ce village\n",
|
||||
" df_without = df.drop(idx)\n",
|
||||
" \n",
|
||||
" # Classifier ce village\n",
|
||||
" prediction, neighbors, votes = knn_classify(\n",
|
||||
" row['latitude'], \n",
|
||||
" row['longitude'], \n",
|
||||
" df_without, \n",
|
||||
" k=k\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" if prediction == row['dept_simple']:\n",
|
||||
" correct += 1\n",
|
||||
" else:\n",
|
||||
" errors.append({\n",
|
||||
" 'village': row['Nom français'],\n",
|
||||
" 'vrai_dept': row['dept_simple'],\n",
|
||||
" 'prediction': prediction,\n",
|
||||
" 'votes': dict(votes)\n",
|
||||
" })\n",
|
||||
" total += 1\n",
|
||||
" \n",
|
||||
" accuracy = (correct / total) * 100\n",
|
||||
" return accuracy, correct, total, errors\n",
|
||||
"\n",
|
||||
"print(\"Test de précision de l'algorithme k-NN...\\n\")\n",
|
||||
"print(\"Validation croisée : chaque village est classifié en fonction de ses voisins.\\n\")\n",
|
||||
"\n",
|
||||
"for k in [1, 3, 5, 10, 15]:\n",
|
||||
" accuracy, correct, total, errors = cross_validation(df, k=k, sample_size=100)\n",
|
||||
" print(f\"k={k:2d} : {accuracy:.1f}% de précision ({correct}/{total} corrects)\")\n",
|
||||
"\n",
|
||||
"# Afficher quelques erreurs pour k=5\n",
|
||||
"print(\"\\n📋 Exemples d'erreurs avec k=5 :\")\n",
|
||||
"_, _, _, errors_k5 = cross_validation(df, k=5, sample_size=100)\n",
|
||||
"if errors_k5:\n",
|
||||
" for error in errors_k5[:5]:\n",
|
||||
" print(f\" • {error['village']} : prédit {error['prediction']} (vrai: {error['vrai_dept']}) - votes: {error['votes']}\")\n",
|
||||
"else:\n",
|
||||
" print(\" Aucune erreur !\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🎓 Questions de réflexion\n",
|
||||
"\n",
|
||||
"1. **Influence de k** : Comment la prédiction change-t-elle avec différentes valeurs de k ?\n",
|
||||
"\n",
|
||||
"2. **Points frontières** : Trouvez des coordonnées où la classification est ambiguë (votes proches).\n",
|
||||
"\n",
|
||||
"3. **Zones problématiques** : Où se situent les villages difficiles à classifier correctement ?\n",
|
||||
"\n",
|
||||
"4. **Validité géographique** : Cette méthode respecte-t-elle toujours les vraies frontières administratives ?\n",
|
||||
"\n",
|
||||
"5. **Améliorations** : Comment pourrait-on améliorer l'algorithme ?\n",
|
||||
" - Pondération par distance inverse\n",
|
||||
" - Prise en compte d'autres critères (altitude, population...)\n",
|
||||
" - k adaptatif selon la densité de villages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 Exercices supplémentaires\n",
|
||||
"\n",
|
||||
"1. **Trouver la frontière** : Trouvez des points sur la \"frontière\" k-NN (là où un changement de k change la classification)\n",
|
||||
"\n",
|
||||
"2. **Villages isolés** : Identifiez les villages dont le département diffère de leurs k plus proches voisins\n",
|
||||
"\n",
|
||||
"3. **Pondération** : Implémentez une version pondérée où les villages plus proches ont plus d'influence\n",
|
||||
"\n",
|
||||
"4. **Comparaison** : Comparez la frontière k-NN avec la vraie frontière administrative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# EXERCICE : Villages \"anomaliques\"\n",
|
||||
"# Trouver les villages dont les k plus proches voisins sont majoritairement de l'autre département\n",
|
||||
"\n",
|
||||
"def find_anomalous_villages(df, k=5):\n",
|
||||
" \"\"\"\n",
|
||||
" Trouve les villages qui seraient mal classifiés par k-NN.\n",
|
||||
" \"\"\"\n",
|
||||
" anomalies = []\n",
|
||||
" \n",
|
||||
" for idx, row in df.iterrows():\n",
|
||||
" # Créer un dataset sans ce village\n",
|
||||
" df_without = df.drop(idx)\n",
|
||||
" \n",
|
||||
" # Classifier ce village\n",
|
||||
" prediction, neighbors, votes = knn_classify(\n",
|
||||
" row['latitude'], \n",
|
||||
" row['longitude'], \n",
|
||||
" df_without, \n",
|
||||
" k=k\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" # Si la prédiction ne correspond pas au vrai département\n",
|
||||
" if prediction != row['dept_simple']:\n",
|
||||
" anomalies.append({\n",
|
||||
" 'village': row['Nom français'],\n",
|
||||
" 'nom_corse': row['Nom corse'],\n",
|
||||
" 'vrai_dept': row['dept_simple'],\n",
|
||||
" 'prediction': prediction,\n",
|
||||
" 'votes_2A': votes.get('2A', 0),\n",
|
||||
" 'votes_2B': votes.get('2B', 0),\n",
|
||||
" 'latitude': row['latitude'],\n",
|
||||
" 'longitude': row['longitude']\n",
|
||||
" })\n",
|
||||
" \n",
|
||||
" return pd.DataFrame(anomalies)\n",
|
||||
"\n",
|
||||
"print(\"Recherche des villages 'anomaliques' avec k=5...\\n\")\n",
|
||||
"anomalies_df = find_anomalous_villages(df, k=5)\n",
|
||||
"\n",
|
||||
"print(f\"Nombre de villages anomaliques : {len(anomalies_df)}\")\n",
|
||||
"print(f\"\\nVillages qui seraient classifiés dans le mauvais département :\\n\")\n",
|
||||
"print(anomalies_df[['village', 'nom_corse', 'vrai_dept', 'prediction', 'votes_2A', 'votes_2B']])\n",
|
||||
"\n",
|
||||
"# Afficher ces villages sur une carte\n",
|
||||
"if len(anomalies_df) > 0:\n",
|
||||
" m_anomalies = folium.Map(location=[42.15, 9.05], zoom_start=9)\n",
|
||||
" \n",
|
||||
" for idx, row in anomalies_df.iterrows():\n",
|
||||
" folium.Marker(\n",
|
||||
" location=[row['latitude'], row['longitude']],\n",
|
||||
" popup=f\"<b>{row['village']}</b><br>Vrai: {row['vrai_dept']}<br>Prédit: {row['prediction']}<br>Votes: {row['votes_2A']}-{row['votes_2B']}\",\n",
|
||||
" icon=folium.Icon(color='orange', icon='exclamation-triangle', prefix='fa')\n",
|
||||
" ).add_to(m_anomalies)\n",
|
||||
" \n",
|
||||
" display(m_anomalies)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
631
activite1/knn_interactive_full.html
Normal file
631
activite1/knn_interactive_full.html
Normal file
File diff suppressed because one or more lines are too long
388
activite1/villages_corse.csv
Normal file
388
activite1/villages_corse.csv
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue