Welcome to the second episode of our R from Zero to Hero journey. In this section we will look at how to get data into our “screen”, in front of our eyes. In other words, how to load or import data, but also how to save data after some process has been completed.

Data collection

There are various way to get data to our screens, depending on the origin or source of data. We will try to cover these methods briefly.

External data sources

we called them external because they are not currently located in our environment - not from our station necessary.

Loading data from a folder

In the early days of your R journey, you are most likely to load data from MyDocument folder. We will look at various case here, depending on the format of the file.

Loadind data from an Excel file

library(readxl)
my_dataframe_xlsx <- read_xlsx('./data/insider-purchases.xlsx')
print(head(my_dataframe_xlsx))

Loadind data from a CSV file

my_dataframe_csv <- read.csv('./data/insider-purchases.csv')
print(head(my_dataframe_csv))
NA

Loadind data from a PDF file

Depending on the industry or the subject that we are working on, we might have to import data from a PDF file. While this is not usually demonstrated in entry level material, I choose to cover it here, because you will not have the choice on the format of data that are made available to you.

library(tabulapdf)
#path_to_file <- "data/insider-purchases-pdf.pdf"
#my_dataframe_pdf <- extract_tables(path_to_file)
#### WE WILL HAVE TO COME BACK HERE TO HANDLE MULTI PAGES PDF TABLE

Loadind data from RDS file

The RDS format in R is a file format used to store a single R object, created using the saveRDS() function and read using the readRDS() function. The primary advantages of RDS are:

  1. Serialization of Single Objects: RDS files store a single R object, making it simple to save and load specific data structures, models, or any R objects.
  2. Preservation of Object Structure: RDS preserves the exact structure and attributes of the saved R object, ensuring consistency when reloaded.
  3. Compact Storage: RDS files are often smaller due to internal compression, which saves disk space.
  4. Flexibility in Naming: Unlike other formats, when reading an RDS file, you can assign any name to the loaded object.
  5. Interoperability: RDS files can be easily shared and used across different R sessions and environments, enhancing reproducibility.
my_dataframe_rds <- readRDS('data/my_dataframe_rds.rds')
print(head(my_dataframe_rds))
NA

Loading data from a database

# here develop how to use Postgres etc

Data Export

After completing certain task on our data, we would most likely need to keep some of the records,…or all of them. For this purpose we will do the exact opposite of the data collection processes: we will export our data. In this section we will use the opposite of the ‘loading’ functions that were previously used. Before exporting our data, we will print a preview, just for sanity checks. we will use the dataset that was previously loaded from an RDS file - this is a discretionary choice.

dataset_to_be_exported <- my_dataframe_rds

print(head(dataset_to_be_exported))
NA

Exporting to Excel file

xlsx::write.xlsx(dataset_to_be_exported, 'data/exports/dataset_to_be_exported.xlsx')

Because this will likely happen, you might want to save this file using the date of today as a key differentiator, we better as well learn how to do it now. We will rely on the paste function of R, as well ad the date formating funtcion - note we can apply that to any file naming prior to saving.

Exporting to an Excel file with a date inside the name


# Format the date of today
today_date_formated = format(as.Date(Sys.Date()), format="%Y-%B-%d")

#Create the file name string
file_name = paste(today_date_formated,"dataset_to_be_exported.xlsx", sep=" ")

# Get the path to export
file_path_for_export = paste(getwd(),'/data/exports/', file_name, sep='')

# Export the file to the previously created name
xlsx::write.xlsx(dataset_to_be_exported,file_path_for_export)

Ah, what just happened. We just used two functions that we have never seen before. 1 - Sys.Date(): This method gives us the current date of our machine. We could also use Sys.time(), which would return for example: “2020-05-31 14:49:47 HKT” 2 - getwd()….

Exporting to CSV file

write.csv(dataset_to_be_exported, 'data/exports/dataset_to_be_exported.csv')

Exporting to RDS file

saveRDS(dataset_to_be_exported, 'data/exports/dataset_to_be_exported.rds')

Exporting to Database

We will leave the following section for now, we will get back at it later with the actual setup of a database - we need to consider what is the best option for data exchange here

Database set up and connection

The folliwing section assumes that you have already setup a database once again.

library(RPostgres)
con <-
  dbConnect(
    RPostgres::Postgres(),
    dbname = "market_data",
    host = "localhost",
    port = 5432,
    user = "myself",
    password = "123456",
  )

Updating Database record - with append

dbWriteTable(
  con2,
  "Example1",
  dataset_to_be_exported,
  overwrite = FALSE,
  row.names = FALSE
)

Updating Database record - with replace

dbWriteTable(
  con2,
  "Example1",
  dataset_to_be_exported,
  overwrite = TRUE,
  row.names = FALSE
)

Internal data sources - saving and loading an environment

With internal data source, we include anything that is in our current environment. Assume that we have done some work on multiple dataframe, and we are not looking to individually save each of them in an excel, CSV or RDS file, because there would be 100 of them. Well R allows us to save the entire environment, as it is, with all the variables, dataframe, etc. that we have been working on. Let’s have a look at the command for that purpose.

Saving an environment

save.image(file='myEnvironment.RData')

Loading an environment

    
load('myHeroEnvironment.RData')

This is it for importing and exporting data for today, we will now move to Data Exploration, which is a critical step in our Machine Learning Pipeline

LS0tDQp0aXRsZTogIlIsIGZyb20gWmVybyB0byBIZXJvOiBEYXRhIE1hbmFnZW1lbnQgKEltcG9ydGluZywgbG9hZGluZywgc2F2aW5nKSINCmF1dGhvcjogIkZyYW50eiBNb3Vkb3V0ZSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCldlbGNvbWUgdG8gdGhlIHNlY29uZCBlcGlzb2RlIG9mIG91ciBSIGZyb20gWmVybyB0byBIZXJvIGpvdXJuZXkuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGxvb2sgYXQgaG93IHRvIGdldCBkYXRhIGludG8gb3VyICJzY3JlZW4iLCBpbiBmcm9udCBvZiBvdXIgZXllcy4gSW4gb3RoZXIgd29yZHMsIGhvdyB0byBsb2FkIG9yIGltcG9ydCBkYXRhLCBidXQgYWxzbyBob3cgdG8gc2F2ZSBkYXRhIGFmdGVyIHNvbWUgcHJvY2VzcyBoYXMgYmVlbiBjb21wbGV0ZWQuDQoNCiMgRGF0YSBjb2xsZWN0aW9uDQpUaGVyZSBhcmUgdmFyaW91cyB3YXkgdG8gZ2V0IGRhdGEgdG8gb3VyIHNjcmVlbnMsIGRlcGVuZGluZyBvbiB0aGUgb3JpZ2luIG9yIHNvdXJjZSBvZiBkYXRhLiBXZSB3aWxsIHRyeSB0byBjb3ZlciB0aGVzZSBtZXRob2RzIGJyaWVmbHkuDQoNCiMjIEV4dGVybmFsIGRhdGEgc291cmNlcw0Kd2UgY2FsbGVkIHRoZW0gZXh0ZXJuYWwgYmVjYXVzZSB0aGV5IGFyZSBub3QgY3VycmVudGx5IGxvY2F0ZWQgaW4gb3VyIGVudmlyb25tZW50IC0gbm90IGZyb20gb3VyIHN0YXRpb24gbmVjZXNzYXJ5LiANCg0KIyMjIExvYWRpbmcgZGF0YSBmcm9tIGEgZm9sZGVyDQpJbiB0aGUgZWFybHkgZGF5cyBvZiB5b3VyIFIgam91cm5leSwgeW91IGFyZSBtb3N0IGxpa2VseSB0byBsb2FkIGRhdGEgZnJvbSBNeURvY3VtZW50IGZvbGRlci4gV2Ugd2lsbCBsb29rIGF0IHZhcmlvdXMgY2FzZSBoZXJlLCBkZXBlbmRpbmcgb24gdGhlIGZvcm1hdCBvZiB0aGUgZmlsZS4NCg0KIyMjIyBMb2FkaW5kIGRhdGEgZnJvbSBhbiBFeGNlbCBmaWxlDQpgYGB7cn0NCmxpYnJhcnkocmVhZHhsKQ0KbXlfZGF0YWZyYW1lX3hsc3ggPC0gcmVhZF94bHN4KCcuL2RhdGEvaW5zaWRlci1wdXJjaGFzZXMueGxzeCcpDQpwcmludChoZWFkKG15X2RhdGFmcmFtZV94bHN4KSkNCmBgYA0KDQoNCiMjIyMgTG9hZGluZCBkYXRhIGZyb20gYSBDU1YgZmlsZQ0KYGBge3J9DQpteV9kYXRhZnJhbWVfY3N2IDwtIHJlYWQuY3N2KCcuL2RhdGEvaW5zaWRlci1wdXJjaGFzZXMuY3N2JykNCnByaW50KGhlYWQobXlfZGF0YWZyYW1lX2NzdikpDQoNCmBgYA0KDQoNCiMjIyMgTG9hZGluZCBkYXRhIGZyb20gYSBQREYgZmlsZQ0KRGVwZW5kaW5nIG9uIHRoZSBpbmR1c3RyeSBvciB0aGUgc3ViamVjdCB0aGF0IHdlIGFyZSB3b3JraW5nIG9uLCB3ZSBtaWdodCBoYXZlIHRvIGltcG9ydCBkYXRhIGZyb20gYSBQREYgZmlsZS4gV2hpbGUgdGhpcyBpcyBub3QgdXN1YWxseSBkZW1vbnN0cmF0ZWQgaW4gZW50cnkgbGV2ZWwgbWF0ZXJpYWwsIEkgY2hvb3NlIHRvIGNvdmVyIGl0IGhlcmUsIGJlY2F1c2UgeW91IHdpbGwgbm90IGhhdmUgdGhlIGNob2ljZSBvbiB0aGUgZm9ybWF0IG9mIGRhdGEgdGhhdCBhcmUgbWFkZSBhdmFpbGFibGUgdG8geW91Lg0KYGBge3J9DQpsaWJyYXJ5KHRhYnVsYXBkZikNCiNwYXRoX3RvX2ZpbGUgPC0gImRhdGEvaW5zaWRlci1wdXJjaGFzZXMtcGRmLnBkZiINCiNteV9kYXRhZnJhbWVfcGRmIDwtIGV4dHJhY3RfdGFibGVzKHBhdGhfdG9fZmlsZSkNCiMjIyMgV0UgV0lMTCBIQVZFIFRPIENPTUUgQkFDSyBIRVJFIFRPIEhBTkRMRSBNVUxUSSBQQUdFUyBQREYgVEFCTEUNCmBgYA0KDQoNCg0KIyMjIyBMb2FkaW5kIGRhdGEgZnJvbSBSRFMgZmlsZQ0KVGhlIFJEUyBmb3JtYXQgaW4gUiBpcyBhIGZpbGUgZm9ybWF0IHVzZWQgdG8gc3RvcmUgYSBzaW5nbGUgUiBvYmplY3QsIGNyZWF0ZWQgdXNpbmcgdGhlIGBzYXZlUkRTKClgIGZ1bmN0aW9uIGFuZCByZWFkIHVzaW5nIHRoZSBgcmVhZFJEUygpYCBmdW5jdGlvbi4gVGhlIHByaW1hcnkgYWR2YW50YWdlcyBvZiBSRFMgYXJlOg0KDQoxLiAqKlNlcmlhbGl6YXRpb24gb2YgU2luZ2xlIE9iamVjdHMqKjogUkRTIGZpbGVzIHN0b3JlIGEgc2luZ2xlIFIgb2JqZWN0LCBtYWtpbmcgaXQgc2ltcGxlIHRvIHNhdmUgYW5kIGxvYWQgc3BlY2lmaWMgZGF0YSBzdHJ1Y3R1cmVzLCBtb2RlbHMsIG9yIGFueSBSIG9iamVjdHMuDQoyLiAqKlByZXNlcnZhdGlvbiBvZiBPYmplY3QgU3RydWN0dXJlKio6IFJEUyBwcmVzZXJ2ZXMgdGhlIGV4YWN0IHN0cnVjdHVyZSBhbmQgYXR0cmlidXRlcyBvZiB0aGUgc2F2ZWQgUiBvYmplY3QsIGVuc3VyaW5nIGNvbnNpc3RlbmN5IHdoZW4gcmVsb2FkZWQuDQozLiAqKkNvbXBhY3QgU3RvcmFnZSoqOiBSRFMgZmlsZXMgYXJlIG9mdGVuIHNtYWxsZXIgZHVlIHRvIGludGVybmFsIGNvbXByZXNzaW9uLCB3aGljaCBzYXZlcyBkaXNrIHNwYWNlLg0KNC4gKipGbGV4aWJpbGl0eSBpbiBOYW1pbmcqKjogVW5saWtlIG90aGVyIGZvcm1hdHMsIHdoZW4gcmVhZGluZyBhbiBSRFMgZmlsZSwgeW91IGNhbiBhc3NpZ24gYW55IG5hbWUgdG8gdGhlIGxvYWRlZCBvYmplY3QuDQo1LiAqKkludGVyb3BlcmFiaWxpdHkqKjogUkRTIGZpbGVzIGNhbiBiZSBlYXNpbHkgc2hhcmVkIGFuZCB1c2VkIGFjcm9zcyBkaWZmZXJlbnQgUiBzZXNzaW9ucyBhbmQgZW52aXJvbm1lbnRzLCBlbmhhbmNpbmcgcmVwcm9kdWNpYmlsaXR5Lg0KDQoNCmBgYHtyfQ0KbXlfZGF0YWZyYW1lX3JkcyA8LSByZWFkUkRTKCdkYXRhL215X2RhdGFmcmFtZV9yZHMucmRzJykNCnByaW50KGhlYWQobXlfZGF0YWZyYW1lX3JkcykpDQoNCmBgYA0KIyMjIExvYWRpbmcgZGF0YSBmcm9tIGEgZGF0YWJhc2UNCg0KYGBge3J9DQojIGhlcmUgZGV2ZWxvcCBob3cgdG8gdXNlIFBvc3RncmVzIGV0Yw0KYGBgDQoNCiMgRGF0YSBFeHBvcnQNCkFmdGVyIGNvbXBsZXRpbmcgY2VydGFpbiB0YXNrIG9uIG91ciBkYXRhLCB3ZSB3b3VsZCBtb3N0IGxpa2VseSBuZWVkIHRvIGtlZXAgc29tZSBvZiB0aGUgcmVjb3JkcywuLi5vciBhbGwgb2YgdGhlbS4gRm9yIHRoaXMgcHVycG9zZSB3ZSB3aWxsIGRvIHRoZSBleGFjdCBvcHBvc2l0ZSBvZiB0aGUgZGF0YSBjb2xsZWN0aW9uIHByb2Nlc3Nlczogd2Ugd2lsbCBleHBvcnQgb3VyIGRhdGEuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHVzZSB0aGUgb3Bwb3NpdGUgb2YgdGhlICdsb2FkaW5nJyBmdW5jdGlvbnMgdGhhdCB3ZXJlIHByZXZpb3VzbHkgdXNlZC4gDQpCZWZvcmUgZXhwb3J0aW5nIG91ciBkYXRhLCB3ZSB3aWxsIHByaW50IGEgcHJldmlldywganVzdCBmb3Igc2FuaXR5IGNoZWNrcy4gd2Ugd2lsbCB1c2UgdGhlIGRhdGFzZXQgdGhhdCB3YXMgcHJldmlvdXNseSBsb2FkZWQgZnJvbSBhbiBSRFMgZmlsZSAtIHRoaXMgaXMgYSBkaXNjcmV0aW9uYXJ5IGNob2ljZS4NCg0KYGBge3J9DQpkYXRhc2V0X3RvX2JlX2V4cG9ydGVkIDwtIG15X2RhdGFmcmFtZV9yZHMNCg0KcHJpbnQoaGVhZChkYXRhc2V0X3RvX2JlX2V4cG9ydGVkKSkNCg0KYGBgDQoNCg0KIyMgRXhwb3J0aW5nIHRvIEV4Y2VsIGZpbGUNCmBgYHtyfQ0KeGxzeDo6d3JpdGUueGxzeChkYXRhc2V0X3RvX2JlX2V4cG9ydGVkLCAnZGF0YS9leHBvcnRzL2RhdGFzZXRfdG9fYmVfZXhwb3J0ZWQueGxzeCcpDQoNCmBgYA0KQmVjYXVzZSB0aGlzIHdpbGwgbGlrZWx5IGhhcHBlbiwgIHlvdSBtaWdodCB3YW50IHRvIHNhdmUgdGhpcyBmaWxlIHVzaW5nIHRoZSBkYXRlIG9mIHRvZGF5IGFzIGEga2V5IGRpZmZlcmVudGlhdG9yLCB3ZSBiZXR0ZXIgYXMgd2VsbCBsZWFybiBob3cgdG8gZG8gaXQgbm93LiBXZSB3aWxsIHJlbHkgb24gdGhlIHBhc3RlIGZ1bmN0aW9uIG9mIFIsIGFzIHdlbGwgYWQgdGhlIGRhdGUgZm9ybWF0aW5nIGZ1bnRjaW9uIC0gbm90ZSB3ZSBjYW4gYXBwbHkgdGhhdCB0byBhbnkgZmlsZSBuYW1pbmcgcHJpb3IgdG8gc2F2aW5nLiANCg0KIyMjIEV4cG9ydGluZyB0byBhbiBFeGNlbCBmaWxlIHdpdGggYSBkYXRlIGluc2lkZSB0aGUgbmFtZQ0KYGBge3J9DQoNCiMgRm9ybWF0IHRoZSBkYXRlIG9mIHRvZGF5DQp0b2RheV9kYXRlX2Zvcm1hdGVkID0gZm9ybWF0KGFzLkRhdGUoU3lzLkRhdGUoKSksIGZvcm1hdD0iJVktJUItJWQiKQ0KDQojQ3JlYXRlIHRoZSBmaWxlIG5hbWUgc3RyaW5nDQpmaWxlX25hbWUgPSBwYXN0ZSh0b2RheV9kYXRlX2Zvcm1hdGVkLCJkYXRhc2V0X3RvX2JlX2V4cG9ydGVkLnhsc3giLCBzZXA9IiAiKQ0KDQojIEdldCB0aGUgcGF0aCB0byBleHBvcnQNCmZpbGVfcGF0aF9mb3JfZXhwb3J0ID0gcGFzdGUoZ2V0d2QoKSwnL2RhdGEvZXhwb3J0cy8nLCBmaWxlX25hbWUsIHNlcD0nJykNCg0KIyBFeHBvcnQgdGhlIGZpbGUgdG8gdGhlIHByZXZpb3VzbHkgY3JlYXRlZCBuYW1lDQp4bHN4Ojp3cml0ZS54bHN4KGRhdGFzZXRfdG9fYmVfZXhwb3J0ZWQsZmlsZV9wYXRoX2Zvcl9leHBvcnQpDQpgYGANCg0KQWgsIHdoYXQganVzdCBoYXBwZW5lZC4gV2UganVzdCB1c2VkIHR3byBmdW5jdGlvbnMgdGhhdCB3ZSBoYXZlIG5ldmVyIHNlZW4gYmVmb3JlLiANCjEgLSBTeXMuRGF0ZSgpOiBUaGlzIG1ldGhvZCBnaXZlcyB1cyB0aGUgY3VycmVudCBkYXRlIG9mIG91ciBtYWNoaW5lLiBXZSBjb3VsZCBhbHNvIHVzZSBTeXMudGltZSgpLCB3aGljaCB3b3VsZCByZXR1cm4gZm9yIGV4YW1wbGU6ICIyMDIwLTA1LTMxIDE0OjQ5OjQ3IEhLVCINCjIgLSBnZXR3ZCgpLi4uLg0KDQoNCg0KDQoNCg0KIyMgRXhwb3J0aW5nIHRvIENTViBmaWxlDQpgYGB7cn0NCndyaXRlLmNzdihkYXRhc2V0X3RvX2JlX2V4cG9ydGVkLCAnZGF0YS9leHBvcnRzL2RhdGFzZXRfdG9fYmVfZXhwb3J0ZWQuY3N2JykNCmBgYA0KDQoNCiMjIEV4cG9ydGluZyB0byBSRFMgZmlsZQ0KYGBge3J9DQpzYXZlUkRTKGRhdGFzZXRfdG9fYmVfZXhwb3J0ZWQsICdkYXRhL2V4cG9ydHMvZGF0YXNldF90b19iZV9leHBvcnRlZC5yZHMnKQ0KYGBgDQoNCg0KDQojIyBFeHBvcnRpbmcgdG8gRGF0YWJhc2UNCldlIHdpbGwgbGVhdmUgdGhlIGZvbGxvd2luZyBzZWN0aW9uIGZvciBub3csIHdlIHdpbGwgZ2V0IGJhY2sgYXQgaXQgbGF0ZXIgd2l0aCB0aGUgYWN0dWFsIHNldHVwIG9mIGEgZGF0YWJhc2UgIC0gd2UgbmVlZCB0byBjb25zaWRlciB3aGF0IGlzIHRoZSBiZXN0IG9wdGlvbiBmb3IgZGF0YSBleGNoYW5nZSBoZXJlDQoNCiMjIyBEYXRhYmFzZSBzZXQgdXAgYW5kIGNvbm5lY3Rpb24NClRoZSBmb2xsaXdpbmcgc2VjdGlvbiBhc3N1bWVzIHRoYXQgeW91IGhhdmUgYWxyZWFkeSBzZXR1cCBhIGRhdGFiYXNlIG9uY2UgYWdhaW4uIA0KYGBge3J9DQpsaWJyYXJ5KFJQb3N0Z3JlcykNCmNvbiA8LQ0KICBkYkNvbm5lY3QoDQogICAgUlBvc3RncmVzOjpQb3N0Z3JlcygpLA0KICAgIGRibmFtZSA9ICJtYXJrZXRfZGF0YSIsDQogICAgaG9zdCA9ICJsb2NhbGhvc3QiLA0KICAgIHBvcnQgPSA1NDMyLA0KICAgIHVzZXIgPSAibXlzZWxmIiwNCiAgICBwYXNzd29yZCA9ICIxMjM0NTYiLA0KICApDQpgYGANCg0KIyMjIFVwZGF0aW5nIERhdGFiYXNlIHJlY29yZCAtIHdpdGggYXBwZW5kDQpgYGB7cn0NCmRiV3JpdGVUYWJsZSgNCiAgY29uMiwNCiAgIkV4YW1wbGUxIiwNCiAgZGF0YXNldF90b19iZV9leHBvcnRlZCwNCiAgb3ZlcndyaXRlID0gRkFMU0UsDQogIHJvdy5uYW1lcyA9IEZBTFNFDQopDQpgYGANCg0KDQojIyMgVXBkYXRpbmcgRGF0YWJhc2UgcmVjb3JkIC0gd2l0aCByZXBsYWNlDQpgYGB7cn0NCmRiV3JpdGVUYWJsZSgNCiAgY29uMiwNCiAgIkV4YW1wbGUxIiwNCiAgZGF0YXNldF90b19iZV9leHBvcnRlZCwNCiAgb3ZlcndyaXRlID0gVFJVRSwNCiAgcm93Lm5hbWVzID0gRkFMU0UNCikNCmBgYA0KDQoNCiMjIEludGVybmFsIGRhdGEgc291cmNlcyAtIHNhdmluZyBhbmQgbG9hZGluZyBhbiBlbnZpcm9ubWVudA0KV2l0aCBpbnRlcm5hbCBkYXRhIHNvdXJjZSwgd2UgaW5jbHVkZSBhbnl0aGluZyB0aGF0IGlzIGluIG91ciBjdXJyZW50IGVudmlyb25tZW50LiBBc3N1bWUgdGhhdCB3ZSBoYXZlIGRvbmUgc29tZSB3b3JrIG9uIG11bHRpcGxlIGRhdGFmcmFtZSwgYW5kIHdlIGFyZSBub3QgbG9va2luZyB0byBpbmRpdmlkdWFsbHkgc2F2ZSBlYWNoIG9mIHRoZW0gaW4gYW4gZXhjZWwsIENTViBvciBSRFMgZmlsZSwgYmVjYXVzZSB0aGVyZSB3b3VsZCBiZSAxMDAgb2YgdGhlbS4gV2VsbCBSIGFsbG93cyB1cyB0byBzYXZlIHRoZSBlbnRpcmUgZW52aXJvbm1lbnQsIGFzIGl0IGlzLCB3aXRoIGFsbCB0aGUgdmFyaWFibGVzLCBkYXRhZnJhbWUsIGV0Yy4gdGhhdCB3ZSBoYXZlIGJlZW4gd29ya2luZyBvbi4gTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIGNvbW1hbmQgZm9yIHRoYXQgcHVycG9zZS4NCg0KIyMjIFNhdmluZyBhbiBlbnZpcm9ubWVudA0KYGBge3J9DQpzYXZlLmltYWdlKGZpbGU9J215SGVyb0Vudmlyb25tZW50LlJEYXRhJykNCmBgYA0KDQoNCiMjIyBMb2FkaW5nIGFuIGVudmlyb25tZW50DQpgYGB7cn0NCgkNCmxvYWQoJ215SGVyb0Vudmlyb25tZW50LlJEYXRhJykNCmBgYA0KVGhpcyBpcyBpdCBmb3IgaW1wb3J0aW5nIGFuZCBleHBvcnRpbmcgZGF0YSBmb3IgdG9kYXksIHdlIHdpbGwgbm93IG1vdmUgdG8gRGF0YSBFeHBsb3JhdGlvbiwgd2hpY2ggaXMgYSBjcml0aWNhbCBzdGVwIGluIG91ciBNYWNoaW5lIExlYXJuaW5nIFBpcGVsaW5lDQoNCg0KDQo=