generated from ajamj/static-export-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ML_SL_Classification.jl
2398 lines (1941 loc) · 90.5 KB
/
ML_SL_Classification.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
### A Pluto.jl notebook ###
# v0.19.26
using Markdown
using InteractiveUtils
# ╔═╡ 07a4255e-4b73-4fc9-8059-67727789dae1
using Plots, PlutoUI, Images, DataFrames, Statistics, NamedArrays, ScikitLearn, RDatasets, StatsBase, PyCall, PyPlot
# ╔═╡ d1cac0d0-5615-4096-bae1-1dca4eef3538
using ScikitLearn.CrossValidation
# ╔═╡ f33c17c5-9fa0-496f-bee3-3e8c4ea1e684
using NearestNeighbors
# ╔═╡ 8f160e80-f75d-11ed-037b-4d1603fba55d
md"""
# Supervised Learning: Classification
"""
# ╔═╡ d2483a60-aa2d-4287-9bfe-477f2ff93c72
md"""
## Pakeeeet
"""
# ╔═╡ a3de3ec7-ea17-4a94-8cd6-cca79fa2a725
TableOfContents(title="📚 Table of Contents", indent=true, depth=4, aside=true)
# ╔═╡ b9047edc-7c3b-41ed-b356-8a5949b051ee
md"""
Modul terakhir kita udah bikin model linear regression buat ngeprediksi harga rumah di Boston. Sekarang kita mau bahas masalah klasifikasi yang fokusnya ngeprediksi nilai yang cuma bisa pilih-pilih.
Jadi, data diskrit itu cuma bisa punya nilai tertentu aja, gak bisa sembarangan nilai kayak yang kontinu gitu.
Contoh masalah klasifikasi itu bisa misalnya:
- Ngeprediksi apakah kanker payudara itu jinak atau ganas, berdasarkan fitur-fiturnya.
- Klasifikasi gambar apakah ada kucing, anjing, atau kuda.
- Ngeprediksi apakah email itu spam atau bukan, cuma dari alamat emailnya aja.
Intinya, labelnya itu bentuknya kategori gitu, terbatas banget pilihan-pilihannya.
Data diskrit bisa berupa angka juga, misalnya jumlah murid di kelas, atau bisa juga berupa kategori kayak merah, biru, atau kuning.kuning.
"""
# ╔═╡ 580506b4-33d6-4612-a47f-0f203a066c07
md"""
## Binary and Multi-class Classification
Ada dua jenis klasifikasi: **binary** dan **multi-class**. Kalau ada dua kelas yang mau diprediksi, itu adalah masalah klasifikasi binary. Contohnya, klasifikasi tumor jinak atau ganas. Kalau ada lebih dari dua kelas, maka itu adalah masalah klasifikasi multi-class. Contohnya, mengklasifikasikan spesies bunga iris, yang bisa berupa versicolor, virginica, atau setosa, berdasarkan karakteristik sepal dan petalnya.
Algoritma yang umum digunakan untuk klasifikasi antara lain *regresi logistik*, *k-nearest neighbors*, *pohon keputusan (decision tree)*, *naive bayes*, *support vector machines*, *jaringan saraf tiruan*, dan sebagainya. Di sini kita akan belajar cara menggunakan *k-nearest neighbors* untuk mengklasifikasikan spesies iris.
Supervised learning dibagi menjadi masalah regresi dan masalah klasifikasi. Keduanya memiliki tujuan yang sama, yaitu membangun fungsi pemetaan dari variabel masukan (X) ke variabel keluaran (y). Perbedaannya terletak pada variabel keluarannya, yang kontinu dalam regresi dan kategorikal dalam klasifikasi."""
# ╔═╡ c481d00c-1c8f-44d1-9673-00b978d65cf7
md"""
## Iris Dataset
Jadi, ada database yang terkenal banget namanya database iris. Nah, database ini sebenernya pertama kali digunakan sama Sir R. A. Fisher dan sering banget dipakai di literatur pengenalan pola (pattern recognition). Di database ini, ada 150 tanaman iris, dan tiap tanaman punya 4 atribut numerik yang diukur: panjang sepal dalam satuan cm, lebar sepal dalam satuan cm, panjang kelopak dalam satuan cm, dan lebar kelopak dalam satuan cm.
Nah, tugasnya adalah memprediksi jenis setiap tanaman iris ini. Jenisnya ada 3, yaitu iris-setosa, iris-versicolor, atau iris-virginica. Jadi, berdasarkan atribut-atribut yang udah diukur ini, kita mau nebak jenis tanaman irisnya tuh termasuk yang mana.
"""
# ╔═╡ 2d5ee741-4849-4e38-8d3c-95c0bd67e30b
load(download("https://api.sololearn.com/DownloadFile?id=3831"))
# ╔═╡ 1fb09b83-c335-4279-bc35-0d86a09beb6e
md"""
Buat ngeliat list dataset yang ada dalam paket RDatasets, lo bisa pake:
"""
# ╔═╡ 6c0cd3f2-2dba-452b-8e45-849e34e24397
RDatasets.datasets()
# ╔═╡ f0e542ef-7f09-4a3f-ab6c-2700a5138fc0
md"""
Kalo lo nyari, di datasets tersebut ada dataset iris.
"""
# ╔═╡ 07ac9fbc-f60f-4fd5-afb0-2fbbf1071957
filter(row -> row.Dataset == "iris", RDatasets.datasets())
# ╔═╡ 3558b2bd-f25f-4a90-8712-3bb5ae0e738f
md"""
Untuk ngeload datasetnya, lo bisa pake:
"""
# ╔═╡ a2068e1d-8424-4163-9203-198587291ceb
iris = dataset("datasets", "iris")
# ╔═╡ 1d273845-9d26-4bf2-b6f6-5cad6dbe533d
md"""
Parameter pertama diisi dengan nama `Package`. Parameter kedua diisi dengan nama `Dataset`.
"""
# ╔═╡ 1caeb827-fc6a-49a2-bb6f-097c720f91dc
md"""
Buat ngecek dimensi dan beberapa baris pertama dari DataFrame-nya di Julia, kita bisa pake cara berikut:
"""
# ╔═╡ ccefc503-3bfd-4c17-9e99-07abbc86f889
size(iris) # Cek dimensinya
# ╔═╡ 1687eca6-36a7-4fb7-85ae-e6b1f2aafb6e
first(iris, 5) # Liat baris pertamanya, cuma 5 aja
# ╔═╡ 51ca8d59-aa75-4b3f-b7d6-960635bf167b
md"""
Jadi, `size(iris)` bakal ngasih tau kita berapa jumlah baris dan kolom dari DataFrame `iris`-nya. Terus, `first(iris, 5)` bakal nampilin baris-baris pertama dari DataFrame `iris`, cuma 5 aja.
"""
# ╔═╡ 271dcd99-fdc7-4918-9f98-547638aa0a0b
md"""
Pas kita lagi belajar tentang algoritma machine learning, menggunakan data yang sederhana dan gampang dipahami seperti dataset bunga iris itu bermanfaat banget. Data yang kayak gitu bisa bantu ngekurangi learning curve dan bikin lebih mudah buat dipahami dan debug.
Dengan data yang sederhana dan well-behaved kayak iris flower dataset, kita bisa lebih fokus ke konsep-konsep dasar dari algoritma machine learning tanpa terlalu ribet sama masalah kompleks dari data yang lebih rumit. Ini jadi kesempatan bagus buat memahami algoritma dan melakukan debugging dengan lebih efektif.
Jadi, dengan menggunakan dataset yang well-behaved seperti iris flower dataset, kita bisa lebih lancar dan nyaman dalam mempelajari algoritma machine learning.
"""
# ╔═╡ 3b895a35-2893-4bc5-bf54-be1d0d415c68
md"""
Cek statistik ringkasnya:
"""
# ╔═╡ e8e970a8-e8b1-4214-92ac-25b307241c9a
describe(iris,:all)
# ╔═╡ 2801bb38-cfb4-46b5-aaea-f19d5cb4cec3
md"""
Fungsi `describe()` ini bakal nampilin statistik ringkas dari setiap kolom dalam DataFrame `iris`, kayak jumlah data, rata-rata, standar deviasi, nilai minimum, nilai maksimum, dan juga kuartil-kuartilnya.
"""
# ╔═╡ 231ef95b-547e-404e-9366-ee486ba5478f
md"""
Semua fitur di sini angkanya beda-beda dan gak ada yang kelewat atau hilang nilainya, jadi datanya udah rapi nih.
Rentang angka dari setiap atribut ini masih sekitar level yang sama, jadi kita bisa lewatin langkah standarisasi dulu. Tapi, tau gak, kadang-kadang kita perlu standardisasi ini buat ngelakuin preprosesing yang penting buat algoritma machine learning. Jadi, bisa dibilang ini tuh scaling fiturnya, biar lebih enak diproses sama algoritma. Kalo mau tau lebih detil soal pentingnya scaling fitur, bisa cek sendiri deh.
Jadi, intinya sekarang kita skip dulu standardisasinya karena rentang angkanya masih mirip-mirip, tapi tetep harus diinget ya, standardisasi bisa jadi langkah yang penting di tahap pra-pemrosesan tergantung dari algoritma machine learning yang digunakan.
"""
# ╔═╡ 5fc14e03-3f89-46f6-88bc-070b3d26b8f1
md"""
## Class Distribution
Dataset ini ada 3 kelas dengan masing-masing 50 contoh data. Buat ngecek ini, kita bisa lakukan langkah berikut ini:
"""
# ╔═╡ 9cb7b0f7-9379-4847-892b-f4c64ee3d62f
combine(groupby(iris, :Species), nrow => :size)
# ╔═╡ 9cc250b2-38a1-40c0-ac5f-28df3472c525
md"""
Metode di atas berguna banget supaya kita tahu distribusi datanya. Dataset iris ini termasuk balanced dataset karena jumlah data di setiap kelas terdistribusi dengan rata.
Contohnya, dataset `fraud` itu termasuk imbalanced dataset. Biasanya, cuma sebagian kecil dari total transaksi yang beneran penipuan, sekitar 1 dari 1000. Nah, kalo dataset imbalanced, analisis yang sedikit berbeda bakal digunakan. Jadi, penting buat kita ngerti apakah datanya *balanced* atau *imbalanced*.
Imbalanced dataset itu artinya kelas-kelas dalam data gak terwakili secara sama. Buat liat lebih lanjut tentang data imbalanced, cek [link ini](https://machinelearningmastery.com/tactics-to-combat-imbalanced-classes-in-your-machine-learning-dataset/), bro.
"""
# ╔═╡ 5408755c-014c-4a78-99db-6e451cb81de0
md"""
# Data Visualization
## Univariate Plot
Buat lebih paham tentang setiap atributnya, kita bisa mulai dengan plot univariat, maksudnya plot untuk masing-masing variabelnya. Buat dataset iris ini, kita bisa bikin histogramnya pake kode berikut:
"""
# ╔═╡ bfc3c304-7bfb-4ae3-9144-f8d12ee0c12d
begin
p1 = histogram(iris[:,1], title="SepalLength")
p2 = histogram(iris[:,2], title="SepalWidth")
p3 = histogram(iris[:,3], title="PetalLength")
p4 = histogram(iris[:,4], title="PetalWidth")
Plots.plot(p1, p2, p3, p4, layout=(2, 2), legend=false)
end
# ╔═╡ facfa26e-b5be-44d9-930b-a974f0c0a313
md"""
Plot histogramnya ngasih kita gambaran yang lebih jelas tentang distribusi dari variabel masukan. Kita bisa lihat bahwa sepal length dan sepal width punya distribusi yang "normal" banget, bro. Artinya, distribusinya punya bentuk symmetric bell shape dan kece banget. Tapi, beda ceritanya sama panjang kelopak bunga (petal length). Plotnya nunjukin ada dua puncak, satu deket nol dan satu lagi di sekitar lima. Nah, buat lebar kelopak (petal width), pola-pola yang munculnya sedikit banget.
Histogram itu sejenis grafik batang yang nunjukin jumlah atau frekuensi relatif dari nilai-nilai yang jatuh dalam interval atau rentang kelas yang berbeda. Ada juga plot univariat lainnya kayak density plot sama boxplot yang bisa bantu kita ngeliatin ringkasan dari variabelnya.
"""
# ╔═╡ 55954998-3298-4b75-9f78-0cc72aef7125
md"""
## Multivariate Plot
Bro, buat ngeliat interaksi antara atribut-atributnya, kita bisa pake scatter plot. Tapi, susah banget kan ngeliat grup-grupnya kalo kita gak tahu spesies asli dari bunga yang direpresentasikan oleh data. Makanya, kita bikin kode warna buat masing-masing spesies biar bisa bedain spesiesnya secara visual:
"""
# ╔═╡ 0bf07e51-4aa9-4dd5-a47c-d0209462d03a
begin
# Membangun kamus yang memetakan spesies ke kode integer
inv_name_dict = Dict(
"setosa" => 1,
"versicolor" => 2,
"virginica" => 3
)
colors = [inv_name_dict[item] for item in iris.Species]
# Plot scatter
Plots.scatter(iris.SepalLength, iris.SepalWidth, group = colors, xlabel = "sepal length (cm)", ylabel = "sepal width (cm)", legend = :topleft)
end
# ╔═╡ 4158b26b-0b9d-405b-a8af-00e390530805
begin
# Membangun kamus yang memetakan spesies ke kode integer
petal_name_dict = Dict(
"setosa" => 1,
"versicolor" => 2,
"virginica" => 3
)
colors_petal = [petal_name_dict[item] for item in iris.Species]
# Plot scatter
Plots.scatter(iris.PetalLength, iris.PetalWidth, group = colors_petal, xlabel = "petal length (cm)", ylabel = "petal width (cm)", legend = :topleft)
end
# ╔═╡ cd90839c-1e54-4c0a-b44b-13c9cbd26429
md"""
## K nearest neighbors
Bro, K nearest neighbors (KNN) itu model machine learning yang bersifat supervised. Jadi, model ini akan ngambil satu data point, trus lihat k data point terdekat yang udah diketahui labelnya, terus nentuin label untuk data point tersebut berdasarkan mayoritas suara.
Kita bisa lihat bahwa mengubah nilai k bisa berpengaruh pada hasil prediksi model. Dalam KNN, k itu adalah hyperparameter. Nah, hyperparameter itu adalah parameter yang nilainya udah ditentuin sebelum proses pembelajaran dimulai. Nanti kita akan belajar cara nge-tune hyperparameter ini.
Misalnya, lihat gambar di bawah ini. Ada dua kelas: kotak biru dan segitiga merah. Label apa yang harus kita berikan ke titik hijau yang belum diketahui labelnya, berdasarkan algoritma 3NN, yaitu ketika k=3? Dari 3 data point terdekat dari titik hijau (lingkaran garis solid), dua di antaranya adalah segitiga merah dan satu lagi kotak biru. Jadi, titik hijau diprediksi sebagai segitiga merah. Kalo k=5 (lingkaran garis putus-putus), maka diprediksi sebagai kotak biru (3 kotak biru versus 2 segitiga merah, kotak biru lebih banyak).
Jadi, dalam KNN, kita memilih label berdasarkan mayoritas suara dari tetangga terdekatnya sesuai dengan nilai k yang kita tentukan.
"""
# ╔═╡ eeb5a22c-74ab-48ed-a31c-1529b6e472ff
load(download("https://api.sololearn.com/DownloadFile?id=3838"))
# ╔═╡ d72e8039-47e4-4732-b18f-32ca4ad8e68f
md"""
Di dalam Julia, algoritma k nearest neighbors diimplementasikan dalam paket NearestNeighbors:
"""
# ╔═╡ 2fc06331-1613-4c48-9888-cd88658f0579
md"""
Lihatlah contohnya dalam dataset iris kita di bawah ini. Tiga tetangga paling dekat dari data yang ditunjuk oleh warna merah adalah setosa (warna ungu).
"""
# ╔═╡ 6cc7e533-c49a-4ef7-933c-e18189ac0d49
load(download("https://api.sololearn.com/DownloadFile?id=3839"))
# ╔═╡ 2c72f583-c0b8-455d-a1f7-33d6355d086a
md"""
Jadi, berdasarkan 3-nn, data yang ditunjuk oleh panah tersebut seharusnya diberi label iris-setosa juga.
K nearest neighbors juga bisa digunakan untuk masalah regresi. Perbedaannya terletak pada cara prediksinya. Di KNN untuk regresi, bukan menggunakan mayoritas suara, melainkan menggunakan rata-rata label dari k data point terdekat untuk membuat prediksi. Jadi, nilai prediksi regresi dihasilkan dari rata-rata label dari k tetangga terdekat tersebut.
"""
# ╔═╡ bb1eb372-68b8-46ef-a7e1-9a77ca77025c
md"""
# Modeling
## Data Preparation
Sebelumnya kita mengidentifikasi bahwa panjang dan lebar kelopak bunga adalah fitur yang paling berguna untuk memisahkan spesies-spesies tersebut; maka kita menentukan fitur dan label sebagai berikut:
"""
# ╔═╡ 615411e4-dcfc-4ad9-853b-8b05be671d2f
X = reshape(convert(Array, [iris.SepalLength iris.SepalWidth iris.PetalLength iris.PetalWidth]), (150, 4))
# ╔═╡ 0a364111-5d8b-4d3e-80f8-103b556b71e3
y = convert(Array, iris.Species)
# ╔═╡ 747e49b4-9295-448f-9018-e56e25555369
md"""
Ingat, untuk mengevaluasi kinerja model, kita melakukannya pada data yang tidak dilihat oleh model saat proses konstruksi. Oleh karena itu, kita menyisihkan sebagian data sebagai data uji yang akan mensimulasikan data yang tidak diketahui oleh model di masa depan. Seperti yang kita lakukan dalam modul sebelumnya, kita menggunakan `train_test_split` dari `ScikitLearn.CrossValidation`.
"""
# ╔═╡ 30bce95f-656d-4382-aba4-f73733aa1e36
X_train, X_test, Y_train, Y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# ╔═╡ c9e24a4d-3fa8-4ebb-a03e-7d68f3f9a0d5
md"""
Kita menggunakan pembagian 70-30, yaitu 70% data digunakan untuk pelatihan dan 30% untuk pengujian.
"""
# ╔═╡ 72603079-c545-4eef-b986-f5518bf409ed
size(Y_train)
# ╔═╡ b78406ec-385b-42d7-9e57-9beac25f0561
size(Y_test)
# ╔═╡ 0970e48d-4e9f-47be-beef-b120d83bf0c9
countmap(Y_train)
# ╔═╡ 7462a53a-9e05-4dc3-a1ad-2ccd9b2ebb7c
md"""
## Modeling
Sekarang kita siap untuk membangun dan melatih model knn. Pertama, kita mengimpor kelas modelnya:
"""
# ╔═╡ 4bf31ddb-015b-4f43-a35e-e0607a103a64
@sk_import neighbors: KNeighborsClassifier
# ╔═╡ a936e05a-26eb-4d76-ac49-b2bfad4bae57
knn = KNeighborsClassifier(n_neighbors=5)
# ╔═╡ 9f1cf2c2-a1a6-4d5f-bbe2-11bbf18eda0a
md"""
Perhatikan bahwa parameter yang perlu kita atur dalam masalah ini adalah `n_neighbors`, atau `k` seperti dalam knn. Kita setel `k` menjadi 5 secara acak.
Gunakan data `X_train` dan `Y_train` untuk melatih model:
"""
# ╔═╡ 819815d6-74a5-4c46-a689-2f7d267bf391
ScikitLearn.fit!(knn, X_train, Y_train)
# ╔═╡ 338c9a25-459c-4bfd-9ac7-1805d8b91591
md"""
## Prediction
Untuk melakukan prediksi dalam ScikitLearn, kita dapat memanggil metode `predict()`. Kita akan mencoba memprediksi spesies iris menggunakan fitur-fitur yang ada dalam matriks fitur X.
Mari kita lakukan prediksi pada data uji dan simpan hasilnya dalam variabel pred untuk ditinjau nanti:
"""
# ╔═╡ 01d4e401-4a28-4088-bcd1-c9828da9f4dc
pred = ScikitLearn.predict(knn, X_test)
# ╔═╡ fc2f493f-ffe0-4964-8fac-2761ea001d36
md"""
Mari kita tinjau lima prediksi pertama:
"""
# ╔═╡ 0d7c720a-28ab-4448-976b-bfabeea2fccc
first_five_pred = pred[1:5]
# ╔═╡ 02e1e36b-8243-4c82-ab65-0e3649c6b83f
md"""
## Probability Prediction
Dari semua algoritma klasifikasi yang diimplementasikan dalam scikit-learn, terdapat metode tambahan `predict_prob`. Alih-alih membagi label, metode ini menghasilkan probabilitas untuk target dalam bentuk array. Mari kita lihat bagaimana probabilitas yang diprediksi untuk bunga ke-11 dan ke-12:
"""
# ╔═╡ beabe24e-f5b5-444a-a082-f9670a138193
proba = predict_proba(knn, X_test)
# ╔═╡ 3c6ef5e6-8f6d-40fb-9adb-3710160f1513
eleventh_proba = proba[11, :]
# ╔═╡ 5f06af67-c860-4ae1-9268-1a24727c343f
twelfth_proba = proba[12, :]
# ╔═╡ ae754d3a-a794-4f07-bc7e-dc0890dab4a8
pred[11:12]
# ╔═╡ beb863ea-ebc9-4cc0-98f5-c66754a7edd9
combine(groupby(iris, :Species), nrow => :size)
# ╔═╡ cbc9212f-3d45-438c-b7cf-0774c1a0a1b4
md"""
# Model Evaluation
## Accuracy
Dalam klasifikasi, metrik yang paling simpel adalah akurasi. Ini menghitung proporsi dari data-data yang label prediksinya sama persis dengan label yang sebenarnya. Mantap banget deh!
"""
# ╔═╡ 843f09d8-a930-4fb3-81ea-5873cb6257fd
sum(pred .== Y_test)
# ╔═╡ ccc977b9-e194-4ee1-9ce6-99d7323004c8
size(Y_test)
# ╔═╡ ca1c7225-9943-4ae2-a3e5-38d53cbd01cd
md"""
Akurasi:
"""
# ╔═╡ f7e1e53e-0097-43eb-9efb-77c1e6bd99a8
(sum(pred .== Y_test)/size(Y_test)[1])
# ╔═╡ 335cfa81-ddb0-4462-b341-fb5d56a769d8
md"""
Woooooooooow... Akurasinya $(sum(pred .== Y_test)/size(Y_test)[1]*100)%! Mantap banget, kan! Akurasi dari model juga bisa dicek pake fungsi berikut:
"""
# ╔═╡ 8c726c0f-1264-4dda-85ff-a2eef87abd42
knn.score(X_test, Y_test)
# ╔═╡ b2abc829-f6f2-48d5-b93a-40b1bf6b1125
md"""
## Confusion Matrix
Dalam model klasifikasi, metric yang paling gampang dipahamin adalah akurasi. Nah, akurasi ini menghitung proporsi data yang hasil prediksinya cocok persis sama kayak label yang aslinya. Jadi, semakin tinggi akurasi, semakin bagus lah prediksi modelnya.
Tapi, hati-hati ya! Akurasi aja kadang bisa menyesatkan, terutama kalo jumlah data di setiap kelas beda-beda atau kalo ada lebih dari dua kelas di datasetnya. Nah, supaya lebih jelas apa yang bener dan apa yang salah, kita bisa pake yang namanya confusion matrix. Itu tuh kayak ringkasan yang ngitung jumlah prediksi yang bener dan yang salah, terus dipecah-pecah berdasarkan tiap kelas.
Kalo kita lagi klasifikasi iris, kita bisa pake `confusion_matrix()` buat ngitungnya. Nah, jadi kita bisa liat hasil prediksi yang bener dan juga jenis kesalahan yang sering dilakuin sama modelnya.
"""
# ╔═╡ 58d544f4-58ae-40a0-aac8-fb2dbbe8a03c
md"""
### 1. Confusion Matrix: ScikitLearn Julia
"""
# ╔═╡ 5cd3c624-1a4c-492e-bb28-ac18b32a15c1
@sk_import metrics: confusion_matrix
# ╔═╡ 1e42e413-fd83-40b8-af0a-a50f38dc43bc
cm = confusion_matrix(Y_test, pred)
# ╔═╡ 4207c0ea-594d-492b-96ca-2accf38b6150
function cm_fix(m)
return reverse(m, dims=1)
end
# ╔═╡ 4d876256-1b0e-415a-b7f1-9015d9113d6e
md"""
### 2. Confusion Matrix: sklearn matplolib python
"""
# ╔═╡ 3473835e-a3d4-4c1f-9c62-efa8f9852549
sklearn_metrics = pyimport("sklearn.metrics")
# ╔═╡ 29785a52-33c2-4eb1-89c6-4b0a425b824b
matplotlib_pyplot = pyimport("matplotlib.pyplot")
# ╔═╡ 0abb981a-f8b9-43a2-a0a0-b101f2fbe83f
display_labels = ["setosa", "versicolor", "virginica"]
# ╔═╡ e01f3319-6eec-4e68-84e6-2fd19c5c3ac1
function plot_cm(m)
heatmap(cm_fix(cm), c=:dense, colorbar=:true, xlabel="Predicted label", ylabel="True label", xtickfont=font(10), ytickfont=font(10), aspect_ratio=:equal, title="Confusion Matrix")
for i in 1:size(cm_fix(cm), 1)
for j in 1:size(cm_fix(cm), 2)
annotate!(j, i, Plots.text(string(cm_fix(cm)[i, j]), :orange, :center))
end
end
xticks!(1:size(cm_fix(cm), 2), display_labels)
yticks!(1:size(cm_fix(cm), 1), reverse(display_labels))
plot!()
end
# ╔═╡ 040e8cb2-4ae2-4d77-8f56-caf1571d329f
plot_cm(cm)
# ╔═╡ 88ad99ec-e3cf-44f5-a187-4921dec3a649
cm_py = sklearn_metrics.confusion_matrix(Y_test, pred, labels=display_labels)
# ╔═╡ bab49bf4-1628-4512-ac8d-3cb4c7af13d9
begin
display_obj = sklearn_metrics.ConfusionMatrixDisplay(cm_py,display_labels=display_labels)
# Plot confusion matrix
figure, ax = matplotlib_pyplot.subplots()
display_obj.plot(include_values=true, ax=ax)
ax.set_xticklabels(display_labels)
ax.set_yticklabels(display_labels)
ax.set_xlabel("Predicted", fontdict=Dict([("fontsize",12)]))
ax.set_ylabel("True", fontdict=Dict([("fontsize",12)]))
ax.set_title("Confusion Matrix")
end
# ╔═╡ 968a4212-62c5-468a-a8cb-7e3bfb760432
figure
# ╔═╡ e3d674dd-ce1b-4971-aa04-470635f52776
md"""
Di sini kita sebutin labelnya secara berurutan. Setiap kolom dari matriks ini nunjukin kelas yang diprediksi dan setiap baris nunjukin sama kelas yang sebenarnya. Jadi jumlah di setiap baris itu nunjukin total jumlah instansi dari kelas tersebut.
Baris pertama ngacu ke iris-setosa aslinya; [15, 0, 0] nunjukin bahwa 15 iris-setosa diprediksi dengan benar, dan gak ada yang salah diprediksi; sedangkan baris terakhir [0, 1, 14] nunjukin bahwa dari 15 iris-virginica yang asli, gak ada yang diprediksi jadi iris-setosa, 1 diprediksi jadi iris-versicolor, dan sisanya 14 diprediksi dengan benar sebagai iris-virginica. Ini konsisten sama yang kita lihat pas exploratory data analysis, bahwa ada beberapa tumpang tindih antara dua spesies ini di scatter plot, dan lebih sulit membedakan iris-versicolor sama iris-virginica daripada mengidentifikasi iris-setosa.
Confusion matrix ini tuh tabel yang sering dipakai buat ngambarkan performa model klasifikasi (atau "classifier") di atas set data test yang nilai-nilainya udah diketahui.
"""
# ╔═╡ cc98d7d2-7d00-4667-ac3f-171e23cb33c9
md"""
## K-fold Cross Validation
Sebelumnya kita udah bagi data jadi data train dan data test sebelum ngefit modelnya biar kita bisa ngecek performa modelnya di data test. Itu metode cross validation sederhana, yang juga dikenal sebagai metode holdout. Tapi, pembagian data secara acak bisa mempengaruhi performa model. Buat ngatasin ini, kita perkenalkan k-fold cross validation.
Di k-fold cross validation, data dibagi jadi k subset. Terus, metode holdout diulang sebanyak k kali, jadi setiap kali, salah satu subset dari k subset itu dipake sebagai data test dan k-1 subset lainnya digabung buat ngetrain modelnya. Terus akurasinya diambil rata-rata dari k percobaan untuk nunjukin efektivitas total modelnya. Dengan cara ini, semua data point dipake; dan ada lebih banyak metrik jadi kita gak cuma mengandalkan satu data test aja buat ngevaluasi performa model.
Cara termudah buat pake k-fold cross-validation di scikit-learn adalah manggil fungsi cross_val_score di atas model dan datasetnya:
"""
# ╔═╡ c82d886c-4c38-42ca-a10f-7d291f50fdf0
knn_cv = KNeighborsClassifier(n_neighbors=5)
# ╔═╡ 39d03ee2-4a15-4937-883a-7e49531e4056
md"""
Setiap holdout set berisi 20% dari data asli.
"""
# ╔═╡ 0b9ba632-3b1d-47dd-bb74-5b8db194772e
cv_scores = cross_val_score(knn_cv, X, y, cv=5)
# ╔═╡ 83f627ad-5c58-4a36-b27b-b93afcfc875b
md"""
Bro, kayak yang lo lihat, gara-gara pembagian data yang acak, akurasi pada holdsets ini fluktuatif antara 0.9 sampe 1 gitu.
"""
# ╔═╡ c4f59d6d-6a30-4ccf-bdc9-1d9ece45d21f
mean_cv = round(mean(cv_scores), digits=2)
# ╔═╡ e7bf0e2a-e7dc-4d77-a718-9fa9aefeaf7a
md"""
Kita nggak bisa mengandalkan hanya satu pembagian train-test aja, yang kita tahu model 3nn ini memiliki akurasi $(mean_cv*100)% berdasarkan cross validation 5-fold.
Sebagai aturan umum, cross validation dengan 5-fold atau 10-fold lebih disukai, tapi sebenarnya nggak ada aturan formalnya sih. Ketika nilai `k` semakin besar, perbedaan ukuran antara set pelatihan dan subset resampling semakin kecil. Ketika perbedaan ini mengecil, bias dari teknik ini juga semakin kecil.
"""
# ╔═╡ 22f91140-8526-4b6e-a87c-e1fc8039d6c6
md"""
## Grid Search
Jadi begini, pas kita bikin model knn pertama, kita atur hyperparameter k-nya jadi 5, terus kemudian jadi 3 pas di k-fold cross validation, random banget sih pilihannya. Nah, pertanyaannya adalah, k yang paling oke tuh yang mana ya? Nah, buat nyari k yang paling optimal, kita bisa pake alat yang keren abis namanya grid search.
Di scikit-learn, kita bisa pake GridSearchCV, yang ngelatih model kita beberapa kali pake berbagai nilai yang kita tentuin pake param_grid, terus diitungin skor cross validation-nya. Jadi kita bisa ngecek mana nilai dari hyperparameter yang kita uji yang paling bagus performanya.
Contohnya kodingannya gini bro:"""
# ╔═╡ bad128da-a052-4395-8745-d94347159cd1
param_grid = Dict("n_neighbors" => collect(2:10))
# ╔═╡ ca3056b6-40a0-4e58-bb2d-5ad33c07c91a
knn2 = KNeighborsClassifier()
# ╔═╡ 721e2f06-e897-4b21-92c9-f6ae03200ea1
knn_gscv = GridSearch.GridSearchCV(knn2, param_grid, cv=5)
# ╔═╡ ad890540-39aa-450d-b96f-dc3670774023
#fit model to data
ScikitLearn.fit!(knn_gscv, X, y)
# ╔═╡ 9f0d4f8c-4a3d-4014-9f8c-48e4a4d95102
knn_gscv.best_estimator_
# ╔═╡ d33d0c9d-cc73-4d92-8942-1db38997c3ab
knn_gscv.best_score_
# ╔═╡ d6119bf9-32ac-4118-8cea-c11125ec070e
knn_final = KNeighborsClassifier(n_neighbors=knn_gscv.best_estimator_["n_neighbors"])
# ╔═╡ 991cd30c-544d-412a-9964-abb471981c10
knn_final.fit(X, y)
# ╔═╡ 2360f8b8-4798-44cb-8109-8846b6ca4955
knn_final.score(X, y)
# ╔═╡ b43dc017-f34b-4a36-971a-952c5db815b6
md"""
Teknik k-fold cross validation dan tuning parameter dengan grid search itu bisa dipake buat dua jenis masalah nih, baik masalah klasifikasi maupun regresi. Jadi nggak cuma buat satu jenis masalah aja, tapi bisa digunakan secara luas. Mantap kan? Jadi kita bisa ngoptimalkan performa model baik buat klasifikasi maupun regresi pake kedua teknik tersebut. Keren abis!
"""
# ╔═╡ 3a87ebdf-6d1f-416f-808f-5a42b3a2d5d5
md"""
# Label Prediction
## Label Prediction with New Data
Oke bro, sekarang kita siap deploy model kita yang udah jadi, yaitu 'knn_final'. Kita punya data ukuran beberapa iris nih, dan panjang dan lebar kelopaknya (sepal) adalah 5.84 cm dan 3.06 cm, sedangkan panjang dan lebar mahkotanya (petal) adalah 3.76 cm dan 1.20 cm. Nah, gimana caranya kita buat prediksi pake model yang udah kita buat tadi?
Kita bisa menggunakan fungsi 'predict' dari model tersebut. Karena model ini dilatih dengan menggunakan panjang dan lebar mahkota sebagai fitur, maka kita butuh data panjang dan lebar mahkota untuk membuat prediksi. Jadi, kita tinggal masukin data tersebut ke fungsi 'predict' untuk mendapatkan hasil prediksinya. Mantap kan? Cukup mudah dan cepat!
Kita masukkan data panjang dan lebar petal ke sebuah array:
"""
# ╔═╡ 00f9b6c7-51de-421c-9253-9a9f47823c53
new_data = [5.84 3.06 3.76 1.20]
# ╔═╡ 85418996-359f-421f-8fd0-e61f6272f75f
new_data_reshaped = reshape(new_data, (1, length(new_data)))
# ╔═╡ 7f42a7c4-bdfe-425c-8ab8-f7a4281b0b01
knn_final.predict(new_data_reshaped)
# ╔═╡ 32887089-c21c-4c71-9d20-d05cb9ff0502
md"""
## Probability Prediction with New Data
Kita lagi mau ngomongin nih tentang data tanaman iris yang seru banget. Ada tiga tanaman iris yang kita bahas, dan semua tanamannya punya lebar kelopak yang sama, yaitu 2.25cm. Tapi bedanya tuh ada di panjang kelopaknya, nih. Ada satu tanaman dengan panjang kelopak 5.03 cm, ada juga yang 3.85 cm, dan satu lagi cuma 1.77 cm.
"""
# ╔═╡ 78efc5c5-81d0-469b-a8b6-c6702ac1680d
data_new = [3.76 1.2; 5.25 1.2; 1.58 1.2]
# ╔═╡ 67c1704d-91e5-4a93-b93b-eb15af5f32c6
ScikitLearn.predict(knn_final, data_new)
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
NamedArrays = "86f7a689-2022-50b4-a561-43c23ac3c673"
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee"
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
ScikitLearn = "3646fa90-6ef7-5e7e-9f22-8aca16db6324"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
[compat]
DataFrames = "~1.5.0"
Images = "~0.25.2"
NamedArrays = "~0.9.8"
NearestNeighbors = "~0.4.13"
Plots = "~1.38.12"
PlutoUI = "~0.7.51"
PyCall = "~1.95.1"
PyPlot = "~2.11.1"
RDatasets = "~0.7.7"
ScikitLearn = "~0.7.0"
StatsBase = "~0.33.21"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.9.0"
manifest_format = "2.0"
project_hash = "d64d1715858e71985f83dba8e7bdced7419bcd66"
[[deps.AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "16b6dbc4cf7caee4e1e75c49485ec67b667098a0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.3.1"
weakdeps = ["ChainRulesCore"]
[deps.AbstractFFTs.extensions]
AbstractFFTsChainRulesCoreExt = "ChainRulesCore"
[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.4"
[[deps.Adapt]]
deps = ["LinearAlgebra", "Requires"]
git-tree-sha1 = "76289dc51920fdc6e0013c872ba9551d54961c24"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.6.2"
weakdeps = ["StaticArrays"]
[deps.Adapt.extensions]
AdaptStaticArraysExt = "StaticArrays"
[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"
[[deps.ArnoldiMethod]]
deps = ["LinearAlgebra", "Random", "StaticArrays"]
git-tree-sha1 = "62e51b39331de8911e4a7ff6f5aaf38a5f4cc0ae"
uuid = "ec485272-7323-5ecc-a04f-4719b315124d"
version = "0.2.0"
[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
[[deps.AxisAlgorithms]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
git-tree-sha1 = "66771c8d21c8ff5e3a93379480a2307ac36863f7"
uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
version = "1.0.1"
[[deps.AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "1dd4d9f5beebac0c03446918741b1a03dc5e5788"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.6"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[deps.BitFlags]]
git-tree-sha1 = "43b1a4a8f797c1cddadf60499a8a077d4af2cd2d"
uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35"
version = "0.1.7"
[[deps.Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"
[[deps.CEnum]]
git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.2"
[[deps.CSV]]
deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"]
git-tree-sha1 = "ed28c86cbde3dc3f53cf76643c2e9bc11d56acc7"
uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
version = "0.10.10"
[[deps.Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+1"
[[deps.CatIndices]]
deps = ["CustomUnitRanges", "OffsetArrays"]
git-tree-sha1 = "a0f80a09780eed9b1d106a1bf62041c2efc995bc"
uuid = "aafaddc9-749c-510e-ac4f-586e18779b91"
version = "0.2.2"
[[deps.CategoricalArrays]]
deps = ["DataAPI", "Future", "Missings", "Printf", "Requires", "Statistics", "Unicode"]
git-tree-sha1 = "1568b28f91293458345dabba6a5ea3f183250a61"
uuid = "324d7699-5711-5eae-9e2f-1d82baa6b597"
version = "0.10.8"
[deps.CategoricalArrays.extensions]
CategoricalArraysJSONExt = "JSON"
CategoricalArraysRecipesBaseExt = "RecipesBase"
CategoricalArraysSentinelArraysExt = "SentinelArrays"
CategoricalArraysStructTypesExt = "StructTypes"
[deps.CategoricalArrays.weakdeps]
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
SentinelArrays = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
[[deps.ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "e30f2f4e20f7f186dc36529910beaedc60cfa644"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.16.0"
[[deps.Clustering]]
deps = ["Distances", "LinearAlgebra", "NearestNeighbors", "Printf", "Random", "SparseArrays", "Statistics", "StatsBase"]
git-tree-sha1 = "a6e6ce44a1e0a781772fc795fb7343b1925e9898"
uuid = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5"
version = "0.15.2"
[[deps.CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.1"
[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
git-tree-sha1 = "be6ab11021cd29f0344d5c4357b163af05a48cba"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.21.0"
[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.4"
[[deps.ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.10"
[[deps.Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.10"
[[deps.Combinatorics]]
git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860"
uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
version = "1.0.2"
[[deps.Compat]]
deps = ["UUIDs"]
git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "4.6.1"
weakdeps = ["Dates", "LinearAlgebra"]
[deps.Compat.extensions]
CompatLinearAlgebraExt = "LinearAlgebra"
[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.0.2+0"
[[deps.ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"
[[deps.ConcurrentUtilities]]
deps = ["Serialization", "Sockets"]
git-tree-sha1 = "96d823b94ba8d187a6d8f0826e731195a74b90e9"
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
version = "2.2.0"
[[deps.Conda]]
deps = ["Downloads", "JSON", "VersionParsing"]
git-tree-sha1 = "e32a90da027ca45d84678b826fffd3110bb3fc90"
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
version = "1.8.0"
[[deps.Contour]]
git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.6.2"
[[deps.CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "f9d7112bfff8a19a3a4ea4e03a8e6a91fe8456bf"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.3"
[[deps.Crayons]]
git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.1.1"
[[deps.CustomUnitRanges]]
git-tree-sha1 = "1a3f97f907e6dd8983b744d2642651bb162a3f7a"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.2"
[[deps.DataAPI]]
git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.15.0"
[[deps.DataFrames]]
deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.5.0"
[[deps.DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.13"
[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"
[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[deps.DelimitedFiles]]
deps = ["Mmap"]
git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae"
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
version = "1.9.1"
[[deps.Distances]]
deps = ["LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "49eba9ad9f7ead780bfb7ee319f962c811c6d3b2"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.8"
[[deps.Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
[[deps.DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.9.3"
[[deps.Downloads]]
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
version = "1.6.0"
[[deps.Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.4.8+0"
[[deps.ExprTools]]
git-tree-sha1 = "c1d06d129da9f55715c6c212866f5b1bddc5fa00"
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
version = "0.1.9"
[[deps.FFMPEG]]
deps = ["FFMPEG_jll"]
git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8"
uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
version = "0.4.1"
[[deps.FFMPEG_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Pkg", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
git-tree-sha1 = "74faea50c1d007c85837327f6775bea60b5492dd"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.4.2+2"
[[deps.FFTViews]]
deps = ["CustomUnitRanges", "FFTW"]
git-tree-sha1 = "cbdf14d1e8c7c8aacbe8b19862e0179fd08321c2"
uuid = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
version = "0.3.2"
[[deps.FFTW]]
deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
git-tree-sha1 = "f9818144ce7c8c41edf5c4c179c684d92aa4d9fe"
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
version = "1.6.0"
[[deps.FFTW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea"
uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
version = "3.3.10+0"
[[deps.FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "299dc33549f68299137e51e6d49a13b5b1da9673"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.16.1"
[[deps.FilePathsBase]]
deps = ["Compat", "Dates", "Mmap", "Printf", "Test", "UUIDs"]
git-tree-sha1 = "e27c4ebe80e8699540f2d6c805cc12203b614f12"
uuid = "48062228-2e41-5def-b9a4-89aafe57970f"
version = "0.9.20"
[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
[[deps.FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"
[[deps.Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.93+0"
[[deps.Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"
[[deps.FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.4+0"
[[deps.FriBidi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91"
uuid = "559328eb-81f9-559d-9380-de523a88c83c"
version = "1.0.10+0"
[[deps.Future]]
deps = ["Random"]
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"
[[deps.GLFW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"]
git-tree-sha1 = "d972031d28c8c8d9d7b41a536ad7bb0c2579caca"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.8+0"
[[deps.GR]]
deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"]
git-tree-sha1 = "d014972cd6f5afb1f8cd7adf000b7a966d62c304"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.72.5"
[[deps.GR_jll]]
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt5Base_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "f670f269909a9114df1380cc0fcaa316fff655fb"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.72.5+0"
[[deps.Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"
[[deps.Ghostscript_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "43ba3d3c82c18d88471cfd2924931658838c9d8f"
uuid = "61579ee1-b43e-5ca0-a5da-69d92c66a64b"
version = "9.55.0+4"
[[deps.Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "d3b3624125c1474292d0d8ed0f65554ac37ddb23"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.74.0+2"
[[deps.Graphics]]