-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathrecoding-data.jl
More file actions
3098 lines (2534 loc) · 112 KB
/
recoding-data.jl
File metadata and controls
3098 lines (2534 loc) · 112 KB
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.20.5
using Markdown
using InteractiveUtils
# ╔═╡ 52fdd6f8-b472-4224-8b8c-1276f0cdb0b7
using HypertextLiteral, PlutoUI; TableOfContents()
# ╔═╡ dee44068-3258-401b-aaf9-a22379817f59
using Tidier
# ╔═╡ d886ef58-5314-4a82-a0b0-f076ba670387
begin
# If the subfolder "data" does not exist, create it
if !("data" in readdir())
mkdir("data")
end
# If not already downloaded,
# Download the MITRE Synthea synthetic data zip file containing multiple .csv files
if !("data.zip" in readdir("data"))
download("https://mitre.box.com/shared/static/aw9po06ypfb9hrau4jamtvtz0e5ziucz.zip", "data/data.zip")
end
using ZipFile # needed for unzipping files
zip_file = ZipFile.Reader("data/data.zip")
for file in zip_file.files[2:end]
seekstart(file)
file_name = str_replace(file.name, "csv/", "data/")
write(file_name, read(file))
end
end
# ╔═╡ 4042d526-18bd-11f0-2d79-8b6ff113de76
begin
html_string = join(readlines("header.html"), "\n")
HypertextLiteral.@htl("""$(HypertextLiteral.Bypass(html_string))""")
end
# ╔═╡ a60ab41c-717e-4f6b-9cca-0a4b37fd1020
md"""
# Recoding data
Recoding data refers to the act of converting data from its raw form into one that is analytically relevant and useful. Most commonly, this involves converting a continuous variable into a set of categories based on a set of criteria.
For example, it's common that age is represented in datasets as a continuous variable. However, if we want to know how many medications are taken by adults versus by children, we need to first break age into categories. In this case, we may simply need to apply a threshold for age above which we consider someone an adult, and otherwise they are a child. However, sometimes there are multiple criteria that need to be considered across multiple columns in recoding data.
Recoding can also involve lumping together many into a fewer number of categories. Fundamentally, this lesson use of logic to create categories irrespective of the form of the source data.
We will learn about `if_else` and `case_when`, which when used within `@mutate` provide a very flexible and extendable approach to recoding data. We will also begin to introduce some of the convenience functions for handling categorical data (from TidierCats), although this will discussed in more depth in the section on [Working with categorical data](working-with-categorical-data.html).
Before we start, let's load the `Tidier` package and the dataset.
"""
# ╔═╡ fa1ab250-9c22-4049-ac56-ce72b235a269
md"""
# Loading `Tidier.jl`
We will start off by loading the Tidier meta-package.
"""
# ╔═╡ b9770ec7-1345-4ccb-abfe-79c99959f8d7
md"""
# Prelude: downloading the MITRE Synthea Dataset
!!! info "Download the MITRE Synthea dataset"
This page assumes that you have already downloaded the MITRE Synthea dataset. If you have not done it yet, please go to the [What is Tidier.jl](what-is-tidier-jl.html) page and follow the instruction on downloading the MITRE Synthea Dataset.
After downloading the dataset, we will read in the `data/patients.csv` file into a data frame.
"""
# ╔═╡ 3dd56607-ab8c-477d-8714-1051e5a87eb2
patients = @chain read_file("data/patients.csv") @clean_names
# ╔═╡ b6c3d41f-b7b4-47a5-9ae6-b6fde37a9225
md"""
# Case study: Calculating age categories
While our motivating example at the beginning focused on converting raw age into age categories, real world data remind us that life isn't quite this simple. Real-world health datasets rarely record age as a column. While age on a given date may be provided as a calculated, the raw data typically encode date of birth and death, and they leave it as an exercise to the analyst to calculate the age.
While this seems inconvenient, this is actually preferred because we rarely care about the age as of today's date. Instead, we usually want to know the age in relation to a specific event (e.g., on the day of surgery).
So, before we can recode the age into a categories, we first need to calculate age in years.
"""
# ╔═╡ dbbdf3ea-7df9-4990-9ddf-4eb4dcde671a
md"""
## First, let's calculate age
For the sake of this analysis, let's assume that we want to know everyone's age as of the most recent date when this dataset was updated. For simplicity, let's assume that this corresponds to the most recent date recorded in *either* the `birthdate` or `deathdate` columns.
"""
# ╔═╡ 260366bf-35fe-4fb4-81ef-4c0a6eda1e5f
most_recent_birth_date = @chain patients begin
@slice_max(birthdate, with_ties = false)
@pull(birthdate) # 1
end
# 1: `@pull` pulls a column from a data frame and returns a vector
# ╔═╡ ef44271c-3df6-43a1-9f1f-8eb49d6b1ca1
most_recent_death_date = @chain patients begin
@slice_max(deathdate, with_ties = false)
@pull(deathdate)
end
# ╔═╡ 46cb6a28-88f9-4cfc-935f-324c2dc46622
todays_date = maximum([most_recent_birth_date, most_recent_death_date])
# ╔═╡ 541603d5-71dd-41c9-ac85-0da45ec922d3
md"""
The most recent `birthdate` recorded in the dataset is $most_recent_birth_date. The most recent `deathdate` recorded in the dataset is $most_recent_death_date.
Overall, the `deathdate` is the most recent, so we will assume that the age we are calculating is the latest age as of $todays_date, which we will consider as "today's date." Remember that in real life, we will usually want the age of an index event which may differ for each patient, and not necessarily a universal date for all patients.
## Let's look at the missingness patterns for the `birthdate` and `deathdate` columns.
If we want to calculate age, we want to make sure we have all the data we need, and that there is no missing data.
"""
# ╔═╡ db1f643d-18cf-455c-ba91-8f741b382198
@chain patients begin
@group_by(birth_date_missing = ismissing(birthdate),
death_date_missing = ismissing(deathdate))
@summarize(n = n())
@ungroup()
end
# ╔═╡ fee90e72-788e-481d-bbbf-71ce3005b34d
md"""
It looks like all patients have a birth date recorded. On the other hand, 1,000 patients have a missing date of death, with only 163 having a recorded death date.
This leads us to our first complication.
## Age is calculated differently for those are alive vs. dead
### For alive patients
For patients with a missing death date, we want to know their age as of `todays_date` ($todays_date). Let's go ahead and filter our dataset to only those patients with a missing `deathdate`, and then calculate their age as of `todays_date`.
!!! info "Note: we have to interpolate todays_date"
Because all variables in `Tidier.jl` are assumed to be in a "data frame scope," referring to `todays_date` in our analysis would lead to an error because `Tidier.jl` wouldn't be able to find a colun named `todays_date` in the data frame. Hence, we need to interpolate it in from the parent scope, which requires us to use `@eval` at the start of `@chain` and `$todays_date` to interpolate in its value.
"""
# ╔═╡ 7b62aa0d-0b1b-42a3-a896-31e67cf19500
@eval @chain patients begin # 1
@filter(ismissing(deathdate)) # 2
@mutate(age =
difftime( # 3
DateTime($todays_date), # 4
DateTime(birthdate),
"days") / 365.25) # 5
@select(birthdate, deathdate, age)
@slice_head(n = 10)
end
# 1: Note the use of `@eval` here because we subsequently use `$` for interpolation
# 2: filter to those with a missing death date
# 3: `difftime` comes from the TidierDates.jl package, reimplementing the same function from base R. `difftime` requires `DateTime` types, so the each of the Dates are converted into DateTaimes.
# 4: Interpolate in `todays_date` from the parent scope
# 5: This is an approximateion to convert days to years, where 365.25 accounts for leap years
# ╔═╡ da450461-ffce-42a9-8612-6c32534a2d9e
md"""
### For dead patients
For the 163 patients in the dataset who have died, age can be calculated by taking the difference between the date of birth and death. For these patients, we would not want to know their age as of `todays_date` because today's date is not relevant to their actual age as of today's date.
"""
# ╔═╡ 512f191f-57e2-47a3-a25f-034e96333ef2
@eval @chain patients begin
@filter(!ismissing(deathdate))
@mutate(age =
difftime(
DateTime(deathdate),
DateTime(birthdate),
"days") / 365.25)
@select(birthdate, deathdate, age)
@slice_head(n = 10)
end
# ╔═╡ 8638b0c2-fb9c-4843-baea-1b6e8eb1f61b
md"""
In both of the prior cases, we first filtered the data to those who are alive or dead, and then we calculated their age using different a calculation for each category.
What if we wanted to calculate the age for the whole dataset? Here, we encounter our first use of `if_else`, which we will subsequently use for recoding data.
## Conditionally creating a column
To create a age column for both alive and dead patients, we will break this calculation into 2 steps. First, we will create a new column that indicates the date on which we want to calculate the age, which is $todays_date for alive patients and the `deathdate` column for dead patients.
Then, we will use take a difference between that new column and the date of birth to calculate age.
For convenience, we will store the result to a new `patients_age` data frame.
"""
# ╔═╡ 1cb8751d-6b2c-42fd-a836-3777c9670d05
patients_age =
@eval @chain patients begin
@mutate(todays_date = # 1
if_else(
ismissing(deathdate),
$todays_date,
deathdate)
)
@mutate(age =
difftime(
DateTime(todays_date),
DateTime(birthdate),
"days") / 365.25
)
@select(birthdate, deathdate, age, gender)
end
# This creates a column `todays_date` in the `patients` data frame, to which we assign the appropriate date based on whether a patient is alive or dead
# ╔═╡ 1c86df78-10c8-497f-af42-0f67032d4706
md"""
# Recoding using `if_else`
Now that we have a column representing age, we can use the `if_else` function to recode age into two values: "Adult" and "Child".
"""
# ╔═╡ 4650642f-0a14-4d87-b9c2-fcf11d436c9b
@chain patients_age begin
@mutate(age_category = if_else(age >= 18, "Adult", "Child"))
end
# ╔═╡ b87accbf-005a-4793-b775-6dce03afbc73
md"""
## `if_else` vs. `ifelse`
!!! info "Note: if_else is a more powerful version of ifelse"
Experienced users may notice that e are using `if_else` rather than the `ifelse` function that is built into Julia. In this instance, `if_else` is not merely an alias but actually refers to a separate function that is bundled with `Tidier.jl`. The main difference between the two functions is that `if_else` includes an optional 4th argument (i.e., `if_else(condition, true, false, missing)`) that describes which value to return if the condition returns a `missing` value.
While we did not use this optional argument here, it can come in quite handy. Take a look at this example, in which we determine whether a patient a patient died before or after 2020. Since `deathdate` is missing for alive patients, we can optionally assign it a value using `if_else`.
"""
# ╔═╡ a399e93d-1ecd-4282-aee5-3c09e135a443
@chain patients begin
@mutate(died_since_2020 =
if_else(
deathdate > ymd("2020-01-01"),
"Died after 2020",
"Died before 2020",
"Still alive"))
@count(died_since_2020)
end
# ╔═╡ d104b2c4-e0bd-42b6-8310-c0582fd770b0
md"""
Returning to our original recoding, let's take a look at how many adults and children we have in the dataset. Remember that `@count(age_category)` is a shortcut for `@group_by(age_category) @summarize(n = n())`.
"""
# ╔═╡ b07dd5e1-db0c-4bfa-81ee-5cff5dd062ea
@chain patients_age begin
@mutate(age_category = if_else(age >= 18, "Adult", "Child"))
@count(age_category)
end
# ╔═╡ a8a6c435-0cad-4cc6-bcfd-7c6dc9d62179
md"""
As long as we have only 2 categories to create (or 3 if our condition returns missing values), then `if_else` is the preferred way to recode data. But if we have more categories, then `case_when` is preferred.
# Recoding using `case_when`
The syntax for `case_when` consists of pairs of `condition => value`, where the value is assigned for the first condition that is met for each row. This means that it is perfectly fine for the conditions to overlap, as long as the earliest condition met takes precedence.
Let's create an age category where those 75 and older are considered older adults on one extreme, and those below 4 are considered infants. We can start with those of the highest age as conditions, and sequentially lower the age until we get to "infants" as the returned value. It is common practice, but not required, to include a final condition `true`, which assigns the return value to all remaining rows.
"""
# ╔═╡ 7ce1f522-387e-473c-a946-1cbfa3f37ae7
@chain patients_age begin
@mutate(age_category =
case_when(age >= 75 => "Older adult",
age >= 18 => "Adult",
age >= 4 => "Child",
true => "Infant")
)
@count(age_category)
end
# ╔═╡ 81163c1a-b2b9-467a-97ce-5501026f7ae4
md"""
If the final condition is not set to `true`, then any rows that do not meet any of the conditions will be assigned a missing value. Here's an example:
"""
# ╔═╡ 32c9a6b5-258c-4dd1-ae41-d3b1bf8056b4
@chain patients_age begin
@mutate(age_category =
case_when(age >= 75 => "Older adult",
age >= 18 => "Adult",
age >= 4 => "Child",
age >= 1 => "Infant")
)
@count(age_category)
end
# ╔═╡ ab6950fc-a178-4cc7-af1e-9e202f14df5b
md"""
Unlike `if_else`, `case_when` does not have an argument dedicated to what to do if the conditions return a `missing` value. However, you can represent the missingness as a condition within the `case_when` function.
"""
# ╔═╡ b9b69efe-c3fb-4d98-ba9c-22af09264428
@chain patients begin
@mutate(died_since_2020 =
case_when(
deathdate > ymd("2020-01-01") => "Died after 2020",
ismissing(deathdate) => "Still alive",
true => "Died before 2020")
)
@count(died_since_2020)
end
# ╔═╡ a4e16054-826c-438d-a4d6-67059daf7fd1
md"""
`case_when` is great not only for situations involving 3+ return values, but also for situations involving complex conditions involving multiple columns of data. Here, we created a separate value ofr male and female adults but left the child and infant categories unchanged.
"""
# ╔═╡ 2a7dce86-cb60-4981-b973-820d73587c92
@chain patients_age begin
@mutate(age_category =
case_when(age >= 75 && gender == "F" => "Older adult female",
age >= 75 && gender == "M" => "Older adult male",
age >= 18 && gender == "F" => "Adult female",
age >= 18 && gender == "M" => "Adult male",
age >= 4 => "Child",
true => "Infant")
)
@count(age_category)
end
# ╔═╡ 8faed932-457a-40d4-95c6-ca7c44f048a9
md"""
!!! info "Disambiguating gender and sex"
Many health datasets, including this one, confuse sex with gender. Whereaas `male` and `female` refer to biological sex, gender refers to a person's identity, which may be similar or different from a person's biological sex. While we chose to use this synthetic dataset because it is much closer to real-world health datasets in good ways, it is unfortunately also similar to real-world data in bad ways.
Any time a dataset applies a label to a person, the same issues can arise. For eaxmple, similar issues of categorization and miscategorization can apply to the way in which race and ethnicity are elicited and recorded in health datasets.
"""
# ╔═╡ 5aea8d11-425e-4d66-85b5-12e4649140be
md"""
# Bespoke `TidierCats.jl` functions for recoding variables
If you think of `if_else` and `case_when` as the workhorses of recoding data, `TidierCats.jl` provides a set of bespoke functions for achieving many of the same goals as our two handy go-to functions. Fancy, fancy!
Had we wanted to chop age into categories, we could've achieved this using CategoricalArrays `cut` function (reexported by TidierCats). THe `extend = true` argument ensures that the values higher than the greatest threshold (i.e., those older than 75) are lumped in with the highest category (in this case, "Older adult").
## `cut` for cutting continuous values into categories
"""
# ╔═╡ d1e255ae-4f4c-409a-a18b-508f6750deaf
@chain patients_age begin
@mutate(age_category =
~cut( # 1
age,
[0, 4, 18, 75],
labels = ["Infant", "Child", "Adult", "Older adult"],
extend = true))
@count(age_category)
end
# 1: Note that we had to prefix `cut` with `~` because `cut` operates on the entire column (and not rowwise on each row), so we do not want it to be vectorized. If you omit the tilde, it will produce an error. Once we update `TidierData` to include `cut` on the list of functions not to vectorize, the `~` will no longer be required, and we will update this code.
# ╔═╡ 2c25e3d3-2861-4c26-80df-e10d7e78a941
md"""
In this case, we get the same exact numbers as what we calculated using `case_when`, but any time you use a bespoke function, you have to be wary as to how threhsolds are applied. If you read the documentation for `cut`, you'll see that the left side of the interval is is inclusive, but the right side is not. In this case, it produces the same logic as what we had coded in `case_when` because we had made the left side inclusive (e.g., `age >= 18`).
This is noticeable if you remove the `labels` keyword argument and let `cut` produce an automatic label. With intervals, square brackets `[]` are considered inclusive, and parentheses `()` are considered exclusive.
"""
# ╔═╡ a385861d-7036-49f0-a4c0-fbbfe04d52c5
@chain patients_age begin
@mutate(age_category =
~cut(
age,
[0, 4, 18, 75],
# labels = ["Infant", "Child", "Adult", "Older adult"],
extend = true))
@count(age_category)
end
# ╔═╡ 066d4e1c-4e89-41ea-a3e1-8905b8e54d55
md"""
Had we wanted to code `age > 18` instead of `age >= 18`, then `cut` would not have been helpful. Thus, my personal preference is to rely on `case_when` rather than `cut`.
Another subtle difference you'll notice if you hover over or tap on the results table produced by TidierCats functions (like `cut`) is that TidierCats functions always return a `CategoricalValue` type, whereas `case_when` returned a string.
"""
# ╔═╡ a46051c1-301d-4744-880a-8c378e69ca2f
md"""
## A brief taste of `cat_*` functions
TidierCats provides a number of functions beginning with `cat_` which make it possible to write concise code. We will dive deeper into the TidierCats packate in the lesson on [Working with categorical data](working-with-categorical-data.html). Here, we'll give you a brief taste of how `Tidier.jl` makes it easy to work with categorical variables.
Let's say you have a categorical variable `age_category`, and now you want to lump any categories with fewer than 10% of the values into a new "Other" category. While this is totally achievable using `case_when`, it would require a bit of thought and work to calculate the proportions before recoding the data.
With `cat_lump_prop`, we can achieve this in a single concise line of code:
"""
# ╔═╡ 023f1d53-267d-4abb-8b86-c8639a259317
@chain patients_age begin
@mutate(age_category =
~cut(
age,
[0, 4, 18, 75],
labels = ["Infant", "Child", "Adult", "Older adult"],
extend = true))
@mutate(age_category = cat_lump_prop(age_category, 0.10))
@count(age_category)
end
# ╔═╡ 7f8e63ab-0110-4ff3-b1b6-8f412623cd83
md"""
!!! info "Use caution when lumping categories"
Just because you *can* lump together categories doesn't mean that you *should*. In this case, you'll notice that infants got lumped with older adults into an "Other" category. We would pretty much never want to lump these two groups together for analytical purposes, so use the TidierCats functions with a healthy dose of caution. With great power comes great responsibility.
"""
# ╔═╡ bf44ca88-2e8b-44af-99af-f68a68d64191
md"""
# Summary
In this lesson, we learned about the two main workhorse functions for recoding data: `if_else` and `case_when`. We also dipped our toes into some of the bespoke TidierCats functions that can help with both recoding data and handling categorical variables more broadly.
We will return to some of these advanced capabilities in the [Working with categorical data](working-with-categorical-data.html) section.
"""
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
Tidier = "f0413319-3358-4bb0-8e7c-0c83523a93bd"
ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"
[compat]
HypertextLiteral = "~0.9.5"
PlutoUI = "~0.7.62"
Tidier = "~1.6.1"
ZipFile = "~0.10.1"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.11.3"
manifest_format = "2.0"
project_hash = "38fdc33f8289778f8fc675da809ec071fc288243"
[[deps.AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.5.0"
weakdeps = ["ChainRulesCore", "Test"]
[deps.AbstractFFTs.extensions]
AbstractFFTsChainRulesCoreExt = "ChainRulesCore"
AbstractFFTsTestExt = "Test"
[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "6e1d2a35f2f90a4bc7c2ed98079b2ba09c35b83a"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.3.2"
[[deps.AbstractTrees]]
git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.4.5"
[[deps.Accessors]]
deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "MacroTools"]
git-tree-sha1 = "3b86719127f50670efe356bc11073d84b4ed7a5d"
uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
version = "0.1.42"
[deps.Accessors.extensions]
AxisKeysExt = "AxisKeys"
IntervalSetsExt = "IntervalSets"
LinearAlgebraExt = "LinearAlgebra"
StaticArraysExt = "StaticArrays"
StructArraysExt = "StructArrays"
TestExt = "Test"
UnitfulExt = "Unitful"
[deps.Accessors.weakdeps]
AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5"
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
[[deps.Adapt]]
deps = ["LinearAlgebra", "Requires"]
git-tree-sha1 = "f7817e2e585aa6d924fd714df1e2a84be7896c60"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "4.3.0"
weakdeps = ["SparseArrays", "StaticArrays"]
[deps.Adapt.extensions]
AdaptSparseArraysExt = "SparseArrays"
AdaptStaticArraysExt = "StaticArrays"
[[deps.AdaptivePredicates]]
git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6"
uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7"
version = "1.2.0"
[[deps.AliasTables]]
deps = ["PtrArrays", "Random"]
git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff"
uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8"
version = "1.1.3"
[[deps.Animations]]
deps = ["Colors"]
git-tree-sha1 = "e092fa223bf66a3c41f9c022bd074d916dc303e7"
uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340"
version = "0.4.2"
[[deps.ArgCheck]]
git-tree-sha1 = "f9e9a66c9b7be1ad7372bbd9b062d9230c30c5ce"
uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197"
version = "2.5.0"
[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.2"
[[deps.ArnoldiMethod]]
deps = ["LinearAlgebra", "Random", "StaticArrays"]
git-tree-sha1 = "d57bd3762d308bded22c3b82d033bff85f6195c6"
uuid = "ec485272-7323-5ecc-a04f-4719b315124d"
version = "0.4.0"
[[deps.ArrayInterface]]
deps = ["Adapt", "LinearAlgebra"]
git-tree-sha1 = "017fcb757f8e921fb44ee063a7aafe5f89b86dd1"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "7.18.0"
[deps.ArrayInterface.extensions]
ArrayInterfaceBandedMatricesExt = "BandedMatrices"
ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices"
ArrayInterfaceCUDAExt = "CUDA"
ArrayInterfaceCUDSSExt = "CUDSS"
ArrayInterfaceChainRulesCoreExt = "ChainRulesCore"
ArrayInterfaceChainRulesExt = "ChainRules"
ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore"
ArrayInterfaceReverseDiffExt = "ReverseDiff"
ArrayInterfaceSparseArraysExt = "SparseArrays"
ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore"
ArrayInterfaceTrackerExt = "Tracker"
[deps.ArrayInterface.weakdeps]
BandedMatrices = "aae01518-5342-5314-be14-df237901396f"
BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e"
ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2"
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527"
ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c"
[[deps.ArrayLayouts]]
deps = ["FillArrays", "LinearAlgebra"]
git-tree-sha1 = "4e25216b8fea1908a0ce0f5d87368587899f75be"
uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
version = "1.11.1"
weakdeps = ["SparseArrays"]
[deps.ArrayLayouts.extensions]
ArrayLayoutsSparseArraysExt = "SparseArrays"
[[deps.Arrow]]
deps = ["ArrowTypes", "BitIntegers", "CodecLz4", "CodecZstd", "ConcurrentUtilities", "DataAPI", "Dates", "EnumX", "Mmap", "PooledArrays", "SentinelArrays", "StringViews", "Tables", "TimeZones", "TranscodingStreams", "UUIDs"]
git-tree-sha1 = "00f0b3f05bc33cc5b68db6cc22e4a7b16b65e505"
uuid = "69666777-d1a9-59fb-9406-91d4454c9d45"
version = "2.8.0"
[[deps.ArrowTypes]]
deps = ["Sockets", "UUIDs"]
git-tree-sha1 = "404265cd8128a2515a81d5eae16de90fdef05101"
uuid = "31f734f8-188a-4ce0-8406-c8a06bd891cd"
version = "2.3.0"
[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
version = "1.11.0"
[[deps.Automa]]
deps = ["PrecompileTools", "SIMD", "TranscodingStreams"]
git-tree-sha1 = "a8f503e8e1a5f583fbef15a8440c8c7e32185df2"
uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
version = "1.1.0"
[[deps.AxisAlgorithms]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712"
uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
version = "1.1.0"
[[deps.AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.7"
[[deps.BangBang]]
deps = ["Accessors", "ConstructionBase", "InitialValues", "LinearAlgebra"]
git-tree-sha1 = "26f41e1df02c330c4fa1e98d4aa2168fdafc9b1f"
uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"
version = "0.4.4"
[deps.BangBang.extensions]
BangBangChainRulesCoreExt = "ChainRulesCore"
BangBangDataFramesExt = "DataFrames"
BangBangStaticArraysExt = "StaticArrays"
BangBangStructArraysExt = "StructArrays"
BangBangTablesExt = "Tables"
BangBangTypedTablesExt = "TypedTables"
[deps.BangBang.weakdeps]
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
version = "1.11.0"
[[deps.Baselet]]
git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e"
uuid = "9718e550-a3fa-408a-8086-8db961cd8217"
version = "0.1.1"
[[deps.BitFlags]]
git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d"
uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35"
version = "0.1.9"
[[deps.BitIntegers]]
deps = ["Random"]
git-tree-sha1 = "f98cfeaba814d9746617822032d50a31c9926604"
uuid = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"
version = "0.3.5"
[[deps.BitTwiddlingConvenienceFunctions]]
deps = ["Static"]
git-tree-sha1 = "f21cfd4950cb9f0587d5067e69405ad2acd27b87"
uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b"
version = "0.1.6"
[[deps.Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.9+0"
[[deps.CEnum]]
git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.5.0"
[[deps.CPUSummary]]
deps = ["CpuId", "IfElse", "PrecompileTools", "Static"]
git-tree-sha1 = "5a97e67919535d6841172016c9530fd69494e5ec"
uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9"
version = "0.2.6"
[[deps.CRC32c]]
uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc"
version = "1.11.0"
[[deps.CRlibm_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc"
uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0"
version = "1.0.1+0"
[[deps.CSV]]
deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"]
git-tree-sha1 = "deddd8725e5e1cc49ee205a1964256043720a6c3"
uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
version = "0.10.15"
[[deps.Cairo]]
deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"]
git-tree-sha1 = "71aa551c5c33f1a4415867fe06b7844faadb0ae9"
uuid = "159f3aea-2a34-519c-b102-8c37f9878175"
version = "1.1.1"
[[deps.CairoMakie]]
deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"]
git-tree-sha1 = "15d6504f47633ee9b63be11a0384925ba0c84f61"
uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
version = "0.13.2"
[[deps.Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "2ac646d71d0d24b44f3f8c84da8c9f4d70fb67df"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.18.4+0"
[[deps.Cascadia]]
deps = ["AbstractTrees", "Gumbo"]
git-tree-sha1 = "c0769cbd930aea932c0912c4d2749c619a263fc1"
uuid = "54eefc05-d75b-58de-a785-1a3403f0919f"
version = "1.0.2"
[[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"
weakdeps = ["JSON", "RecipesBase", "SentinelArrays", "StructTypes"]
[deps.CategoricalArrays.extensions]
CategoricalArraysJSONExt = "JSON"
CategoricalArraysRecipesBaseExt = "RecipesBase"
CategoricalArraysSentinelArraysExt = "SentinelArrays"
CategoricalArraysStructTypesExt = "StructTypes"
[[deps.Chain]]
git-tree-sha1 = "9ae9be75ad8ad9d26395bf625dea9beac6d519f1"
uuid = "8be319e6-bccf-4806-a6f7-6fae938471bc"
version = "0.6.0"
[[deps.ChainRulesCore]]
deps = ["Compat", "LinearAlgebra"]
git-tree-sha1 = "1713c74e00545bfe14605d2a2be1712de8fbcb58"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.25.1"
weakdeps = ["SparseArrays"]
[deps.ChainRulesCore.extensions]
ChainRulesCoreSparseArraysExt = "SparseArrays"
[[deps.Cleaner]]
deps = ["PrettyTables", "Tables", "Unicode"]
git-tree-sha1 = "664021fefeab755dccb11667cc96263ee6d7fdf6"
uuid = "caabdcdb-0ab6-47cf-9f62-08858e44f38f"
version = "1.1.1"
[[deps.CloseOpenIntervals]]
deps = ["Static", "StaticArrayInterface"]
git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581"
uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9"
version = "0.1.13"
[[deps.Clustering]]
deps = ["Distances", "LinearAlgebra", "NearestNeighbors", "Printf", "Random", "SparseArrays", "Statistics", "StatsBase"]
git-tree-sha1 = "3e22db924e2945282e70c33b75d4dde8bfa44c94"
uuid = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5"
version = "0.15.8"
[[deps.CodecInflate64]]
deps = ["TranscodingStreams"]
git-tree-sha1 = "d981a6e8656b1e363a2731716f46851a2257deb7"
uuid = "6309b1aa-fc58-479c-8956-599a07234577"
version = "0.1.3"
[[deps.CodecLz4]]
deps = ["Lz4_jll", "TranscodingStreams"]
git-tree-sha1 = "d58afcd2833601636b48ee8cbeb2edcb086522c2"
uuid = "5ba52731-8f18-5e0d-9241-30f10d1ec561"
version = "0.4.6"
[[deps.CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.8"
[[deps.CodecZstd]]
deps = ["TranscodingStreams", "Zstd_jll"]
git-tree-sha1 = "d0073f473757f0d39ac9707f1eb03b431573cbd8"
uuid = "6b39b394-51ab-5f42-8807-6242bab2b4c2"
version = "0.8.6"
[[deps.ColorBrewer]]
deps = ["Colors", "JSON"]
git-tree-sha1 = "e771a63cc8b539eca78c85b0cabd9233d6c8f06f"
uuid = "a2cac450-b92f-5266-8821-25eda20663c8"
version = "0.4.1"
[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
git-tree-sha1 = "403f2d8e209681fcbd9468a8514efff3ea08452e"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.29.0"
[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.5"
[[deps.ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"]
git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.10.0"
weakdeps = ["SpecialFunctions"]
[deps.ColorVectorSpace.extensions]
SpecialFunctionsExt = "SpecialFunctions"
[[deps.Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "64e15186f0aa277e174aa81798f7eb8598e0157e"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.13.0"
[[deps.CommonWorldInvalidations]]
git-tree-sha1 = "ae52d1c52048455e85a387fbee9be553ec2b68d0"
uuid = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8"
version = "1.0.0"
[[deps.Compat]]
deps = ["TOML", "UUIDs"]
git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "4.16.0"
weakdeps = ["Dates", "LinearAlgebra"]
[deps.Compat.extensions]
CompatLinearAlgebraExt = "LinearAlgebra"
[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.1.1+0"
[[deps.CompositionsBase]]
git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad"
uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b"
version = "0.1.2"
weakdeps = ["InverseFunctions"]
[deps.CompositionsBase.extensions]
CompositionsBaseInverseFunctionsExt = "InverseFunctions"
[[deps.ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"
[[deps.ConcurrentUtilities]]
deps = ["Serialization", "Sockets"]
git-tree-sha1 = "d9d26935a0bcffc87d2613ce14c527c99fc543fd"
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
version = "2.5.0"
[[deps.ConstructionBase]]
git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157"
uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
version = "1.5.8"
weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"]
[deps.ConstructionBase.extensions]
ConstructionBaseIntervalSetsExt = "IntervalSets"
ConstructionBaseLinearAlgebraExt = "LinearAlgebra"
ConstructionBaseStaticArraysExt = "StaticArrays"
[[deps.Contour]]
git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.6.3"
[[deps.CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "a692f5e257d332de1e554e4566a4e5a8a72de2b2"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.4"
[[deps.CpuId]]
deps = ["Markdown"]
git-tree-sha1 = "fcbb72b032692610bfbdb15018ac16a36cf2e406"
uuid = "adafc99b-e345-5852-983c-f28acb93d879"
version = "0.3.1"
[[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.DBInterface]]
git-tree-sha1 = "a444404b3f94deaa43ca2a58e18153a82695282b"
uuid = "a10d1c49-ce27-4219-8d33-6db1a4562965"
version = "2.6.1"
[[deps.DataAPI]]
git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.16.0"
[[deps.DataFrames]]
deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "fb61b4812c49343d7ef0b533ba982c46021938a6"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.7.0"
[[deps.DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.22"
[[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"
version = "1.11.0"
[[deps.DecFP]]
deps = ["DecFP_jll", "Printf", "Random", "SpecialFunctions"]
git-tree-sha1 = "88e521a871a1b11488a1f48b8c085b4be8f71be5"
uuid = "55939f99-70c6-5e9b-8bb0-5071ed7d61fd"
version = "1.4.1"
[[deps.DecFP_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e9a8da19f847bbfed4076071f6fef8665a30d9e5"
uuid = "47200ebd-12ce-5be5-abb7-8e082af23329"
version = "2.0.3+1"
[[deps.DefineSingletons]]
git-tree-sha1 = "0fba8b706d0178b4dc7fd44a96a92382c9065c2c"
uuid = "244e2a9f-e319-4986-a169-4d1fe445cd52"
version = "0.1.2"
[[deps.DelaunayTriangulation]]
deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"]
git-tree-sha1 = "5620ff4ee0084a6ab7097a27ba0c19290200b037"
uuid = "927a84f5-c5f4-47a5-9785-b46e178433df"
version = "1.6.4"
[[deps.Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.12"
weakdeps = ["ChainRulesCore", "SparseArrays"]
[deps.Distances.extensions]
DistancesChainRulesCoreExt = "ChainRulesCore"
DistancesSparseArraysExt = "SparseArrays"
[[deps.Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
version = "1.11.0"
[[deps.Distributions]]
deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"]
git-tree-sha1 = "0b4190661e8a4e51a842070e7dd4fae440ddb7f4"
uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
version = "0.25.118"
[deps.Distributions.extensions]
DistributionsChainRulesCoreExt = "ChainRulesCore"
DistributionsDensityInterfaceExt = "DensityInterface"
DistributionsTestExt = "Test"
[deps.Distributions.weakdeps]