Proses bisnis di perusahaan perusahaan: spekulasi dan kenyataan. Terangkan dengan R

Artikel singkat tentang penambangan proses bisnis dalam konteks meningkatnya minat dalam konsep "kembar digital". Karena kemunculan berkala dari topik ini, saya menganggap pantas untuk berbagi pendekatan terhadap solusi.


Pernyataan masalah


Situasinya sangat sederhana.


  • Ada perusahaan X (Y, Z, ...).
  • Perusahaan memiliki proses bisnis yang diotomatisasi oleh berbagai sistem TI.
  • Ada analis bisnis yang telah menggambar diagram bpmn untuk proses ini. Lebih khusus lagi, "ide bpmn" mereka sendiri tentang bagaimana seharusnya proses ini terlihat.
  • Pengguna bisnis ingin memiliki semacam representasi (KPI) dari proses ini.

Bagaimana cara mendapatkan kebenaran dan menghitung metrik ini?


Ini adalah kelanjutan dari publikasi sebelumnya .


Kami merumuskan tugas dalam persyaratan yang ramah komputer


Postulat dasar:


  • Ada log peristiwa sementara (berbagai log sistem TI, cdr \ xdr, hanya catatan peristiwa dalam database) dari berbagai tingkat kemurnian, kelengkapan dan konsistensi.
  • Sistem TI bertindak sebagai mesin negara dan "berjalan" antara berbagai negara sesuai dengan tindakan pengguna dan logika bisnis yang ditetapkan oleh para programmer di dalamnya.
  • Interaksi pengguna dilakukan dalam bentuk transaksional.

Koreksi dunia fisik:


  • Jumlah perubahan yang dilakukan pada sistem TI sedemikian rupa sehingga diagram bpmn analis bisnis hampir tidak ada hubungannya dengan kenyataan.
  • Data bisa sangat tidak terstruktur (misalnya, log aplikasi).
  • "Transaksional" adalah konsep yang logis. Catatan peristiwa itu sendiri hanya berisi atribut yang melekat di negara ini dan tidak ada pengidentifikasi transaksi ujung ke ujung.
  • Jumlah catatan per hari adalah puluhan, ratusan, ribuan jutaan keping .

Solusi Set-Count


Untuk mengatasi masalah seperti itu perlu:


  • Rekonstruksi transaksi
  • Rekonstruksi proses bisnis nyata
  • membuat perhitungan;
  • menghasilkan hasil dalam format yang dapat dibaca manusia.

Anda dapat mulai mencari solusi vendor dan membayar jutaan. Tapi kami punya R. di tangan kami, ini sangat memungkinkan kami untuk menyelesaikan masalah ini. Pertimbangan singkat di bawah ini.


Semuanya tampak sederhana dan R memiliki paket bupaR yang konsisten dan bagus. Tapi ada lalat di salep yang hadir dan meracuni segalanya. Pengaturan ini dalam waktu yang dapat diterima hanya dapat mengatasi sejumlah kecil peristiwa (ratusan ribu - beberapa juta).
Untuk volume besar, pendekatan lain harus digunakan.


Tambahkan kecepatan!


Meniru dataset input


Untuk mendemonstrasikan ide, perlu untuk membentuk semacam set data uji. Mari kita ambil contoh rantai toko federal sebagai sumber fisik untuk model matematika. Untungnya, ini dapat dimengerti oleh semua orang. Meskipun dengan keberhasilan yang sama dapat berupa ATM, pusat panggilan, transportasi umum, pasokan air, dll.


  • Ada toko-toko dengan berbagai ukuran (kecil, sedang dan besar).
  • Di toko ada meja kas (terminal pos).
  • Nomor toko bisa alfanumerik, nomor terminal bisa digital.
  • Pembeli pergi ke toko dan melakukan pembelian sesuatu sambil membayar dengan kartu.
  • Interaksi terminal pos dengan kartu dan bank dijelaskan oleh serangkaian negara tertentu dan aturan untuk transisi di antara mereka.
  • Transaksi berhasil, tidak berhasil, ditangguhkan dan tidak lengkap (bank tidak tersedia, misalnya).
  • Transaksi memiliki batas waktu.

Ambil rangkaian pola transaksi bisnis berikut ini:


"INIT-REQUEST-RESPONSE-SUCCESS" "INIT-REQUEST-RESPONSE-ERROR" "INIT-REQUEST-RESPONSE-DEFFERED" "INIT-REQUEST" "INIT" 

Untuk menunjukkan pendekatan, kami akan membuat sampel kecil, tetapi semuanya bekerja dengan baik pada miliaran catatan (untuk volume seperti itu tanpa optimasi superdeep, waktu karakteristik diukur hanya dalam ratusan detik pada satu server dengan kinerja sangat biasa-biasa saja).


Spoiler langsung untuk volume besar:


  • di banyak tempat, tidyverse berarti tidyverse tidak bisa mendapatkan jawaban;
  • mengoptimalkan bahkan langkah mikro berguna dan dapat memberikan kontribusi yang signifikan.

Contoh Kode Simulasi
 library(tidyverse) library(datapasta) library(tictoc) library(data.table) library(stringi) library(anytime) library(rTRNG) data.table::setDTthreads(0) #      data.table data.table::getDTthreads() #     set.seed(46572) RcppParallel::setThreadOptions(numThreads = parallel::detectCores() - 1) #   --   -,     #  5   -, 2  --   bo_pattern <- tibble::tribble( #  ,    ,    ~pattern, ~prob, ~mean_duration, "INIT-REQUEST-RESPONSE-SUCCESS", 0.7, 5, "INIT-REQUEST-RESPONSE-ERROR", 0.15, 5, "INIT-REQUEST-RESPONSE-DEFFERED", 0.07, 8, "INIT-REQUEST", 0.05, 2, "INIT", 0.03, 0.5 ) #    +     checkmate::assertTRUE(sum(bo_pattern$prob) == 1) df <- bo_pattern %>% separate_rows(pattern) %>% #   mutate(coeff = sum(prob)) %>% group_by(pattern) %>% #    summarise(event_prob = sum(prob/coeff)*100) %>% ungroup() checkmate::assertTRUE(sum(df$event_prob) == 100) #   3  :  (4 ),  (12 ),  (30 ) df1 <- tribble( ~type, ~n_pos, ~n_store, "small", 4, 10, "medium", 12, 5, "large", 30, 2 ) %>% #       mutate(store = map2(row_number(), n_store, ~sample(x = .x * 1000 + 1:.y, size = .y, replace = FALSE))) %>% unnest(store) %>% #       mutate(pos = map(n_pos, ~sample(x = .x, size = .x, replace = FALSE))) %>% unnest(pos) %>% mutate(pattern = sample(bo_pattern$pattern, n(), replace = TRUE, prob = bo_pattern$prob)) tic("Generate transactions") #     ,      #         ,       df2 <- df1 %>% #         select(-matches("duration")) %>% left_join(bo_pattern, by = "pattern") %>% #   sample_frac(size = 200, replace = TRUE) %>% mutate(duration = rnorm(n(), mean = mean_duration, sd = mean_duration * .25)) %>% select(-prob, -mean_duration) %>% #   ,      >  #    30  filter(duration > 0.5 & duration < 30) %>% #    POS       mutate(session_id = row_number()) %>% #     ,       separate_rows(pattern) %>% rename(event = pattern) toc() tic("Generate time markers, data.table way") samples_tbl <- data.table::as.data.table(df2) %>% # setkey(session_id, duration, physical = FALSE) %>% #           # 1-       , ,      5  # .[, ticks := base::sort(runif(.N, 5, 5 + duration)), by = .(session_id, duration)] %>% #          match.arg   base::order!! #     #       0  1     #     # .[, tshift := runif(.N, 0, 1)] %>% #    trng     (    ) # ,           .[, trand := runif_trng(.N, 0, 1, parallelGrain = 100L) * duration] %>% #      ,      # .[, ticks := sort(tshift), by = .(session_id)] %>% #  ,     session_id,   ,     .[, t_idx := session_id + trand / max(trand)/10] %>% #         # session_id            .  .[, tshift := (sort(t_idx) - session_id) * 10 * max(trand)] %>% #   ,     POS     (60 ) .[event == "INIT", tshift := tshift + runif_trng(.N, 0, 60, parallelGrain = 100L)] %>% #     .[, `:=`(duration = NULL, trand = NULL, t_idx = NULL, n_store = NULL, n_pos = NULL, timestamp = as.numeric(anytime("2019-03-11 08:00:00 MSK")))] %>% #     ,   01.03.2019     .[, timestamp := timestamp + cumsum(tshift), by = .(store, pos)] %>% #      .[timestamp <= as.numeric(anytime("2019-04-11 23:00:00 MSK")), ] %>% #           .[, timestamp := anytime(timestamp, tz = "Europe/Moscow")] %>% as_tibble() %>% select(store, pos, event, timestamp, session_id) toc() 

Untuk kemurnian percobaan, kami hanya menyisakan parameter signifikan dan mencampur semuanya. Dalam kehidupan nyata, masih perlu untuk secara acak membuang bagian dari fragmen (mungkin dalam blok waktu yang terpisah), dengan demikian meniru kerugian dalam menerima data.


 #   log_tbl <- samples_tbl %>% select(store, pos, state = event, timestamp_msk = timestamp) %>% sample_n(n()) #   log_tbl %>% mutate(timegroup = lubridate::ceiling_date(timestamp_msk, unit = "10 mins")) %>% ggplot(aes(timegroup)) + # geom_bar(width = 0.7*600) + geom_bar(colour = "white", size = 1.3) + theme_bw() 


Kami menggambarkan diagram proses dengan gambar


Perhitungan pada `data.frame` asli


dan distribusi negara
visualisasi menggunakan `bupaR`


Fluktuasi ringan disebabkan oleh fakta bahwa tabel dipertimbangkan di awal (termasuk dalam kode), dan bupaR::process_map bekerja pada akhirnya ketika beberapa data yang dihasilkan secara acak yang tidak sesuai dengan kendala integral terpotong oleh elemen penyaringan.


Rekonstruksi Transaksi


Hal pertama yang biasanya ditawarkan ketika Anda harus mengumpulkan / membongkar / membandingkan deret waktu adalah pengelompokan dan siklus perbandingan. Dalam demo dengan 100 entri, kenaikan ini akan berhasil, tetapi jutaan daftar tidak akan. Untuk mengatasi tugas ini, Anda perlu melokalkan titik-titik kehilangan waktu (loop internal, alokasi memori menengah dan menyalin) dan mencoba untuk menghilangkan mereka ke minimum.


Akibatnya, masalah ini dapat dikurangi menjadi sepuluh baris.


kode rekonstruksi transaksi
 clean_dt <- as.data.table(log_tbl) %>% #     INIT .[, start := (state == "INIT")] %>% #  session_id      ,  #             .[, event_date := lubridate::as_date(timestamp_msk)] %>% .[, date_str := format(.BY[[1]], "%y%m%d"), by = event_date] %>% #            # timestamp_msk    setorder(store, pos, timestamp_msk) %>% #     --              .[, session_id := paste(date_str, store, pos, cumsum(start), sep = "_")] %>% #        ( 30 ) # .[, time_shift := timestamp_msk - shift(timestamp_msk), by = .(store, pos)] %>% #        ,   INIT .[, time_locf := cummax(as.numeric(timestamp_msk) * as.numeric(start)), by = .(store, pos)] %>% .[, time_shift := as.numeric(timestamp_msk) - time_locf] %>% #   ,       30  .[, lost_chain := time_shift > 30] %>% # .[, time_shift := as.numeric(!start) * as.numeric(timestamp_msk - shift(timestamp_msk, fill = 0))] %>% # INIT    # .[, time_accu := cumsum(time_shift)] %>% .[, date_str := NULL] #          #    tidyverse  ,      dt <- as.data.table(clean_dt) %>% #     !!! .[lost_chain != TRUE] %>% #        1-    .[order(timestamp_msk, store, pos)] %>% .[, bp_pattern := stri_join(state, collapse = "-"), by = session_id] #     as_tibble(dt) %>% distinct(session_id, bp_pattern) %>% count(session_id, sort = TRUE) 

Dalam beberapa detik, kami memiliki gambaran proses bisnis yang telah direkonstruksi.


Dan (siapa sangka !!!), ternyata proses bisnis terotomatisasi dalam sistem TI bekerja agak berbeda (atau tidak sama sekali) karena analis bisnis meyakinkan semua orang. Keajaiban dan argumen "pemilik proses" akan menemani studi gambar akhir.


Terapkan trik secara aktif


Ketika kecepatan komputasi menjadi kuantitas penting, menulis kode kerja tidak cukup. Perlu memperhatikan semua tingkatan. Ada juga sejumlah trik algoritmik yang dapat secara signifikan mengurangi waktu eksekusi.


Secara khusus, dalam tugas ini kita dapat menyebutkan hal berikut:


  1. Untuk pemrosesan utama, hanya data.table (kecepatan, kerja pada tautan), + akuntansi untuk optimasi kueri internal.
  2. POSIXct dapat berisi milidetik (meskipun tidak ditampilkan secara normal, tetapi dapat diperbaiki dengan options(digits.secs=X) ), kami menyembunyikannya di sana, akan lebih mudah untuk membandingkan dan mengurutkan.
  3. Hindari penyortiran fisik di dalam kelompok! Penyortiran fisik tunggal dari seluruh vektor memastikan penyortiran data dalam kelompok.
  4. Hindari komputasi dalam kelompok. Kami mencoba melakukan semua yang mungkin pada data sumber (kami menerapkan vektorisasi, mengurangi faktur untuk panggilan fungsi).
  5. Kami menggunakan batas waktu transaksi untuk menangani kesenjangan waktu.
  6. Metode locf (Last Observation Carried Forward) lambat. Untuk mentransfer properti pada timeline, gunakan cumsum , cummax .
  7. Operasi yang memakan waktu, seperti POSIX -> konversi string, pencarian reguler, dll. Kami tidak melakukannya elemen demi elemen, tetapi pada konvolusi. Overhead pengindeksan internal dan pengelompokan bidang yang dikonversi jauh lebih kecil.
  8. Kami secara aktif menggunakan multithreading (termasuk paket intra).
  9. Jangan abaikan optimasi mikro. Sebagai contoh, stri_c beberapa kali lebih cepat dari paste0 .

 #  1 log <- getLog(fileName) bench::mark( paste0 = paste0(log$value, collapse = "\n"), stringi = stri_c(log$value, collapse = "\n") ) # # A tibble: 2 x 13 # expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time # <bch:expr> <bch:> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> # 1 paste0 58ms 59.1ms 16.9 496KB 0 9 0 533ms # 2 stringi 16.9ms 17.5ms 57.1 0B 0 29 0 508ms 

Posting sebelumnya - pisau pemrosesan json Swiss .

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


All Articles