استخدام R للمهام فائدة

تتيح لك الأداة الجيدة + توفر المهارات اللازمة للعمل معها ، والتي يتم تحقيقها من خلال الممارسة العملية ، حل العديد من المهام غير التقليدية المختلفة مثل "بسهولة". فيما يلي بعض الأمثلة المتشابهة. أنا متأكد من أن الكثيرين يمكنهم توسيع هذه القائمة.


إنه استمرار للمنشورات السابقة .


تحليلات سجل التطبيق


شعبية جدا هي مهمة إجراء العمليات الحسابية التحليلية على أساس سجلات التطبيق. على سبيل المثال ، قم بإجراء تحليل لإجراءات المستخدم وتقدير مؤشرات التنبؤ ، أو اختبار الفرضيات. يمكنك اتباع الإصدار الكلاسيكي ورفع مكدس ELK أو ما شابه ذلك (في الآونة الأخيرة ، انقطعت Splunk عن الأنظمة المتوفرة في روسيا). ولكن يمكنك التفكير قليلاً وبسرعة في القيام بكل شيء على R. بسرعة بكل معنى الكلمة ، سواء في التنفيذ أو في وقت المعالجة.


ولكن هناك عددًا من الميزات عند حل مشكلة مماثلة:


  1. عادةً ، تتم كتابة ملفات السجل بتنسيق log4j الكلاسيكي: الطابع الزمني ، الأهمية ، نوع النظام الفرعي ، نص الرسالة.
  2. قد يحتوي الطابع الزمني على أحداث بدقة ميلي ثانية واحدة ، والتي يجب الحفاظ عليها للتأكد من دقة التحليلات اللاحقة. يمكن للملي ثانية الكتابة دون الامتثال لـ ISO 8601.
  3. نص الرسالة هو كيان غير منظم عمليا. يكتب المطورون كل ما يعتبرونه ضروريًا هناك ، دون حصر أنفسهم في أي تنسيقات تقديمية.
  4. في بعض الأحيان يكون نص الرسالة متعدد الأسطر ، على سبيل المثال ، إخراج مكدس java call ، أو حزمة تبادل intersystem xml. من الضروري إعادة إنشاء التسجيلات متعددة الخطوط في سجل واحد (علامة الطابع الزمني هي علامة على بداية التسجيل).
  5. يمكن أن يكون عدد من وحدات الإرسال خارج المحتوى ويجب أن يتم الحصول عليه بطريقة مختلفة ، على سبيل المثال ، يمكن ترميز معرف الكائن باسم ملف السجل.
  6. يمكن أن تكون السجلات في شكل ملفات عدة ميغابايت أو مئات غيغا بايت.
  7. المهمة موازية بشكل جيد للغاية.

في الواقع ، يمكن تقسيم المهمة إلى خطوتين:


  • معالجة البيانات الأولية
  • تحليلات لاحقة.

يتم تحديد محتوى الخطوة الأخيرة حسب مجال الموضوع ومهام العمل ، R مناسب بشكل مثالي لهذه الخطوة. لا يعرف الكثير من الأشخاص ، ولكن يمكن أيضًا حل الخطوة الأولى بسهولة مع R. علاوة على ذلك ، ووفقًا لحجم ملفات السجل ، يمكن إضافة نتيجة سابقة التجهيز منظمة جزئيًا مناسبة لمزيد من التحليل إلى الملفات وكذلك إلى قاعدة البيانات. تيرابايت طحن واحد أو اثنين.


مجرد رمز مثال:
 library(readr) library(tidyverse) library(magrittr) library(stringi) library(fs) library(glue) library(RClickhouse) library(DBI) library(anytime) library(tictoc) library(iterators) library(foreach) library(doParallel) library(futile.logger) library(re2r) library(data.table) library(future) library(doFuture) common_logname <- "DEV_log_parser.log" table_name <- "DEV_LOGS" flog.appender(appender.file(common_logname)) flog.threshold(INFO) flog.info("Start batch processing") oneTimeProcessing <- function(f_iter, log_type = c("app", "system")) { log_type <- match.arg(log_type) checkmate::assertNames(names(f_iter), permutation.of = c("fname", "short_fname", "location", "wk", "size", "id")) cfg <- list(app = list(db_table = "DEV_APP_LOGS"), system = list(db_table = "DEV_LOGS")) #   data <- readr::read_lines(file = f_iter$fname, progress = FALSE) log_df <- setDT(tibble::enframe(data, name = NULL)) %>% .[, log_line_start := re2r::re2_detect(value, pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}", parallel = F)] %>% .[, log_line_number := cumsum(log_line_start)] %>% .[, body := stri_c(value, collapse = "\n"), by = log_line_number] %>% .[, `:=`(value = NULL, log_line_start = NULL, log_line_number = NULL)] %>% tibble::as_tibble() %>% #  body = character(0)      0  #      POSIXct tidyr::extract(col = "body", into = c("timestamp", "tz", "level", "module", "class", "message"), # tz   (  DEV),      ( DEV) regex = "^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}:\\d+([+-]\\d+)?) (.*?) <(.*?)> \\[(.*?)\\] (?s:(.*))$", case_insensitive = TRUE, ignore.case = TRUE) %>% #     ISO         (   ?) #  ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601) mutate_at("timestamp", re2r::re2_replace, # tz   (  DEV),      ( DEV) pattern = "(.*) (\\d{2}:\\d{2}:\\d{2}):(\\d+([+-]\\d+)?)", replacement = "\\1T\\2.\\3") %>% mutate_at("timestamp", lubridate::as_datetime, tz = "Europe/Moscow") %>% #    mutate(location = f_iter$location, wk = f_iter$wk) # TRUNCATE  CH    ,           #    CH, ms    (timestamp %% 1) conn <- DBI::dbConnect(RClickhouse::clickhouse(), host = "10.0.0.1", db = "DEV_LOGS") # m <- DBI::dbExecute(conn, glue("ALTER TABLE {table_name}")) write_res <- log_df %>% mutate(ms = (as.numeric(timestamp) %% 1) * 1000) %>% select(location, wk, timestamp, ms, level, module, class, message) %>% #            DBI::dbWriteTable(conn, cfg[[log_type]][["db_table"]], ., append = TRUE) DBI::dbDisconnect(conn) #       res <- tibble::tibble(id = f_iter$id, lines = nrow(log_df), min_t = min(log_df$timestamp), max_t = max(log_df$timestamp), write_res) rm(data, log_df) return(res) } #    tic("Batch processing") #    gc(full = TRUE) nworkers <- parallel::detectCores() - 1 registerDoFuture() # future::plan(multiprocess) # future::plan(multisession) future::plan(multisession, workers = nworkers) # future::plan(sequential) #  ~  #      CH #   ------------------ fnames_tbl <- here::here("raw_data") %>% fs::dir_ls(recurse = TRUE, glob = "*dev_app*.gz") %>% enframe(name = "fname") %>% #         mutate(short_fname = as.character(fs::path_rel(fname, start = "./raw_data"))) %>% select(-value) %>% mutate(size = fs::file_size(fname)) %>% tidyr::extract(col = "short_fname", into = c("location", "wk"), regex = "^([^/]+)/wk(\\d+)", remove = FALSE) %>% arrange(size) %>% mutate(id = paste(format(row_number(), justify = "r", width = 4), "/", n())) %>% #   ~ N  mutate(chunk = (row_number() %% nworkers + 1)) %>% #    ,  dopar    arrange(chunk) start_time <- Sys.time() stat_list <- foreach(it = iter(fnames_tbl, by = "row"), .export = c("start_time"), .verbose = TRUE, .inorder = FALSE, .errorhandling = "remove") %dopar% { #   flog.appender(appender.file(common_logname)) # flog.info(capture.output(gc(verbose = TRUE))) res <- oneTimeProcessing(it, log_type = "app") flog.info(glue("Step {it$id} finished.", "Elapsed {round(difftime(Sys.time(), start_time, units = 'mins'), digits = 2)} min(s) ----------->", .sep = " ")) return(res) } flog.info("Load finished") #    -------------- #    ,    future::plan(sequential) gc(reset = TRUE, full = TRUE) flog.info(capture.output(toc())) #     ------------- logstat_tbl <- stat_list %>% dplyr::bind_rows() %>% #    left_join(fnames_tbl, by = "id") %>% #          mutate(delta_t = as.numeric(difftime(max_t, min_t, units = "mins"))) %>% arrange(min_t) write_delim(logstat_tbl, here::here("output", "DEV_parse_stat.csv.gz"), delim = ";") # ,     ? if(nrow(logstat_tbl) < nrow(fnames_tbl)){ flog.error("!!!!!!! Not all workers were executed successfully !!!!!!!!!") } 

يحتوي مثال التعليمات البرمجية هذا على مفاهيم أساسية مثل التوازي ومعالجة الوقت مع مراعاة أجزاء من الثانية والحفظ في قاعدة البيانات والمحاسبة للسجلات متعددة الخطوط وتلخيص نتائج العمل واستخدام السمات الخارجية ووضع المعايير الأولية واختيار الوظائف والحزم المثلى ( re2r ، على سبيل المثال ؛ هذا مكتبة google للتعامل مع البرامج العادية هي الأسرع وتستخدم كثيرًا ، واتخاذ نفس ClickHouse المذكور في الكود { bencmark ، قد يكون بعض المشغلين قد أغلقوا ILV}). لكن الكود لا يدعي أنه مثالي ، لأنه مجرد إجراء لمرة واحدة بشأن معالجة البيانات المسبقة. انها تفعل بسرعة وبشكل صحيح ، حسنا ، حسنا. لمهمة أخرى مماثلة ، ونحن تصحيح ، مع الأخذ بعين الاعتبار resp. إدخال البيانات.


هل سيكون الحصول على النتيجة النهائية بلغات أخرى أسرع بشكل لافت للنظر؟ السؤال مفتوح. إصدارات متوازية مع python ، perl ، awk لم تظهر أي اختلافات واضحة. من الممكن أن يحقق المعلم الموجود في python نتائج أفضل ، لكن لا تنسَ أن هذه مهمة دورية "لمرة واحدة".


استعادة النظام في الصور


بعد رحلة مع العديد من الأجهزة في متناول اليد ، يجب عليك جمع كل الصور معًا وترتيبها بطريقة أو بأخرى قبل إجراء مزيد من المعالجة. أحد أفضل الخيارات هو تسمية الملفات حسب تاريخ التصوير ( YYYY-MM-DD hh_mm_ss ) ، وبالتالي ضمان ترتيب الصورة على سهم الوقت. تساعد سمات Exif في حل هذه المشكلة في خطوة واحدة.


ويمكن القيام بذلك أيضًا باستخدام R في "بضعة أسطر". exifr و exifr للمساعدة.


  • قدمت قائمة من الملفات.
  • انسحبت الصفات ؛
  • نسخ الملفات مع إعادة تسمية لجنة التنسيق الإدارية. مع الصفات الصحيحة.

في الواقع ، تم تقليص المهمة إلى سابقتها ، ويتم تجميع السمات فقط ليس من خلال اسم الملف ، ولكن من خلال سمات exif الخاصة بها ، وعند المعالجة هناك ببساطة نسخ الملف مع إعادة التسمية. الهيكل العظمي للنص ومنطق العمل لم يتغير.


عينة رمز اليد السريعة:
 library(tidyverse) library(magrittr) library(stringi) library(lubridate) library(stringi) library(fs) library(glue) library(futile.logger) library(anytime) library(tictoc) library(bench) library(exifr) library(tictoc) input_path <- "S:/ " %>% fs::path_real() #       output_path <- "S:/ " %>% fs::path_real() i_fnames <- input_path %>% fs::dir_ls(recurse = TRUE, regexp = "(JPG|jpg)$") raw_df <- read_exif(i_fnames, tags = c("SourceFile", "Model", "DateTimeOriginal")) %>% #      base64, ,    mutate(tmp = sub("^base64:(.*)", "\\1", SourceFile)) %>% mutate(i_fname = purrr::map_chr(tmp, ~rawToChar(jsonlite::base64_dec(.)))) %>% mutate(tm = anytime::anytime(DateTimeOriginal)) %>% select(i_fname, DateTimeOriginal, model = Model, tm) #         clean_df <- raw_df %>% mutate(timestamp = case_when( model == 'iPhone ...' ~ tm, model == 'Nikon ...' ~ tm - lubridate::minutes(56), model == 'Samsung ...' ~ tm - lubridate::minutes(62), TRUE ~ tm) ) %>% mutate_at("i_fname", fs::path_real) %>% mutate(fname = format(timestamp, format = '%Y-%m-%d %H_%M_%S')) %>% #  ,     (""),      mutate(fname = dplyr::coalesce(fname, fs::path_ext_remove(fs::path_file(i_fname)))) %>% #  ,         group_by(fname) %>% mutate(n = n(), idx = row_number()) %>% ungroup() %>% #        mutate(fname = case_when( n > 1 ~ stri_c(fname, '_', idx), TRUE ~ fname ) ) %>% mutate(o_fname = fs::path(!!output_path, paste0(fname, ".jpg"))) #     janitor::get_dupes(clean_df, o_fname) #   tic(" ") clean_df %$% # purrr::walk2(i_fname, o_fname, ~print(glue("{.x} -> {.y}"))) purrr::walk2(i_fname, o_fname, ~fs::file_copy(.x, .y, overwrite = TRUE)) toc() #              

لماذا exifr ؟ لأنه ExifTool الأداة المساعدة ExifTool القوية عبر النظام الأساسي.


ربما تبدو المهمة اصطناعية ، وهو أمر يصعب الجدال معه ، نظرًا لوجود الكثير من الأدوات المساعدة المختلفة ومعرفات المستخدم الرسومية للعمل مع Exif وإعادة التسمية ، ولكن هناك فارق بسيط. لا تستطيع جميع الأجهزة التقاط المنطقة الزمنية التي تم تغييرها وضبط الوقت (الكاميرات ، على سبيل المثال ، كم عدد المرات التي يحدد فيها المستخدم الكاميرا الوقت بالضبط عليها؟) ، لذا أثناء إعادة التسمية ، ستحتاج أيضًا إلى تحويل الطوابع الزمنية وفقًا للمصدر.


اكتمال


هناك العديد من المشكلات المشابهة ، يمكن حل الكثير منها بمساعدة R أيضًا.


المنشور السابق - "الأطفال والرياضيات والبحث العلمي" .

Source: https://habr.com/ru/post/ar464849/


All Articles