Skip to content

Data Wrangler

s3lst_ds.data_wrangling.data_wrangling.DataWrangler

A class for wrangling Sentinel-3, spatial predictor, AOI and and possibly validation Landsat data associated with multiple timestamps.

Attributes:

Name Type Description
data_vars DataVars

Aliases for predictors and target and their kinds.

path_sentinel3 Path

Path to the directory containing Sentinel-3 data folders. Each folder contains a georeferenced Sentinel-3 SLSTR Level-2 LST product file (https://sentiwiki.copernicus.eu/web/slstr-products#S3-SLSTR-Products-L2-LST-Products) and a georeferenced Sentinel-3 Synergy Level-2 product file (https://sentiwiki.copernicus.eu/web/synergy-products#SYNERGYProducts-L2SYNSDRprocessingS3-Synergy-Products-L2-SYN-SDR-processing).

path_spatial_pred Path or None, default=None

Path to the NetCDF file with the spatial predictor data. If not set, no spatial predictor data is considered.

aoi Path or str or None, default=None

WKT string representing the AOI, or path to its shapefile. If not set, no AOI is considered and no masking is applied.

path_landsat Path or None, default=None

Path to the directory containing Landsat 8/9 data folders. Each folder contains a LST.TIF file for georeferenced Level-2 LST, having a resolution of 30 m (https://www.usgs.gov/centers/eros/science/usgs-eros-archive-landsat-archives-landsat-8-9-olitirs-collection-2-level-2). In the wrangling, such data and Sentinel-3's will be "matched" if the respective folders have the same name (it is implied here that the user had analysed the acquisitions obtained by the two platforms and set the names of the Landsat 8/9 data folders as the ones of Sentinel-3's (start sensing times) whose start sensing times and spatial extents are approximately the same).

transform {None, "center", "standardize"}, default=None

The transform to apply on the coarse target (as well as validation one) and coarse and fine spatio-temporal predictors from a copy of the wrangled data in each SingleDataWrangler instance by using coarse data statistics. The transformations are set in SingleDataWrangler'sdatawith the same names as the original columns with the substring"_trans"suffixed to them. Note that the transformations are timestamp-specific, that is, the computed statistics and the applied transformations in each timestamp solely concern the data of that timestamp. The possible values fortransformare: -None- not transforming the data; -"center"- subtracting the mean from the data; -"standardize"` - subtracting the mean from the data and diving the result by the standard deviation.

max_workers int, default=1

Number of simultaneous multiple processes to consider in wrangling with the special cases: - 1 or None: no multiprocessing is considered; - -1: all processors are used; - -k: all processors except k-1 are used.

timestamps list[Timestamp]

Start sensing times associated with each Sentinel-3 data folder of interest.

timestamps_landsat list[Timestamp]

Start sensing times associated with each Sentinel-3 data folder of interest for which there is Landsat data available.

single_data_wrangler dict[Timestamp, SingleDataWrangler]

SingleDataWrangler instances keyed by respective timestamp. These correspond timestamp-specific data wranglers.

metadata DataFrame

Metadata DataFrame associated with the wrangled data having columns: - "timestamp" - "season" - season associated with timestamp; - "landsat_exists" - indicator of existence of Landsat data;

grids (coarse, fine)

Aliases of the wrangled coarse and fine Sentinel-3 grids: - "coarse", with resolution of approximately 1000 m, obtained from the original Sentinel-3 LST data after it being clipped to the AOI bounds; - "fine", with resolution of approximately 300 m, obtained from the original Sentinel-3 SYN data after it being clipped to the AOI bounds.

logger RichLogger or None

A rich logger for showing progress of the wrangling.

show_progress bool, default=True

True to display the wrangling progress.

Methods:

Name Description
__init__

Initialize DataWrangler instance by reading Sentinel-3, spatial predictor, AOI

apply

Use pandas' apply method (of arguments pandas_kwargs) on wrangled and, if

apply_set_data

Use pandas' apply method (of arguments pandas_kwargs) on wrangled and, if

dropna

Use pandas' dropna method (of arguments pandas_kwargs) on wrangled

extract_metadata

Extract the metadata of the wrangled data (timestamps, season and existence of

get_coords

Get Sentinel-3's coordinates associated with issued timestamps and grid

get_data

Get wrangled and, if trans is True, further transformed data vars for

get_data_X_and_mask

Get wrangled and, if trans is True, further transformed predictor and AOI

get_data_y

Get wrangled and, if trans is True, further transformed target data for

get_metadata

Get values of metadata vars associated with the timestamps timestamps of the

get_shape

Get Sentinel-3's grid shape associated with issued timestamps and grid

save

Write the instance to path with joblib.

set_data

Set wrangled and, if trans is True, further transformed data vars of

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
  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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
class DataWrangler:
    """
    A class for wrangling Sentinel-3, spatial predictor, AOI and and possibly validation
    Landsat data associated with multiple timestamps.

    Attributes
    ----------

    data_vars : DataVars
        Aliases for predictors and target and their kinds.

    path_sentinel3 : Path
        Path to the directory containing Sentinel-3 data folders. Each folder contains a
        georeferenced Sentinel-3 SLSTR Level-2 LST product file
        (https://sentiwiki.copernicus.eu/web/slstr-products#S3-SLSTR-Products-L2-LST-Products)
        and a georeferenced Sentinel-3 Synergy Level-2 product file
        (https://sentiwiki.copernicus.eu/web/synergy-products#SYNERGYProducts-L2SYNSDRprocessingS3-Synergy-Products-L2-SYN-SDR-processing).

    path_spatial_pred : Path or None, default=None
        Path to the NetCDF file with the spatial predictor data. If not set, no spatial
        predictor data is considered.

    aoi : Path or str or None, default=None
        WKT string representing the AOI, or path to its shapefile. If not set, no AOI is
        considered and no masking is applied.

    path_landsat : Path or None, default=None
        Path to the directory containing Landsat 8/9 data folders. Each folder contains
        a `LST.TIF` file for georeferenced Level-2 LST, having a resolution of 30 m
        (https://www.usgs.gov/centers/eros/science/usgs-eros-archive-landsat-archives-landsat-8-9-olitirs-collection-2-level-2).
        In the wrangling, such data and Sentinel-3's will be "matched" if the respective
        folders have the same name (it is implied here that the user had analysed the
        acquisitions obtained by the two platforms and set the names of the Landsat 8/9
        data folders as the ones of Sentinel-3's (start sensing times) whose start
        sensing times and spatial extents are approximately the same).

    transform : {None, "center", "standardize"}, default=None
        The transform to apply on the coarse target (as well as validation one) and
        coarse and fine spatio-temporal predictors from a copy of the wrangled `data` in
        each `SingleDataWrangler` instance by using coarse data statistics. The
        transformations are set in
        `SingleDataWrangler's `data` with the same names as the original columns with
        the substring `"_trans"` suffixed to them. Note that the transformations are
        timestamp-specific, that is, the computed statistics and the applied
        transformations in each timestamp solely concern the data of that timestamp. The
        possible values for `transform` are:
            - `None` - not transforming the data;
            - `"center"` - subtracting the mean from the data;
            - `"standardize"` - subtracting the mean from the data and diving the result
            by the standard deviation.

    max_workers : int, default=1
        Number of simultaneous multiple processes to consider in wrangling with the
        special cases:
            - `1` or `None`: no multiprocessing is considered;
            - `-1`: all processors are used;
            - `-k`: all processors except k-1 are used.

    timestamps : list[pd.Timestamp]
        Start sensing times associated with each Sentinel-3 data folder of interest.

    timestamps_landsat : list[pd.Timestamp]
        Start sensing times associated with each Sentinel-3 data folder of interest for
        which there is Landsat data available.

    single_data_wrangler: dict[pd.Timestamp, SingleDataWrangler]
        `SingleDataWrangler` instances keyed by respective timestamp. These correspond
        timestamp-specific data wranglers.

    metadata : pd.DataFrame
        Metadata DataFrame associated with the wrangled data having columns:
            - `"timestamp"`
            - `"season"` - season associated with timestamp;
            - `"landsat_exists"` - indicator of existence of Landsat data;

    grids: ("coarse", "fine")
        Aliases of the wrangled coarse and fine Sentinel-3 grids:
            - `"coarse"`, with resolution of approximately 1000 m, obtained from the
            original Sentinel-3 LST data after it being clipped to the AOI bounds;
            - `"fine"`, with resolution of approximately 300 m, obtained from the
            original Sentinel-3 SYN data after it being clipped to the AOI bounds.

    logger : RichLogger or None
        A rich logger for showing progress of the wrangling.

    show_progress : bool, default=True
        `True` to display the wrangling progress.


    """

    # ---> Class attributes
    grids: ClassVar[tuple[str, ...]] = ("coarse", "fine")

    # ---> Instance methods
    def __init__(
        self,
        data_vars: DataVars,
        path_sentinel3: Path,
        aoi: Path | str | None = None,
        path_spatial_pred: Path | None = None,
        path_landsat: Path | None = None,
        timestamps: list[pd.Timestamp] | None = None,
        transform: Literal["center", "standardize"] | None = None,  # type: ignore
        max_workers: int = 1,
        logger: RichLogger | None = None,
        show_progress: bool = True,
    ) -> None:
        """
        Initialize DataWrangler instance by reading Sentinel-3, spatial predictor, AOI
        and Landsat data from issued paths `path_sentinel3`, `path_spatial_pred`,
        `aoi`, `path_landsat`, reprojecting it to Sentinel-3 coarse and fine grids,
        combining and masking it for each grid and further transforming it with the
        issued `transform`.

        Parameters
        ----------
        data_vars : DataVars
            Aliases for predictors and target and their kinds.

        path_sentinel3 : Path
            Path to the directory containing Sentinel-3 data folders. Each folder
            contains a georeferenced Sentinel-3 SLSTR Level-2 LST product file
            (https://sentiwiki.copernicus.eu/web/slstr-products#S3-SLSTR-Products-L2-LST-Products)
            and a georeferenced Sentinel-3 Synergy Level-2 product file
            (https://sentiwiki.copernicus.eu/web/synergy-products#SYNERGYProducts-L2SYNSDRprocessingS3-Synergy-Products-L2-SYN-SDR-processing).

        path_spatial_pred : Path or None, default=None
            Path to the NetCDF file with the spatial predictor data. If not set, no
            spatial predictor data is considered.

        aoi : Path or str or None, default=None
            WKT string representing the AOI, or path to its shapefile. If not set, no
            AOI is considered and no masking is applied.

        path_landsat : Path or None, default=None
            Path to the directory containing Landsat 8/9 data folders. Each folder
            contains a `LST.TIF` file for georeferenced Level-2 LST, having a resolution
            of 30 m
            (https://www.usgs.gov/centers/eros/science/usgs-eros-archive-landsat-archives-landsat-8-9-olitirs-collection-2-level-2).
            In the wrangling, such data and Sentinel-3's will be "matched" if the
            respective folders have the same name (it is implied here that the user had
            analysed the acquisitions obtained by the two platforms and set the names of
            the Landsat 8/9 data folders as the ones of Sentinel-3's (start sensing
            times) whose start sensing times and spatial extents are approximately the
            same).

        timestamps : list[pd.Timestamp] | None = None
            Start sensing times associated with each Sentinel-3 data folder of interest.
            If not issued, all Sentinel-3 data folders are considered.

        transform : {None, "center", "standardize"}, default=None
            The transform to apply on the coarse target and coarse and fine
            spatio-temporal predictors from a copy of the wrangled `data` in each
            `SingleDataWrangler` instance by using coarse data statistics. The
            transformations are set in `SingleDataWrangler's `data` with the same names
            as the original columns with the substring `"_trans"` suffixed to them. Note
            that the transformations are timestamp-specific, that is, the computed
            statistics and the applied transformations in each timestamp solely concern
            the data of that timestamp. The possible values for `transform` are:
                - `None` - not transforming the data;
                - `"center"` - subtracting the mean from the data;
                - `"standardize"` - subtracting the mean from the data and diving the
                result by the standard deviation.

        max_workers : int, default=1
            Number of simultaneous multiple processes to consider in wrangling with the
            special cases:
                - `1` or `None`: no multiprocessing is considered;
                - `-1`: all processors are used;
                - `-k`: all processors except k-1 are used.

        logger : RichLogger or None
            A rich logger for showing progress of the wrangling.

        show_progress : bool, default=True
            `True` to display the wrangling progress.
        """

        self.data_vars = data_vars
        self.path_sentinel3 = path_sentinel3
        self.path_spatial_pred = path_spatial_pred
        self.aoi = aoi
        self.path_landsat = path_landsat
        self._transform = transform
        self.max_workers = parse_n_jobs(max_workers)
        self.logger = logger
        self.show_progress = show_progress

        # Get names of Sentinel-3 data folders of interest
        foldernames_sentinel3 = (
            [
                path_sentinel3_folder.name
                for path_sentinel3_folder in path_sentinel3.iterdir()
                if path_sentinel3_folder.is_dir()
            ]
            if timestamps is None
            else [timestamp.strftime("%Y%m%dT%H%M%S") for timestamp in timestamps]
        )
        # Check if Sentinel-3 data folders exist for all issued timestamps of interest
        if timestamps is not None:
            missing_foldernames_sentinel3 = [
                foldername_sentinel3
                for foldername_sentinel3 in foldernames_sentinel3
                if not (path_sentinel3 / foldername_sentinel3).is_dir()
            ]
            if missing_foldernames_sentinel3:
                raise FileNotFoundError(
                    "The following Sentinel-3 data folders do not exist in"
                    f" {path_sentinel3}:"
                    "\n"
                    + "\n".join(
                        [
                            str(f"{missing_foldername_sentinel3!r}")
                            for missing_foldername_sentinel3 in missing_foldernames_sentinel3
                        ]
                    )
                )

        # Get start sensing times associated with the Sentinel-3 data folders of
        # interest
        self.timestamps = (
            [
                pd.Timestamp(foldername_sentinel3)
                for foldername_sentinel3 in foldernames_sentinel3
            ]
            if timestamps is None
            else timestamps
        )

        # Sort timestamps and names of the Sentinel-3 data folders in ascending order of
        # the timestamps
        self.timestamps, foldernames_sentinel3 = [
            list(timestamp__foldername_sentinel3)
            for timestamp__foldername_sentinel3 in zip(
                *sorted(zip(self.timestamps, foldernames_sentinel3))
            )
        ]

        # Get a SingleDataWrangler instance for each timestamp and perform wrangling of
        # the respective data
        if self.logger is not None:
            self.logger.info(  # type: ignore
                "Getting a SingleDataWrangler instance for each timestamp and"
                " performing wrangling of the respective data..."
            )

        pbar = (
            tqdm(
                # Prefix for the progressbar
                bar_format=f"{'':9}" + "{l_bar}{bar}{r_bar}",
                desc=f"{'':8}",
                total=len(self.timestamps),
                unit="timestamp",
                position=0,
                leave=True,  # Keep progress on the screen after completion.
                options={"console": self.logger.console},
            )
            if self.show_progress is True and self.logger is not None
            else None
        )
        self.single_data_wrangler = {}
        if self.max_workers != 1:
            with ProcessPoolExecutor(max_workers=self.max_workers) as executor:
                # List of placeholders for the eventual result of a computation
                futures = [
                    # NOTE: Using executor.submit() can be safely used as key of
                    # dictionary since executor.submit() returns a Future object
                    # (https://docs.python.org/3/library/asyncio-future.html#future-object)
                    # and all of these objects are unique and hashable.
                    executor.submit(
                        get_single_data_wrangler,
                        timestamp=timestamp,
                        data_vars=data_vars,
                        path_sentinel3=path_sentinel3 / foldername_sentinel3,
                        path_spatial_pred=path_spatial_pred,
                        aoi=aoi,
                        path_landsat=(
                            (path_landsat / foldername_sentinel3)
                            if (
                                path_landsat is not None
                                and (path_landsat / foldername_sentinel3).exists()
                            )
                            else None
                        ),
                        transform=transform,
                    )
                    for timestamp, foldername_sentinel3 in zip(
                        self.timestamps, foldernames_sentinel3
                    )
                ]

                for future in as_completed(futures):
                    # Get result of the completed future (the timestamps and
                    # the single data wranglers)
                    result = future.result()
                    timestamp = result["timestamp"]
                    single_data_wrangle = result["single_data_wrangler"]
                    # Add result to dictionary of results
                    self.single_data_wrangler[timestamp] = single_data_wrangle

                    # Update progress bar with one more count per completed process
                    if pbar is not None:
                        pbar.update()
        else:
            for timestamp, foldername_sentinel3 in zip(
                self.timestamps, foldernames_sentinel3
            ):
                # Get current single data wrangler
                timestap__single_data_wrangler = get_single_data_wrangler(
                    timestamp=timestamp,
                    data_vars=data_vars,
                    path_sentinel3=path_sentinel3 / foldername_sentinel3,
                    path_spatial_pred=path_spatial_pred,
                    aoi=aoi,
                    path_landsat=(
                        (path_landsat / foldername_sentinel3)
                        if (
                            path_landsat is not None
                            and (path_landsat / foldername_sentinel3).exists()
                        )
                        else None
                    ),
                    transform=transform,
                )
                # Set current single data wrangler
                self.single_data_wrangler[
                    timestap__single_data_wrangler["timestamp"]
                ] = timestap__single_data_wrangler["single_data_wrangler"]

                # Update progress bar with one more count per completed process
                if pbar is not None:
                    pbar.update()

        # At the end close progress bar
        if pbar is not None:
            pbar.close()

        # Infer the metadata of the wrangled data (timestamps, season and existence of
        # Landsat data)
        self.metadata = self.extract_metadata()

        # Get timestamps for which there is Landsat data available
        self.timestamps_landsat = self.metadata[self.metadata["landsat_exists"]][
            "timestamp"
        ].tolist()

    @property
    def transform(self) -> Literal["center", "standardize"] | None:
        return self._transform  # type: ignore

    @transform.setter
    def transform(self, value: Literal["center", "standardize"] | None) -> None:
        self._transform = value
        for timestamp in self.timestamps:
            self.single_data_wrangler[timestamp].transform = value

    def extract_metadata(self) -> pd.DataFrame:
        """
        Extract the metadata of the wrangled data (timestamps, season and existence of
        Landsat data).

        Returns
        -------
        metadata : pd.DataFrame
            Metadata DataFrame associated with the wrangled data having columns:
                - `"timestamp"`
                - `"season"` - season associated with timestamp;
                - `"landsat_exists"` - indicator of existence of Landsat data.
        """

        metadata = pd.DataFrame({"timestamp": self.timestamps})
        metadata["season"] = metadata["timestamp"].apply(get_season)
        metadata["landsat_exists"] = metadata["timestamp"].apply(
            lambda timestamp: (
                self.single_data_wrangler[timestamp].path_landsat is not None
            )
        )

        return metadata

    def get_metadata(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        vars: str | list[str] | None = None,
    ) -> pd.Series | pd.DataFrame:
        """
        Get values of metadata `vars` associated with the timestamps `timestamps` of the
        wrangled data.

        Note that if `timestamps` or `vars` are not issued, the returned value
        corresponds to the metadata of of all timestamps or metadata variables,
        respectively.

        Parameters
        ----------
        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the metadata. If not issued, the metadata of all
            timestamps is considered.
        vars : str or list[str] or None, default=None
            Variables of the metadata to return. If not issued, all metadata variables
            are returned.

        Returns
        -------

        metadata : pd.Series or pd.DataFrame
        Get values of metadata `vars` associated with the timestamps `timestamps` of the
        wrangled data. Note that if `timestamps` or `vars` are not issued, the returned
        value corresponds to the metadata of of all timestamps or metadata variables,
        respectively.
        """

        metadata = (
            self.metadata
            if timestamps is None
            else self.metadata[
                self.metadata["timestamp"].isin(
                    [timestamps] if isinstance(timestamps, pd.Timestamp) else timestamps
                )
            ]
        )[vars if vars is not None else self.metadata.columns]

        return metadata

    def get_coords(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
    ) -> (
        xr.core.coordinates.DatasetCoordinates  # type: ignore
        | dict[pd.Timestamp, xr.core.coordinates.DatasetCoordinates]  # type: ignore
        | dict[
            pd.Timestamp,
            dict[Literal["coarse", "fine"], xr.core.coordinates.DatasetCoordinates],  # type: ignore
        ]
    ):
        """
        Get Sentinel-3's coordinates associated with issued `timestamps` and `grid`
        alias.

        Note that if `timestamps` or `grid` alias are not issued, the returned value
        corresponds to coordinates of all timestamps or grids, respectively, keyed by
        timestamp or grid alias.

        Parameters
        ----------

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the coordinates. If not issued, the coordinates
            of all timestamps is considered.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the coordinates. If not issued, the
            coordinates of both grids are returned.


        Returns
        -------
        coords : xr.core.coordinates.DatasetCoordinates or dict[pd.Timestamp,
        xr.core.coordinates.DatasetCoordinates] or dict[pd.Timestamp, dict[{"coarse",
        "fine"}, xr.core.coordinates.DatasetCoordinates]]
            Coordinates associated with Sentinel-3's issued `timestamps` and `grid`
            alias. Note that if `timestamps` or `grid` are not issued, the returned
            value corresponds to coordinates of all timestamps or grids, respectively,
            keyed by timestamp or grid alias.
        """

        coords = {
            timestamp_: {
                grid_: self.single_data_wrangler[timestamp_].coords[grid_]
                for grid_ in ([grid] if grid is not None else self.grids)
            }
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            )
        }

        # Squeeze
        if grid is not None:
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            ):
                coords[timestamp_] = coords[timestamp_][grid]  # type: ignore
        if isinstance(timestamps, pd.Timestamp):
            coords = coords[timestamps]

        return coords

    def get_shape(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
    ) -> (
        tuple[int, int]
        | dict[pd.Timestamp, tuple[int, int]]
        | dict[pd.Timestamp, dict[Literal["coarse", "fine"], tuple[int, int]]]
    ):
        """
        Get Sentinel-3's grid shape associated with issued `timestamps` and `grid`
        alias.

        Note that if `timestamps` or `grid` are not issued, the returned value
        corresponds to shapes of all timestamps or grids, respectively, keyed by
        timestamp or grid alias.

        Parameters
        ----------

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the shapes. If not issued, the shapes of all
            timestamps is considered.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the shapes. If not issued, the shapes of
            both grids are returned.

        Returns
        -------
        shape : tuple[int, int] or dict[pd.Timestamp, tuple[int, int]] or
        dict[pd.Timestamp, dict[{"coarse", "fine"}, tuple[int, int]]]
            Grid shape associated with Sentinel-3's issued `timestamps` and `grid`
            alias. Note that if `timestamps` or `grid` are not issued, the returned
            value corresponds to shapes of all timestamps or grids, respectively, keyed
            by timestamp or grid alias.
        """

        shape = {
            timestamp_: {
                grid_: self.single_data_wrangler[timestamp_].shape[grid_]
                for grid_ in ([grid] if grid is not None else self.grids)
            }
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            )
        }

        # Squeeze
        if grid is not None:
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            ):
                shape[timestamp_] = shape[timestamp_][grid]  # type: ignore
        if isinstance(timestamps, pd.Timestamp):
            shape = shape[timestamps]

        return shape  # type: ignore

    def get_data(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        vars: str | list[str] | None = None,
        trans: bool = False,
        aggregate: bool = False,
    ) -> (
        pd.Series
        | pd.DataFrame
        | dict[pd.Timestamp, pd.Series | pd.DataFrame]
        | dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
        | dict[pd.Timestamp, dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]]
    ):
        """
        Get wrangled and, if `trans` is `True`, further transformed data `vars` for
        issued `timestamps` and `grid` alias.

        Note that if `vars` is not issued, the data of all variables is returned. Also,
        if `timestamps` or `grid` are not issued, the returned value corresponds to data
        of all timestamps or grids, respectively, keyed by timestamp or grid alias. If
        `aggregate` is `True`, the data instead of being keyed by timestamp is
        aggregated with respect to it. If the instance has no transformation (attribute
        `transform` is `None`), the untransformed data is the one considered regardless
        of the value of `trans`.

        Parameters
        ----------

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is considered.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the data. If not issued, the data of both
            grids is returned.

        vars : str or list[str] or None, default=None
            Variables of the data to return. If not issued, the data of all variables is
            returned.

        trans : bool, default=False
            Whether to get transformed data.

        aggregate: bool, default=False
            Whether to aggregate the data with respect to timestamps.

        Returns
        -------
        data : pd.Series or pd.DataFrame or dict[pd.Timestamp, pd.Series or
        pd.DataFrame] or dict[{"coarse", "fine"}, pd.Series or pd.DataFrame] or
        dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]
            Wrangled and, if `trans` is `True`, further transformed data `vars` for
            issued `timestamps` and `grid` alias. Note that if `vars` is not issued, the
            data of all variables is returned. Also, if `timestamps` or `grid` are not
            issued, the returned value corresponds to data of all timestamps or grids,
            respectively, keyed by timestamp or grid alias. If `aggregate` is `True`,
            the data instead of being keyed by timestamp is aggregated with respect to
            it. If the instance has no transformation (attribute `transform` is `None`),
            the untransformed data is the one considered regardless of the value of
            `trans`.
        """

        data = {
            timestamp_: {
                grid_: self.single_data_wrangler[timestamp_].get_data(
                    grid=grid_,  # type: ignore
                    vars=vars,
                    trans=trans,
                )
                for grid_ in ([grid] if grid is not None else self.grids)
            }
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            )
        }

        # If wanted, aggregate (concatenate) the data with respect to timestamps
        if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
            data = {
                grid_: pd.concat(
                    [
                        data[timestamp_][grid_]
                        for timestamp_ in (
                            timestamps
                            if isinstance(timestamps, list)
                            else self.timestamps
                        )
                    ],  # type: ignore
                    ignore_index=True,
                )
                for grid_ in ([grid] if grid is not None else self.grids)
            }

            # NOTE: when concatenating the data, categorical columns may cease to be
            # categorical, hence the necessity of re-setting their type after
            # concatenation.
            for grid_ in [grid] if grid is not None else self.grids:
                if not isinstance(vars, str):
                    X_cat = [
                        var
                        for var in self.data_vars.X_cat
                        if var in data[grid_].columns
                    ]
                    data[grid_][X_cat] = data[grid_][X_cat].astype("category")
                else:
                    if vars in self.data_vars.X_cat:
                        data[grid_] = data[grid_].astype("category")

        # Squeeze
        if grid is not None:
            if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
                data = data[grid]
            else:
                for timestamp_ in (
                    [timestamps]
                    if isinstance(timestamps, pd.Timestamp)
                    else timestamps
                    if isinstance(timestamps, list)
                    else self.timestamps
                ):
                    data[timestamp_] = data[timestamp_][grid]  # type: ignore
        if isinstance(timestamps, pd.Timestamp):
            data = data[timestamps]  # type: ignore

        return data  # type: ignore

    def get_data_X_and_mask(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        trans: bool = False,
        aggregate: bool = False,
    ) -> (
        pd.DataFrame
        | dict[pd.Timestamp, pd.DataFrame]
        | dict[Literal["coarse", "fine"], pd.DataFrame]
        | dict[pd.Timestamp, dict[Literal["coarse", "fine"], pd.DataFrame]]
    ):
        """
        Get wrangled and, if `trans` is `True`, further transformed predictor and AOI
        mask data for issued `timestamps` and `grid` alias.

        Note that if `timestamps` or `grid` are not issued, the returned value
        corresponds to data of all timestamps or grids, respectively, keyed by timestamp
        or grid alias. If `aggregate` is `True`, the data instead of being keyed by
        timestamp is aggregated with respect to it. If the instance has no
        transformation (attribute `transform` is `None`), the untransformed data is the
        one considered regardless of the value of `trans`.

        Parameters
        ----------

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is considered.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the data. If not issued, the data of both
            grids is returned.

        trans : bool, default=False
            Whether to get transformed data.


        aggregate: bool, default=False
            Whether to aggregate the data with respect to timestamps.

        Returns
        -------
        data_X_and_mask : pd.DataFrame or dict[pd.Timestamp, pd.DataFrame] or
        dict[{"coarse", "fine"}, pd.DataFrame] or dict[pd.Timestamp, dict[{"coarse",
        "fine"}, pd.DataFrame]]
            Wrangled and, if `trans` is `True`, further transformed predictor and AOI
            mask data for issued `timestamps` and `grid` alias. Note that if
            `timestamps` or `grid` are not issued, the returned value corresponds to
            data of all timestamps or grids, respectively, keyed by timestamp or grid
            alias. If `aggregate` is `True`, the data instead of being keyed by
            timestamp is aggregated with respect to it. If the instance has no
            transformation (attribute `transform` is `None`), the untransformed data is
            the one considered regardless of the value of `trans`.
        """

        return self.get_data(
            timestamps=timestamps,
            grid=grid,
            vars=self.data_vars.X + (["aoi"] if self.aoi is not None else []),  # type: ignore
            trans=trans,
            aggregate=aggregate,
        )  # type: ignore

    def get_data_y(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        trans: bool = False,
        aggregate: bool = False,
    ) -> (
        pd.Series
        | dict[pd.Timestamp, pd.Series]
        | dict[Literal["coarse", "fine"], pd.Series]
        | dict[pd.Timestamp, dict[Literal["coarse", "fine"], pd.Series]]
    ):
        """
        Get wrangled and, if `trans` is `True`, further transformed target data for
        issued issued `timestamps` and `grid` alias.

        Note that if `timestamps` or `grid` are not issued, the returned value
        corresponds to data of all timestamps or grids, respectively, keyed by timestamp
        or grid alias. If `aggregate` is `True`, the data instead of being keyed by
        timestamp is aggregated with respect to it. If the instance has no
        transformation (attribute `transform` is `None`), the untransformed data is the
        one considered regardless of the value of `trans`.

        Parameters
        ----------

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is considered.

        grid : {"coarse", "fine", None}, default="coarse"
            Alias of the grid associated with the data. If not issued, the data of both
            grids is returned.

        trans : bool, default=False
            Whether to get transformed data.

        aggregate: bool, default=False
            Whether to aggregate the data with respect to timestamps.

        Returns
        -------
        data_y : pd.Series or dict[pd.Timestamp, pd.Series] or dict[{"coarse", "fine"},
        pd.Series] or dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series]]
            Wrangled and, if `trans` is `True`, further transformed target data for
            issued `timestamps` and `grid` alias. Note that if `timestamps` or `grid`
            are not issued, the returned value corresponds to data of all timestamps or
            grids, respectively, keyed by timestamp or grid alias. If `aggregate` is
            `True`, the data instead of being keyed by timestamp is aggregated with
            respect to it. If the instance has no transformation (attribute `transform`
            is `None`), the untransformed data is the one considered regardless of the
            value of `trans`.
        """

        return self.get_data(
            timestamps=timestamps,
            grid=grid,
            vars=self.data_vars.y,
            trans=trans,
            aggregate=aggregate,
        )  # type: ignore

    def set_data(
        self,
        values: (
            pd.Series
            | pd.DataFrame
            | dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
            | dict[pd.Timestamp, pd.Series | pd.DataFrame]
            | dict[
                pd.Timestamp, dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
            ]
        ),
        vars: str | list[str] | None = None,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        trans: bool = False,
    ) -> None:
        """
        Set wrangled and, if `trans` is `True`, further transformed data `vars` of
        issued `timestamps` and `grid` alias to `values`.

        Note that `vars` may correspond to new variables. If not defined, `vars` is set
        to all variables of the data. If `timestamps` or `grid` is not issued, the data
        of all timestamps or grids, respectively, is set. If there is no transform in
        the instance (attribute `transform` is `None`), the untransformed data is the
        one considered regardless of the value of `trans`.

        Parameters
        ----------

        values : dict[pd.Timestamp, pd.Series or pd.DataFrame] or dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]
            Values to set.

        vars : str or list[str] or None, default=None
            Variables of `single_data_wrangler`s data to set. Note that `vars` may
            correspond to new variables. If not defined, `vars` is set as all variables
            of the data.

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is set.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the data. If not issued, the data of both
            grids is set.

        trans : bool, default=False
            Whether to set transformed data.
        """

        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        ):
            self.single_data_wrangler[timestamp_].set_data(
                values=(
                    values
                    if isinstance(timestamps, pd.Timestamp)
                    else values[timestamp_]  # type: ignore
                ),
                vars=vars,
                grid=grid,
                trans=trans,
            )

    def apply(
        self,
        vars: str | list[str] | None = None,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        trans: bool = False,
        aggregate: bool = False,
        **pandas_kwargs: Any,
    ) -> (
        pd.Series
        | pd.DataFrame
        | dict[pd.Timestamp, pd.Series | pd.DataFrame]
        | dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
        | dict[
            pd.Timestamp,
            dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame],
        ]
    ):
        """
        Use `pandas`' `apply` method (of arguments `pandas_kwargs`) on wrangled and, if
        `trans` is `True`, further transformed data `vars` of issued `timestamps` and
        `grid` alias.

        Note that if not defined, `vars` is set to all variables of the data. If
        `timestamps` or `grid` are not issued, the data of all timestamps or grids are
        used and the returned value is keyed by timestamp or grid alias, respectively.
        If `aggregate` is `True`, the data instead of being keyed by timestamp is
        aggregated with respect to it. If the instance has no transformation (attribute
        `transform` is `None`), the untransformed data is the one considered regardless
        of the value of `trans`.

        Parameters
        ----------

        vars : str or list[str] or None, default=None
            Variables of `single_data_wrangler`s data to use in `apply`. If not issued,
            the whole data is used.

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is used.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the data. If not issued, the data of both
            grids is used.

        trans : bool, default=False
            Whether to consider transformed data.

        aggregate: bool, default=False
            Whether to aggregate the result with respect to timestamps.

        pandas_kwargs :
            Keyword arguments of `pandas`' `apply` method.

        Returns
        -------

        pd.Series or pd.DataFrame or dict[pd.Timestamp, pd.Series or pd.DataFrame] or
        dict[{"coarse", "fine"}, pd.Series or pd.DataFrame] or dict[pd.Timestamp,
        dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]
            Result of `pandas`' `apply` method (of arguments `pandas_kwargs`) on
            wrangled and, if `trans` is `True`, further transformed data `vars` of
            issued `timestamps` and `grid` alias. Note that if not defined, `vars` is
            set to all variables of the data. If `timestamps` or `grid` are not issued,
            the data of all timestamps or grids are used and the returned value is keyed
            by timestamp or grid alias, respectively. If `aggregate` is `True`, the data
            instead of being keyed by timestamp is aggregated with respect to it. If the
            instance has no transformation (attribute `transform` is `None`), the
            untransformed data is the one considered regardless of the value of `trans`.
        """

        result = {
            timestamp_: {
                grid_: self.get_data(
                    timestamps=timestamp_,
                    grid=grid_,  # type: ignore
                    vars=vars,
                    trans=trans,
                    aggregate=False,
                ).apply(  # type: ignore
                    **pandas_kwargs
                )
                for grid_ in ([grid] if grid is not None else self.grids)
            }
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            )
        }

        # If wanted, aggregate (concatenate) the result with respect to timestamps
        if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
            result = {
                grid_: pd.concat(
                    [
                        result[timestamp_][grid_]
                        for timestamp_ in (
                            timestamps
                            if isinstance(timestamps, list)
                            else self.timestamps
                        )
                    ],
                    ignore_index=True,
                )
                for grid_ in ([grid] if grid is not None else self.grids)
            }

        # Squeeze
        if grid is not None:
            if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
                result = result[grid]
            else:
                for timestamp_ in (
                    [timestamps]
                    if isinstance(timestamps, pd.Timestamp)
                    else timestamps
                    if isinstance(timestamps, list)
                    else self.timestamps
                ):
                    result[timestamp_] = result[timestamp_][grid]  # type: ignore
        if isinstance(timestamps, pd.Timestamp):
            result = result[timestamps]  # type: ignore

        return result  # type: ignore

    def apply_set_data(
        self,
        vars_apply: str | list[str] | None = None,
        vars_set: str | list[str] | None = None,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        trans_apply: bool = False,
        trans_set: bool | None = None,
        **pandas_kwargs: Any,
    ) -> None:
        """
        Use `pandas`' `apply` method (of arguments `pandas_kwargs`) on wrangled and, if
        `trans_apply` is `True`, further transformed data `vars_apply` of issued
        `timestamps` and `grid` alias and set the result to `vars_set` as transformed
        data if `trans_set` is `True` or as untransformed data if otherwise.

        If `vars_apply` is not defined, it is set to all variables of the data. If
        `vars_set` or `trans_set` are not defined, they are set to `vars_apply` or
        `trans_apply`, respectively. If `timestamps` or `grid` are not issued, the data
        of all timestamps or grids (coarse and fine), respectively, is used and set. If
        there is no transform in the instance (attribute `transform` is `None`), the
        untransformed data is the one considered regardless of the values of
        `trans_apply` and `trans_set`.

        Parameters
        ----------

        vars_apply : str or list[str] or None, default=None
            Variables of `single_data_wrangler`s data to use in `apply`. If not issued,
            the whole data is used.

        vars_set : str or list[str] or None, default=None
            Variables of `single_data_wrangler`s data to use in `set_data`. If not
            issued, it is set to `vars_apply`.

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is used.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the data. If not issued, the data of both
            grids is used.

        trans_apply : bool, default=False
            Whether to consider transformed data in `apply`.

        trans_set : bool or None, default=None
            Whether to consider transformed data in `set_data`. If not issued, it is set
            to `trans_apply`.

        pandas_kwargs :
            Keyword arguments of `pandas`' `apply` method.
        """

        if vars_set is None:
            vars_set = vars_apply

        if trans_set is None:
            trans_set = trans_apply

        self.set_data(
            values=self.apply(
                vars=vars_apply,
                timestamps=timestamps,
                grid=grid,
                trans=trans_apply,
                aggregate=False,
                **pandas_kwargs,
            ),  # type: ignore
            vars=vars_set,
            timestamps=timestamps,
            grid=grid,
            trans=trans_set,
        )

    def dropna(
        self,
        timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
        grid: Literal["coarse", "fine"] | None = None,
        **pandas_kwargs: Any,
    ) -> None:
        """
        Use `pandas`' `dropna` method (of arguments `pandas_kwargs`) on wrangled
        untransformed and transformed data associated with the issued `timestamps` and
        `grid` alias.

        Note that if `timestamps` or `grid` are not issued, the data of all timestamps
        or grids is considered, respectively.

        Parameters
        ----------

        timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
            Timestamps associated with the data. If not issued, the data of all
            timestamps is considered.

        grid : {"coarse", "fine", None}, default=None
            Alias of the grid associated with the data. If not issued, the data of both
            grids is considered.

        pandas_kwargs :
            Keyword arguments of `pandas`' `dropna` method.
        """

        # Remove pandas `dropna` argument `inplace` if it exists since a set after drop
        # will be enforced (which is equivalent to `inplace=True`).
        pandas_kwargs.pop("inplace", None)
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        ):
            for grid_ in [grid] if grid is not None else self.grids:
                self.single_data_wrangler[timestamp_].data[grid_] = (
                    self.single_data_wrangler[timestamp_]
                    .data[grid_]
                    .dropna(
                        **pandas_kwargs,
                    )
                )

    def save(self, path: Path) -> None:
        """
        Write the instance to `path` with `joblib`.

        Parameters
        ----------
        path : Path
            Path to write the instance to.
        """

        joblib.dump(value=self, filename=path)

aoi instance-attribute

aoi = aoi

data_vars instance-attribute

data_vars = data_vars

grids class-attribute

grids: tuple[str, ...] = ('coarse', 'fine')

logger instance-attribute

logger = logger

max_workers instance-attribute

max_workers = parse_n_jobs(max_workers)

metadata instance-attribute

metadata = self.extract_metadata()

path_landsat instance-attribute

path_landsat = path_landsat

path_sentinel3 instance-attribute

path_sentinel3 = path_sentinel3

path_spatial_pred instance-attribute

path_spatial_pred = path_spatial_pred

show_progress instance-attribute

show_progress = show_progress

single_data_wrangler instance-attribute

single_data_wrangler = {}

timestamps instance-attribute

timestamps = (
    [
        pd.Timestamp(foldername_sentinel3)
        for foldername_sentinel3 in foldernames_sentinel3
    ]
    if timestamps is None
    else timestamps
)

timestamps_landsat instance-attribute

timestamps_landsat = self.metadata[self.metadata["landsat_exists"]][
    "timestamp"
].tolist()

transform property writable

transform: Literal['center', 'standardize'] | None

__init__

__init__(
    data_vars: DataVars,
    path_sentinel3: Path,
    aoi: Path | str | None = None,
    path_spatial_pred: Path | None = None,
    path_landsat: Path | None = None,
    timestamps: list[Timestamp] | None = None,
    transform: Literal["center", "standardize"] | None = None,
    max_workers: int = 1,
    logger: RichLogger | None = None,
    show_progress: bool = True,
) -> None

aoi, path_landsat, reprojecting it to Sentinel-3 coarse and fine grids, combining and masking it for each grid and further transforming it with the issued transform.

Parameters:

Name Type Description Default
data_vars DataVars

Aliases for predictors and target and their kinds.

required
path_sentinel3 Path

Path to the directory containing Sentinel-3 data folders. Each folder contains a georeferenced Sentinel-3 SLSTR Level-2 LST product file (https://sentiwiki.copernicus.eu/web/slstr-products#S3-SLSTR-Products-L2-LST-Products) and a georeferenced Sentinel-3 Synergy Level-2 product file (https://sentiwiki.copernicus.eu/web/synergy-products#SYNERGYProducts-L2SYNSDRprocessingS3-Synergy-Products-L2-SYN-SDR-processing).

required
path_spatial_pred Path or None

Path to the NetCDF file with the spatial predictor data. If not set, no spatial predictor data is considered.

None
aoi Path or str or None

WKT string representing the AOI, or path to its shapefile. If not set, no AOI is considered and no masking is applied.

None
path_landsat Path or None

Path to the directory containing Landsat 8/9 data folders. Each folder contains a LST.TIF file for georeferenced Level-2 LST, having a resolution of 30 m (https://www.usgs.gov/centers/eros/science/usgs-eros-archive-landsat-archives-landsat-8-9-olitirs-collection-2-level-2). In the wrangling, such data and Sentinel-3's will be "matched" if the respective folders have the same name (it is implied here that the user had analysed the acquisitions obtained by the two platforms and set the names of the Landsat 8/9 data folders as the ones of Sentinel-3's (start sensing times) whose start sensing times and spatial extents are approximately the same).

None
timestamps list[pd.Timestamp] | None = None

Start sensing times associated with each Sentinel-3 data folder of interest. If not issued, all Sentinel-3 data folders are considered.

None
transform (None, center, standardize)

The transform to apply on the coarse target and coarse and fine spatio-temporal predictors from a copy of the wrangled data in each SingleDataWrangler instance by using coarse data statistics. The transformations are set in SingleDataWrangler'sdatawith the same names as the original columns with the substring"_trans"suffixed to them. Note that the transformations are timestamp-specific, that is, the computed statistics and the applied transformations in each timestamp solely concern the data of that timestamp. The possible values fortransformare: -None- not transforming the data; -"center"- subtracting the mean from the data; -"standardize"` - subtracting the mean from the data and diving the result by the standard deviation.

None
max_workers int

Number of simultaneous multiple processes to consider in wrangling with the special cases: - 1 or None: no multiprocessing is considered; - -1: all processors are used; - -k: all processors except k-1 are used.

1
logger RichLogger or None

A rich logger for showing progress of the wrangling.

None
show_progress bool

True to display the wrangling progress.

True
Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def __init__(
    self,
    data_vars: DataVars,
    path_sentinel3: Path,
    aoi: Path | str | None = None,
    path_spatial_pred: Path | None = None,
    path_landsat: Path | None = None,
    timestamps: list[pd.Timestamp] | None = None,
    transform: Literal["center", "standardize"] | None = None,  # type: ignore
    max_workers: int = 1,
    logger: RichLogger | None = None,
    show_progress: bool = True,
) -> None:
    """
    Initialize DataWrangler instance by reading Sentinel-3, spatial predictor, AOI
    and Landsat data from issued paths `path_sentinel3`, `path_spatial_pred`,
    `aoi`, `path_landsat`, reprojecting it to Sentinel-3 coarse and fine grids,
    combining and masking it for each grid and further transforming it with the
    issued `transform`.

    Parameters
    ----------
    data_vars : DataVars
        Aliases for predictors and target and their kinds.

    path_sentinel3 : Path
        Path to the directory containing Sentinel-3 data folders. Each folder
        contains a georeferenced Sentinel-3 SLSTR Level-2 LST product file
        (https://sentiwiki.copernicus.eu/web/slstr-products#S3-SLSTR-Products-L2-LST-Products)
        and a georeferenced Sentinel-3 Synergy Level-2 product file
        (https://sentiwiki.copernicus.eu/web/synergy-products#SYNERGYProducts-L2SYNSDRprocessingS3-Synergy-Products-L2-SYN-SDR-processing).

    path_spatial_pred : Path or None, default=None
        Path to the NetCDF file with the spatial predictor data. If not set, no
        spatial predictor data is considered.

    aoi : Path or str or None, default=None
        WKT string representing the AOI, or path to its shapefile. If not set, no
        AOI is considered and no masking is applied.

    path_landsat : Path or None, default=None
        Path to the directory containing Landsat 8/9 data folders. Each folder
        contains a `LST.TIF` file for georeferenced Level-2 LST, having a resolution
        of 30 m
        (https://www.usgs.gov/centers/eros/science/usgs-eros-archive-landsat-archives-landsat-8-9-olitirs-collection-2-level-2).
        In the wrangling, such data and Sentinel-3's will be "matched" if the
        respective folders have the same name (it is implied here that the user had
        analysed the acquisitions obtained by the two platforms and set the names of
        the Landsat 8/9 data folders as the ones of Sentinel-3's (start sensing
        times) whose start sensing times and spatial extents are approximately the
        same).

    timestamps : list[pd.Timestamp] | None = None
        Start sensing times associated with each Sentinel-3 data folder of interest.
        If not issued, all Sentinel-3 data folders are considered.

    transform : {None, "center", "standardize"}, default=None
        The transform to apply on the coarse target and coarse and fine
        spatio-temporal predictors from a copy of the wrangled `data` in each
        `SingleDataWrangler` instance by using coarse data statistics. The
        transformations are set in `SingleDataWrangler's `data` with the same names
        as the original columns with the substring `"_trans"` suffixed to them. Note
        that the transformations are timestamp-specific, that is, the computed
        statistics and the applied transformations in each timestamp solely concern
        the data of that timestamp. The possible values for `transform` are:
            - `None` - not transforming the data;
            - `"center"` - subtracting the mean from the data;
            - `"standardize"` - subtracting the mean from the data and diving the
            result by the standard deviation.

    max_workers : int, default=1
        Number of simultaneous multiple processes to consider in wrangling with the
        special cases:
            - `1` or `None`: no multiprocessing is considered;
            - `-1`: all processors are used;
            - `-k`: all processors except k-1 are used.

    logger : RichLogger or None
        A rich logger for showing progress of the wrangling.

    show_progress : bool, default=True
        `True` to display the wrangling progress.
    """

    self.data_vars = data_vars
    self.path_sentinel3 = path_sentinel3
    self.path_spatial_pred = path_spatial_pred
    self.aoi = aoi
    self.path_landsat = path_landsat
    self._transform = transform
    self.max_workers = parse_n_jobs(max_workers)
    self.logger = logger
    self.show_progress = show_progress

    # Get names of Sentinel-3 data folders of interest
    foldernames_sentinel3 = (
        [
            path_sentinel3_folder.name
            for path_sentinel3_folder in path_sentinel3.iterdir()
            if path_sentinel3_folder.is_dir()
        ]
        if timestamps is None
        else [timestamp.strftime("%Y%m%dT%H%M%S") for timestamp in timestamps]
    )
    # Check if Sentinel-3 data folders exist for all issued timestamps of interest
    if timestamps is not None:
        missing_foldernames_sentinel3 = [
            foldername_sentinel3
            for foldername_sentinel3 in foldernames_sentinel3
            if not (path_sentinel3 / foldername_sentinel3).is_dir()
        ]
        if missing_foldernames_sentinel3:
            raise FileNotFoundError(
                "The following Sentinel-3 data folders do not exist in"
                f" {path_sentinel3}:"
                "\n"
                + "\n".join(
                    [
                        str(f"{missing_foldername_sentinel3!r}")
                        for missing_foldername_sentinel3 in missing_foldernames_sentinel3
                    ]
                )
            )

    # Get start sensing times associated with the Sentinel-3 data folders of
    # interest
    self.timestamps = (
        [
            pd.Timestamp(foldername_sentinel3)
            for foldername_sentinel3 in foldernames_sentinel3
        ]
        if timestamps is None
        else timestamps
    )

    # Sort timestamps and names of the Sentinel-3 data folders in ascending order of
    # the timestamps
    self.timestamps, foldernames_sentinel3 = [
        list(timestamp__foldername_sentinel3)
        for timestamp__foldername_sentinel3 in zip(
            *sorted(zip(self.timestamps, foldernames_sentinel3))
        )
    ]

    # Get a SingleDataWrangler instance for each timestamp and perform wrangling of
    # the respective data
    if self.logger is not None:
        self.logger.info(  # type: ignore
            "Getting a SingleDataWrangler instance for each timestamp and"
            " performing wrangling of the respective data..."
        )

    pbar = (
        tqdm(
            # Prefix for the progressbar
            bar_format=f"{'':9}" + "{l_bar}{bar}{r_bar}",
            desc=f"{'':8}",
            total=len(self.timestamps),
            unit="timestamp",
            position=0,
            leave=True,  # Keep progress on the screen after completion.
            options={"console": self.logger.console},
        )
        if self.show_progress is True and self.logger is not None
        else None
    )
    self.single_data_wrangler = {}
    if self.max_workers != 1:
        with ProcessPoolExecutor(max_workers=self.max_workers) as executor:
            # List of placeholders for the eventual result of a computation
            futures = [
                # NOTE: Using executor.submit() can be safely used as key of
                # dictionary since executor.submit() returns a Future object
                # (https://docs.python.org/3/library/asyncio-future.html#future-object)
                # and all of these objects are unique and hashable.
                executor.submit(
                    get_single_data_wrangler,
                    timestamp=timestamp,
                    data_vars=data_vars,
                    path_sentinel3=path_sentinel3 / foldername_sentinel3,
                    path_spatial_pred=path_spatial_pred,
                    aoi=aoi,
                    path_landsat=(
                        (path_landsat / foldername_sentinel3)
                        if (
                            path_landsat is not None
                            and (path_landsat / foldername_sentinel3).exists()
                        )
                        else None
                    ),
                    transform=transform,
                )
                for timestamp, foldername_sentinel3 in zip(
                    self.timestamps, foldernames_sentinel3
                )
            ]

            for future in as_completed(futures):
                # Get result of the completed future (the timestamps and
                # the single data wranglers)
                result = future.result()
                timestamp = result["timestamp"]
                single_data_wrangle = result["single_data_wrangler"]
                # Add result to dictionary of results
                self.single_data_wrangler[timestamp] = single_data_wrangle

                # Update progress bar with one more count per completed process
                if pbar is not None:
                    pbar.update()
    else:
        for timestamp, foldername_sentinel3 in zip(
            self.timestamps, foldernames_sentinel3
        ):
            # Get current single data wrangler
            timestap__single_data_wrangler = get_single_data_wrangler(
                timestamp=timestamp,
                data_vars=data_vars,
                path_sentinel3=path_sentinel3 / foldername_sentinel3,
                path_spatial_pred=path_spatial_pred,
                aoi=aoi,
                path_landsat=(
                    (path_landsat / foldername_sentinel3)
                    if (
                        path_landsat is not None
                        and (path_landsat / foldername_sentinel3).exists()
                    )
                    else None
                ),
                transform=transform,
            )
            # Set current single data wrangler
            self.single_data_wrangler[
                timestap__single_data_wrangler["timestamp"]
            ] = timestap__single_data_wrangler["single_data_wrangler"]

            # Update progress bar with one more count per completed process
            if pbar is not None:
                pbar.update()

    # At the end close progress bar
    if pbar is not None:
        pbar.close()

    # Infer the metadata of the wrangled data (timestamps, season and existence of
    # Landsat data)
    self.metadata = self.extract_metadata()

    # Get timestamps for which there is Landsat data available
    self.timestamps_landsat = self.metadata[self.metadata["landsat_exists"]][
        "timestamp"
    ].tolist()

apply

apply(
    vars: str | list[str] | None = None,
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
    aggregate: bool = False,
    **pandas_kwargs: Any,
) -> (
    Series
    | DataFrame
    | dict[Timestamp, Series | DataFrame]
    | dict[Literal["coarse", "fine"], Series | DataFrame]
    | dict[Timestamp, dict[Literal["coarse", "fine"], Series | DataFrame]]
)

Use pandas' apply method (of arguments pandas_kwargs) on wrangled and, if trans is True, further transformed data vars of issued timestamps and grid alias.

Note that if not defined, vars is set to all variables of the data. If timestamps or grid are not issued, the data of all timestamps or grids are used and the returned value is keyed by timestamp or grid alias, respectively. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Parameters:

Name Type Description Default
vars str or list[str] or None

Variables of single_data_wranglers data to use in apply. If not issued, the whole data is used.

None
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is used.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is used.

"coarse"
trans bool

Whether to consider transformed data.

False
aggregate bool

Whether to aggregate the result with respect to timestamps.

False
pandas_kwargs Any

Keyword arguments of pandas' apply method.

{}

Returns:

Type Description
pd.Series or pd.DataFrame or dict[pd.Timestamp, pd.Series or pd.DataFrame] or
dict[{"coarse", "fine"}, pd.Series or pd.DataFrame] or dict[pd.Timestamp,
dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]

Result of pandas' apply method (of arguments pandas_kwargs) on wrangled and, if trans is True, further transformed data vars of issued timestamps and grid alias. Note that if not defined, vars is set to all variables of the data. If timestamps or grid are not issued, the data of all timestamps or grids are used and the returned value is keyed by timestamp or grid alias, respectively. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def apply(
    self,
    vars: str | list[str] | None = None,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
    aggregate: bool = False,
    **pandas_kwargs: Any,
) -> (
    pd.Series
    | pd.DataFrame
    | dict[pd.Timestamp, pd.Series | pd.DataFrame]
    | dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
    | dict[
        pd.Timestamp,
        dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame],
    ]
):
    """
    Use `pandas`' `apply` method (of arguments `pandas_kwargs`) on wrangled and, if
    `trans` is `True`, further transformed data `vars` of issued `timestamps` and
    `grid` alias.

    Note that if not defined, `vars` is set to all variables of the data. If
    `timestamps` or `grid` are not issued, the data of all timestamps or grids are
    used and the returned value is keyed by timestamp or grid alias, respectively.
    If `aggregate` is `True`, the data instead of being keyed by timestamp is
    aggregated with respect to it. If the instance has no transformation (attribute
    `transform` is `None`), the untransformed data is the one considered regardless
    of the value of `trans`.

    Parameters
    ----------

    vars : str or list[str] or None, default=None
        Variables of `single_data_wrangler`s data to use in `apply`. If not issued,
        the whole data is used.

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is used.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the data. If not issued, the data of both
        grids is used.

    trans : bool, default=False
        Whether to consider transformed data.

    aggregate: bool, default=False
        Whether to aggregate the result with respect to timestamps.

    pandas_kwargs :
        Keyword arguments of `pandas`' `apply` method.

    Returns
    -------

    pd.Series or pd.DataFrame or dict[pd.Timestamp, pd.Series or pd.DataFrame] or
    dict[{"coarse", "fine"}, pd.Series or pd.DataFrame] or dict[pd.Timestamp,
    dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]
        Result of `pandas`' `apply` method (of arguments `pandas_kwargs`) on
        wrangled and, if `trans` is `True`, further transformed data `vars` of
        issued `timestamps` and `grid` alias. Note that if not defined, `vars` is
        set to all variables of the data. If `timestamps` or `grid` are not issued,
        the data of all timestamps or grids are used and the returned value is keyed
        by timestamp or grid alias, respectively. If `aggregate` is `True`, the data
        instead of being keyed by timestamp is aggregated with respect to it. If the
        instance has no transformation (attribute `transform` is `None`), the
        untransformed data is the one considered regardless of the value of `trans`.
    """

    result = {
        timestamp_: {
            grid_: self.get_data(
                timestamps=timestamp_,
                grid=grid_,  # type: ignore
                vars=vars,
                trans=trans,
                aggregate=False,
            ).apply(  # type: ignore
                **pandas_kwargs
            )
            for grid_ in ([grid] if grid is not None else self.grids)
        }
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        )
    }

    # If wanted, aggregate (concatenate) the result with respect to timestamps
    if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
        result = {
            grid_: pd.concat(
                [
                    result[timestamp_][grid_]
                    for timestamp_ in (
                        timestamps
                        if isinstance(timestamps, list)
                        else self.timestamps
                    )
                ],
                ignore_index=True,
            )
            for grid_ in ([grid] if grid is not None else self.grids)
        }

    # Squeeze
    if grid is not None:
        if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
            result = result[grid]
        else:
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            ):
                result[timestamp_] = result[timestamp_][grid]  # type: ignore
    if isinstance(timestamps, pd.Timestamp):
        result = result[timestamps]  # type: ignore

    return result  # type: ignore

apply_set_data

apply_set_data(
    vars_apply: str | list[str] | None = None,
    vars_set: str | list[str] | None = None,
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans_apply: bool = False,
    trans_set: bool | None = None,
    **pandas_kwargs: Any,
) -> None

Use pandas' apply method (of arguments pandas_kwargs) on wrangled and, if trans_apply is True, further transformed data vars_apply of issued timestamps and grid alias and set the result to vars_set as transformed data if trans_set is True or as untransformed data if otherwise.

If vars_apply is not defined, it is set to all variables of the data. If vars_set or trans_set are not defined, they are set to vars_apply or trans_apply, respectively. If timestamps or grid are not issued, the data of all timestamps or grids (coarse and fine), respectively, is used and set. If there is no transform in the instance (attribute transform is None), the untransformed data is the one considered regardless of the values of trans_apply and trans_set.

Parameters:

Name Type Description Default
vars_apply str or list[str] or None

Variables of single_data_wranglers data to use in apply. If not issued, the whole data is used.

None
vars_set str or list[str] or None

Variables of single_data_wranglers data to use in set_data. If not issued, it is set to vars_apply.

None
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is used.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is used.

"coarse"
trans_apply bool

Whether to consider transformed data in apply.

False
trans_set bool or None

Whether to consider transformed data in set_data. If not issued, it is set to trans_apply.

None
pandas_kwargs Any

Keyword arguments of pandas' apply method.

{}
Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def apply_set_data(
    self,
    vars_apply: str | list[str] | None = None,
    vars_set: str | list[str] | None = None,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans_apply: bool = False,
    trans_set: bool | None = None,
    **pandas_kwargs: Any,
) -> None:
    """
    Use `pandas`' `apply` method (of arguments `pandas_kwargs`) on wrangled and, if
    `trans_apply` is `True`, further transformed data `vars_apply` of issued
    `timestamps` and `grid` alias and set the result to `vars_set` as transformed
    data if `trans_set` is `True` or as untransformed data if otherwise.

    If `vars_apply` is not defined, it is set to all variables of the data. If
    `vars_set` or `trans_set` are not defined, they are set to `vars_apply` or
    `trans_apply`, respectively. If `timestamps` or `grid` are not issued, the data
    of all timestamps or grids (coarse and fine), respectively, is used and set. If
    there is no transform in the instance (attribute `transform` is `None`), the
    untransformed data is the one considered regardless of the values of
    `trans_apply` and `trans_set`.

    Parameters
    ----------

    vars_apply : str or list[str] or None, default=None
        Variables of `single_data_wrangler`s data to use in `apply`. If not issued,
        the whole data is used.

    vars_set : str or list[str] or None, default=None
        Variables of `single_data_wrangler`s data to use in `set_data`. If not
        issued, it is set to `vars_apply`.

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is used.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the data. If not issued, the data of both
        grids is used.

    trans_apply : bool, default=False
        Whether to consider transformed data in `apply`.

    trans_set : bool or None, default=None
        Whether to consider transformed data in `set_data`. If not issued, it is set
        to `trans_apply`.

    pandas_kwargs :
        Keyword arguments of `pandas`' `apply` method.
    """

    if vars_set is None:
        vars_set = vars_apply

    if trans_set is None:
        trans_set = trans_apply

    self.set_data(
        values=self.apply(
            vars=vars_apply,
            timestamps=timestamps,
            grid=grid,
            trans=trans_apply,
            aggregate=False,
            **pandas_kwargs,
        ),  # type: ignore
        vars=vars_set,
        timestamps=timestamps,
        grid=grid,
        trans=trans_set,
    )

dropna

dropna(
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    **pandas_kwargs: Any,
) -> None

Use pandas' dropna method (of arguments pandas_kwargs) on wrangled untransformed and transformed data associated with the issued timestamps and grid alias.

Note that if timestamps or grid are not issued, the data of all timestamps or grids is considered, respectively.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is considered.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is considered.

"coarse"
pandas_kwargs Any

Keyword arguments of pandas' dropna method.

{}
Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def dropna(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    **pandas_kwargs: Any,
) -> None:
    """
    Use `pandas`' `dropna` method (of arguments `pandas_kwargs`) on wrangled
    untransformed and transformed data associated with the issued `timestamps` and
    `grid` alias.

    Note that if `timestamps` or `grid` are not issued, the data of all timestamps
    or grids is considered, respectively.

    Parameters
    ----------

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is considered.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the data. If not issued, the data of both
        grids is considered.

    pandas_kwargs :
        Keyword arguments of `pandas`' `dropna` method.
    """

    # Remove pandas `dropna` argument `inplace` if it exists since a set after drop
    # will be enforced (which is equivalent to `inplace=True`).
    pandas_kwargs.pop("inplace", None)
    for timestamp_ in (
        [timestamps]
        if isinstance(timestamps, pd.Timestamp)
        else timestamps
        if isinstance(timestamps, list)
        else self.timestamps
    ):
        for grid_ in [grid] if grid is not None else self.grids:
            self.single_data_wrangler[timestamp_].data[grid_] = (
                self.single_data_wrangler[timestamp_]
                .data[grid_]
                .dropna(
                    **pandas_kwargs,
                )
            )

extract_metadata

extract_metadata() -> DataFrame

Extract the metadata of the wrangled data (timestamps, season and existence of Landsat data).

Returns:

Name Type Description
metadata DataFrame

Metadata DataFrame associated with the wrangled data having columns: - "timestamp" - "season" - season associated with timestamp; - "landsat_exists" - indicator of existence of Landsat data.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def extract_metadata(self) -> pd.DataFrame:
    """
    Extract the metadata of the wrangled data (timestamps, season and existence of
    Landsat data).

    Returns
    -------
    metadata : pd.DataFrame
        Metadata DataFrame associated with the wrangled data having columns:
            - `"timestamp"`
            - `"season"` - season associated with timestamp;
            - `"landsat_exists"` - indicator of existence of Landsat data.
    """

    metadata = pd.DataFrame({"timestamp": self.timestamps})
    metadata["season"] = metadata["timestamp"].apply(get_season)
    metadata["landsat_exists"] = metadata["timestamp"].apply(
        lambda timestamp: (
            self.single_data_wrangler[timestamp].path_landsat is not None
        )
    )

    return metadata

get_coords

get_coords(
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
) -> (
    DatasetCoordinates
    | dict[Timestamp, DatasetCoordinates]
    | dict[Timestamp, dict[Literal["coarse", "fine"], DatasetCoordinates]]
)

Get Sentinel-3's coordinates associated with issued timestamps and grid alias.

Note that if timestamps or grid alias are not issued, the returned value corresponds to coordinates of all timestamps or grids, respectively, keyed by timestamp or grid alias.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the coordinates. If not issued, the coordinates of all timestamps is considered.

None
grid (coarse, fine, None)

Alias of the grid associated with the coordinates. If not issued, the coordinates of both grids are returned.

"coarse"

Returns:

Name Type Description
coords xr.core.coordinates.DatasetCoordinates or dict[pd.Timestamp,
xr.core.coordinates.DatasetCoordinates] or dict[pd.Timestamp, dict[{"coarse",
"fine"}, xr.core.coordinates.DatasetCoordinates]]

Coordinates associated with Sentinel-3's issued timestamps and grid alias. Note that if timestamps or grid are not issued, the returned value corresponds to coordinates of all timestamps or grids, respectively, keyed by timestamp or grid alias.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def get_coords(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
) -> (
    xr.core.coordinates.DatasetCoordinates  # type: ignore
    | dict[pd.Timestamp, xr.core.coordinates.DatasetCoordinates]  # type: ignore
    | dict[
        pd.Timestamp,
        dict[Literal["coarse", "fine"], xr.core.coordinates.DatasetCoordinates],  # type: ignore
    ]
):
    """
    Get Sentinel-3's coordinates associated with issued `timestamps` and `grid`
    alias.

    Note that if `timestamps` or `grid` alias are not issued, the returned value
    corresponds to coordinates of all timestamps or grids, respectively, keyed by
    timestamp or grid alias.

    Parameters
    ----------

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the coordinates. If not issued, the coordinates
        of all timestamps is considered.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the coordinates. If not issued, the
        coordinates of both grids are returned.


    Returns
    -------
    coords : xr.core.coordinates.DatasetCoordinates or dict[pd.Timestamp,
    xr.core.coordinates.DatasetCoordinates] or dict[pd.Timestamp, dict[{"coarse",
    "fine"}, xr.core.coordinates.DatasetCoordinates]]
        Coordinates associated with Sentinel-3's issued `timestamps` and `grid`
        alias. Note that if `timestamps` or `grid` are not issued, the returned
        value corresponds to coordinates of all timestamps or grids, respectively,
        keyed by timestamp or grid alias.
    """

    coords = {
        timestamp_: {
            grid_: self.single_data_wrangler[timestamp_].coords[grid_]
            for grid_ in ([grid] if grid is not None else self.grids)
        }
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        )
    }

    # Squeeze
    if grid is not None:
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        ):
            coords[timestamp_] = coords[timestamp_][grid]  # type: ignore
    if isinstance(timestamps, pd.Timestamp):
        coords = coords[timestamps]

    return coords

get_data

get_data(
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    vars: str | list[str] | None = None,
    trans: bool = False,
    aggregate: bool = False,
) -> (
    Series
    | DataFrame
    | dict[Timestamp, Series | DataFrame]
    | dict[Literal["coarse", "fine"], Series | DataFrame]
    | dict[Timestamp, dict[Literal["coarse", "fine"], Series | DataFrame]]
)

Get wrangled and, if trans is True, further transformed data vars for issued timestamps and grid alias.

Note that if vars is not issued, the data of all variables is returned. Also, if timestamps or grid are not issued, the returned value corresponds to data of all timestamps or grids, respectively, keyed by timestamp or grid alias. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is considered.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is returned.

"coarse"
vars str or list[str] or None

Variables of the data to return. If not issued, the data of all variables is returned.

None
trans bool

Whether to get transformed data.

False
aggregate bool

Whether to aggregate the data with respect to timestamps.

False

Returns:

Name Type Description
data pd.Series or pd.DataFrame or dict[pd.Timestamp, pd.Series or
pd.DataFrame] or dict[{"coarse", "fine"}, pd.Series or pd.DataFrame] or
dict[Timestamp, dict[{coarse, fine}, Series or DataFrame]]

Wrangled and, if trans is True, further transformed data vars for issued timestamps and grid alias. Note that if vars is not issued, the data of all variables is returned. Also, if timestamps or grid are not issued, the returned value corresponds to data of all timestamps or grids, respectively, keyed by timestamp or grid alias. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def get_data(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    vars: str | list[str] | None = None,
    trans: bool = False,
    aggregate: bool = False,
) -> (
    pd.Series
    | pd.DataFrame
    | dict[pd.Timestamp, pd.Series | pd.DataFrame]
    | dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
    | dict[pd.Timestamp, dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]]
):
    """
    Get wrangled and, if `trans` is `True`, further transformed data `vars` for
    issued `timestamps` and `grid` alias.

    Note that if `vars` is not issued, the data of all variables is returned. Also,
    if `timestamps` or `grid` are not issued, the returned value corresponds to data
    of all timestamps or grids, respectively, keyed by timestamp or grid alias. If
    `aggregate` is `True`, the data instead of being keyed by timestamp is
    aggregated with respect to it. If the instance has no transformation (attribute
    `transform` is `None`), the untransformed data is the one considered regardless
    of the value of `trans`.

    Parameters
    ----------

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is considered.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the data. If not issued, the data of both
        grids is returned.

    vars : str or list[str] or None, default=None
        Variables of the data to return. If not issued, the data of all variables is
        returned.

    trans : bool, default=False
        Whether to get transformed data.

    aggregate: bool, default=False
        Whether to aggregate the data with respect to timestamps.

    Returns
    -------
    data : pd.Series or pd.DataFrame or dict[pd.Timestamp, pd.Series or
    pd.DataFrame] or dict[{"coarse", "fine"}, pd.Series or pd.DataFrame] or
    dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]
        Wrangled and, if `trans` is `True`, further transformed data `vars` for
        issued `timestamps` and `grid` alias. Note that if `vars` is not issued, the
        data of all variables is returned. Also, if `timestamps` or `grid` are not
        issued, the returned value corresponds to data of all timestamps or grids,
        respectively, keyed by timestamp or grid alias. If `aggregate` is `True`,
        the data instead of being keyed by timestamp is aggregated with respect to
        it. If the instance has no transformation (attribute `transform` is `None`),
        the untransformed data is the one considered regardless of the value of
        `trans`.
    """

    data = {
        timestamp_: {
            grid_: self.single_data_wrangler[timestamp_].get_data(
                grid=grid_,  # type: ignore
                vars=vars,
                trans=trans,
            )
            for grid_ in ([grid] if grid is not None else self.grids)
        }
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        )
    }

    # If wanted, aggregate (concatenate) the data with respect to timestamps
    if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
        data = {
            grid_: pd.concat(
                [
                    data[timestamp_][grid_]
                    for timestamp_ in (
                        timestamps
                        if isinstance(timestamps, list)
                        else self.timestamps
                    )
                ],  # type: ignore
                ignore_index=True,
            )
            for grid_ in ([grid] if grid is not None else self.grids)
        }

        # NOTE: when concatenating the data, categorical columns may cease to be
        # categorical, hence the necessity of re-setting their type after
        # concatenation.
        for grid_ in [grid] if grid is not None else self.grids:
            if not isinstance(vars, str):
                X_cat = [
                    var
                    for var in self.data_vars.X_cat
                    if var in data[grid_].columns
                ]
                data[grid_][X_cat] = data[grid_][X_cat].astype("category")
            else:
                if vars in self.data_vars.X_cat:
                    data[grid_] = data[grid_].astype("category")

    # Squeeze
    if grid is not None:
        if not isinstance(timestamps, pd.Timestamp) and aggregate is True:
            data = data[grid]
        else:
            for timestamp_ in (
                [timestamps]
                if isinstance(timestamps, pd.Timestamp)
                else timestamps
                if isinstance(timestamps, list)
                else self.timestamps
            ):
                data[timestamp_] = data[timestamp_][grid]  # type: ignore
    if isinstance(timestamps, pd.Timestamp):
        data = data[timestamps]  # type: ignore

    return data  # type: ignore

get_data_X_and_mask

get_data_X_and_mask(
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
    aggregate: bool = False,
) -> (
    DataFrame
    | dict[Timestamp, DataFrame]
    | dict[Literal["coarse", "fine"], DataFrame]
    | dict[Timestamp, dict[Literal["coarse", "fine"], DataFrame]]
)

Get wrangled and, if trans is True, further transformed predictor and AOI mask data for issued timestamps and grid alias.

Note that if timestamps or grid are not issued, the returned value corresponds to data of all timestamps or grids, respectively, keyed by timestamp or grid alias. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is considered.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is returned.

"coarse"
trans bool

Whether to get transformed data.

False
aggregate bool

Whether to aggregate the data with respect to timestamps.

False

Returns:

Name Type Description
data_X_and_mask pd.DataFrame or dict[pd.Timestamp, pd.DataFrame] or
dict[{"coarse", "fine"}, pd.DataFrame] or dict[pd.Timestamp, dict[{"coarse",
"fine"}, pd.DataFrame]]

Wrangled and, if trans is True, further transformed predictor and AOI mask data for issued timestamps and grid alias. Note that if timestamps or grid are not issued, the returned value corresponds to data of all timestamps or grids, respectively, keyed by timestamp or grid alias. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def get_data_X_and_mask(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
    aggregate: bool = False,
) -> (
    pd.DataFrame
    | dict[pd.Timestamp, pd.DataFrame]
    | dict[Literal["coarse", "fine"], pd.DataFrame]
    | dict[pd.Timestamp, dict[Literal["coarse", "fine"], pd.DataFrame]]
):
    """
    Get wrangled and, if `trans` is `True`, further transformed predictor and AOI
    mask data for issued `timestamps` and `grid` alias.

    Note that if `timestamps` or `grid` are not issued, the returned value
    corresponds to data of all timestamps or grids, respectively, keyed by timestamp
    or grid alias. If `aggregate` is `True`, the data instead of being keyed by
    timestamp is aggregated with respect to it. If the instance has no
    transformation (attribute `transform` is `None`), the untransformed data is the
    one considered regardless of the value of `trans`.

    Parameters
    ----------

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is considered.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the data. If not issued, the data of both
        grids is returned.

    trans : bool, default=False
        Whether to get transformed data.


    aggregate: bool, default=False
        Whether to aggregate the data with respect to timestamps.

    Returns
    -------
    data_X_and_mask : pd.DataFrame or dict[pd.Timestamp, pd.DataFrame] or
    dict[{"coarse", "fine"}, pd.DataFrame] or dict[pd.Timestamp, dict[{"coarse",
    "fine"}, pd.DataFrame]]
        Wrangled and, if `trans` is `True`, further transformed predictor and AOI
        mask data for issued `timestamps` and `grid` alias. Note that if
        `timestamps` or `grid` are not issued, the returned value corresponds to
        data of all timestamps or grids, respectively, keyed by timestamp or grid
        alias. If `aggregate` is `True`, the data instead of being keyed by
        timestamp is aggregated with respect to it. If the instance has no
        transformation (attribute `transform` is `None`), the untransformed data is
        the one considered regardless of the value of `trans`.
    """

    return self.get_data(
        timestamps=timestamps,
        grid=grid,
        vars=self.data_vars.X + (["aoi"] if self.aoi is not None else []),  # type: ignore
        trans=trans,
        aggregate=aggregate,
    )  # type: ignore

get_data_y

get_data_y(
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
    aggregate: bool = False,
) -> (
    Series
    | dict[Timestamp, Series]
    | dict[Literal["coarse", "fine"], Series]
    | dict[Timestamp, dict[Literal["coarse", "fine"], Series]]
)

Get wrangled and, if trans is True, further transformed target data for issued issued timestamps and grid alias.

Note that if timestamps or grid are not issued, the returned value corresponds to data of all timestamps or grids, respectively, keyed by timestamp or grid alias. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is considered.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is returned.

"coarse"
trans bool

Whether to get transformed data.

False
aggregate bool

Whether to aggregate the data with respect to timestamps.

False

Returns:

Name Type Description
data_y pd.Series or dict[pd.Timestamp, pd.Series] or dict[{"coarse", "fine"},
pd.Series] or dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series]]

Wrangled and, if trans is True, further transformed target data for issued timestamps and grid alias. Note that if timestamps or grid are not issued, the returned value corresponds to data of all timestamps or grids, respectively, keyed by timestamp or grid alias. If aggregate is True, the data instead of being keyed by timestamp is aggregated with respect to it. If the instance has no transformation (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def get_data_y(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
    aggregate: bool = False,
) -> (
    pd.Series
    | dict[pd.Timestamp, pd.Series]
    | dict[Literal["coarse", "fine"], pd.Series]
    | dict[pd.Timestamp, dict[Literal["coarse", "fine"], pd.Series]]
):
    """
    Get wrangled and, if `trans` is `True`, further transformed target data for
    issued issued `timestamps` and `grid` alias.

    Note that if `timestamps` or `grid` are not issued, the returned value
    corresponds to data of all timestamps or grids, respectively, keyed by timestamp
    or grid alias. If `aggregate` is `True`, the data instead of being keyed by
    timestamp is aggregated with respect to it. If the instance has no
    transformation (attribute `transform` is `None`), the untransformed data is the
    one considered regardless of the value of `trans`.

    Parameters
    ----------

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is considered.

    grid : {"coarse", "fine", None}, default="coarse"
        Alias of the grid associated with the data. If not issued, the data of both
        grids is returned.

    trans : bool, default=False
        Whether to get transformed data.

    aggregate: bool, default=False
        Whether to aggregate the data with respect to timestamps.

    Returns
    -------
    data_y : pd.Series or dict[pd.Timestamp, pd.Series] or dict[{"coarse", "fine"},
    pd.Series] or dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series]]
        Wrangled and, if `trans` is `True`, further transformed target data for
        issued `timestamps` and `grid` alias. Note that if `timestamps` or `grid`
        are not issued, the returned value corresponds to data of all timestamps or
        grids, respectively, keyed by timestamp or grid alias. If `aggregate` is
        `True`, the data instead of being keyed by timestamp is aggregated with
        respect to it. If the instance has no transformation (attribute `transform`
        is `None`), the untransformed data is the one considered regardless of the
        value of `trans`.
    """

    return self.get_data(
        timestamps=timestamps,
        grid=grid,
        vars=self.data_vars.y,
        trans=trans,
        aggregate=aggregate,
    )  # type: ignore

get_metadata

get_metadata(
    timestamps: Timestamp | list[Timestamp] | None = None,
    vars: str | list[str] | None = None,
) -> Series | DataFrame

Get values of metadata vars associated with the timestamps timestamps of the wrangled data.

Note that if timestamps or vars are not issued, the returned value corresponds to the metadata of of all timestamps or metadata variables, respectively.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the metadata. If not issued, the metadata of all timestamps is considered.

None
vars str or list[str] or None

Variables of the metadata to return. If not issued, all metadata variables are returned.

None

Returns:

Name Type Description
metadata Series or DataFrame
Get values of metadata `vars` associated with the timestamps `timestamps` of the
wrangled data. Note that if `timestamps` or `vars` are not issued, the returned
value corresponds to the metadata of of all timestamps or metadata variables,
respectively.
Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def get_metadata(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    vars: str | list[str] | None = None,
) -> pd.Series | pd.DataFrame:
    """
    Get values of metadata `vars` associated with the timestamps `timestamps` of the
    wrangled data.

    Note that if `timestamps` or `vars` are not issued, the returned value
    corresponds to the metadata of of all timestamps or metadata variables,
    respectively.

    Parameters
    ----------
    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the metadata. If not issued, the metadata of all
        timestamps is considered.
    vars : str or list[str] or None, default=None
        Variables of the metadata to return. If not issued, all metadata variables
        are returned.

    Returns
    -------

    metadata : pd.Series or pd.DataFrame
    Get values of metadata `vars` associated with the timestamps `timestamps` of the
    wrangled data. Note that if `timestamps` or `vars` are not issued, the returned
    value corresponds to the metadata of of all timestamps or metadata variables,
    respectively.
    """

    metadata = (
        self.metadata
        if timestamps is None
        else self.metadata[
            self.metadata["timestamp"].isin(
                [timestamps] if isinstance(timestamps, pd.Timestamp) else timestamps
            )
        ]
    )[vars if vars is not None else self.metadata.columns]

    return metadata

get_shape

get_shape(
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
) -> (
    tuple[int, int]
    | dict[Timestamp, tuple[int, int]]
    | dict[Timestamp, dict[Literal["coarse", "fine"], tuple[int, int]]]
)

Get Sentinel-3's grid shape associated with issued timestamps and grid alias.

Note that if timestamps or grid are not issued, the returned value corresponds to shapes of all timestamps or grids, respectively, keyed by timestamp or grid alias.

Parameters:

Name Type Description Default
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the shapes. If not issued, the shapes of all timestamps is considered.

None
grid (coarse, fine, None)

Alias of the grid associated with the shapes. If not issued, the shapes of both grids are returned.

"coarse"

Returns:

Name Type Description
shape tuple[int, int] or dict[pd.Timestamp, tuple[int, int]] or
dict[Timestamp, dict[{coarse, fine}, tuple[int, int]]]

Grid shape associated with Sentinel-3's issued timestamps and grid alias. Note that if timestamps or grid are not issued, the returned value corresponds to shapes of all timestamps or grids, respectively, keyed by timestamp or grid alias.

Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def get_shape(
    self,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
) -> (
    tuple[int, int]
    | dict[pd.Timestamp, tuple[int, int]]
    | dict[pd.Timestamp, dict[Literal["coarse", "fine"], tuple[int, int]]]
):
    """
    Get Sentinel-3's grid shape associated with issued `timestamps` and `grid`
    alias.

    Note that if `timestamps` or `grid` are not issued, the returned value
    corresponds to shapes of all timestamps or grids, respectively, keyed by
    timestamp or grid alias.

    Parameters
    ----------

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the shapes. If not issued, the shapes of all
        timestamps is considered.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the shapes. If not issued, the shapes of
        both grids are returned.

    Returns
    -------
    shape : tuple[int, int] or dict[pd.Timestamp, tuple[int, int]] or
    dict[pd.Timestamp, dict[{"coarse", "fine"}, tuple[int, int]]]
        Grid shape associated with Sentinel-3's issued `timestamps` and `grid`
        alias. Note that if `timestamps` or `grid` are not issued, the returned
        value corresponds to shapes of all timestamps or grids, respectively, keyed
        by timestamp or grid alias.
    """

    shape = {
        timestamp_: {
            grid_: self.single_data_wrangler[timestamp_].shape[grid_]
            for grid_ in ([grid] if grid is not None else self.grids)
        }
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        )
    }

    # Squeeze
    if grid is not None:
        for timestamp_ in (
            [timestamps]
            if isinstance(timestamps, pd.Timestamp)
            else timestamps
            if isinstance(timestamps, list)
            else self.timestamps
        ):
            shape[timestamp_] = shape[timestamp_][grid]  # type: ignore
    if isinstance(timestamps, pd.Timestamp):
        shape = shape[timestamps]

    return shape  # type: ignore

save

save(path: Path) -> None

Write the instance to path with joblib.

Parameters:

Name Type Description Default
path Path

Path to write the instance to.

required
Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def save(self, path: Path) -> None:
    """
    Write the instance to `path` with `joblib`.

    Parameters
    ----------
    path : Path
        Path to write the instance to.
    """

    joblib.dump(value=self, filename=path)

set_data

set_data(
    values: Series
    | DataFrame
    | dict[Literal["coarse", "fine"], Series | DataFrame]
    | dict[Timestamp, Series | DataFrame]
    | dict[Timestamp, dict[Literal["coarse", "fine"], Series | DataFrame]],
    vars: str | list[str] | None = None,
    timestamps: Timestamp | list[Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
) -> None

Set wrangled and, if trans is True, further transformed data vars of issued timestamps and grid alias to values.

Note that vars may correspond to new variables. If not defined, vars is set to all variables of the data. If timestamps or grid is not issued, the data of all timestamps or grids, respectively, is set. If there is no transform in the instance (attribute transform is None), the untransformed data is the one considered regardless of the value of trans.

Parameters:

Name Type Description Default
values dict[Timestamp, Series or DataFrame] or dict[Timestamp, dict[{coarse, fine}, Series or DataFrame]]

Values to set.

required
vars str or list[str] or None

Variables of single_data_wranglers data to set. Note that vars may correspond to new variables. If not defined, vars is set as all variables of the data.

None
timestamps Timestamp or list[Timestamp] or None

Timestamps associated with the data. If not issued, the data of all timestamps is set.

None
grid (coarse, fine, None)

Alias of the grid associated with the data. If not issued, the data of both grids is set.

"coarse"
trans bool

Whether to set transformed data.

False
Source code in src/s3lst_ds/data_wrangling/data_wrangling.py
def set_data(
    self,
    values: (
        pd.Series
        | pd.DataFrame
        | dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
        | dict[pd.Timestamp, pd.Series | pd.DataFrame]
        | dict[
            pd.Timestamp, dict[Literal["coarse", "fine"], pd.Series | pd.DataFrame]
        ]
    ),
    vars: str | list[str] | None = None,
    timestamps: pd.Timestamp | list[pd.Timestamp] | None = None,
    grid: Literal["coarse", "fine"] | None = None,
    trans: bool = False,
) -> None:
    """
    Set wrangled and, if `trans` is `True`, further transformed data `vars` of
    issued `timestamps` and `grid` alias to `values`.

    Note that `vars` may correspond to new variables. If not defined, `vars` is set
    to all variables of the data. If `timestamps` or `grid` is not issued, the data
    of all timestamps or grids, respectively, is set. If there is no transform in
    the instance (attribute `transform` is `None`), the untransformed data is the
    one considered regardless of the value of `trans`.

    Parameters
    ----------

    values : dict[pd.Timestamp, pd.Series or pd.DataFrame] or dict[pd.Timestamp, dict[{"coarse", "fine"}, pd.Series or pd.DataFrame]]
        Values to set.

    vars : str or list[str] or None, default=None
        Variables of `single_data_wrangler`s data to set. Note that `vars` may
        correspond to new variables. If not defined, `vars` is set as all variables
        of the data.

    timestamps : pd.Timestamp or list[pd.Timestamp] or None, default=None
        Timestamps associated with the data. If not issued, the data of all
        timestamps is set.

    grid : {"coarse", "fine", None}, default=None
        Alias of the grid associated with the data. If not issued, the data of both
        grids is set.

    trans : bool, default=False
        Whether to set transformed data.
    """

    for timestamp_ in (
        [timestamps]
        if isinstance(timestamps, pd.Timestamp)
        else timestamps
        if isinstance(timestamps, list)
        else self.timestamps
    ):
        self.single_data_wrangler[timestamp_].set_data(
            values=(
                values
                if isinstance(timestamps, pd.Timestamp)
                else values[timestamp_]  # type: ignore
            ),
            vars=vars,
            grid=grid,
            trans=trans,
        )