#事件日志 #过程 #挖掘 #事件流 #数据 #导入导出 #xes

process_mining

用于处理(以对象为中心)事件数据的过程挖掘库

36 个版本

0.3.14 2024年7月8日
0.3.4 2024年5月24日
0.2.7 2024年3月28日
0.1.1 2023年12月16日

#220数据结构

Download history 141/week @ 2024-04-16 76/week @ 2024-04-30 70/week @ 2024-05-07 2/week @ 2024-05-14 696/week @ 2024-05-21 18/week @ 2024-05-28 147/week @ 2024-06-04 10/week @ 2024-06-11 269/week @ 2024-06-18 762/week @ 2024-06-25 176/week @ 2024-07-02 53/week @ 2024-07-09 2/week @ 2024-07-23 45/week @ 2024-07-30

187 每月下载量

MIT/Apache

260KB
5.5K SLoC

process_mining

此crate包含过程挖掘的基本数据结构、函数和实用工具。

有关此crate的模块、结构体和函数的完整文档可在 docs.rs/process_mining/ 找到。

由于此crate仍在非常活跃的开发中,因此预期在次要(甚至修补)版本更新中也会有较大的API更改。

特性

入门

要开始,您可以尝试使用以下代码片段导入XES事件日志

use process_mining::{import_xes_file, XESImportOptions};

let log_res = import_xes_file("log.xes", XESImportOptions::default());

match log_res {
    Ok(log) => {
      println!("Imported event log with {} traces", log.traces.len())
    },
    Err(e) => {
      eprintln!("XES Import failed: {:?}", e)
    },
}

更多信息

完整代码示例展示

use std::{fs::File, io::BufReader, time::Instant};

use process_mining::{
    event_log::{
        activity_projection::EventLogActivityProjection,
        constants::ACTIVITY_NAME,
        export_xes::{export_xes_event_log_to_file_path, export_xes_trace_stream_to_file},
        import_xes::{build_ignore_attributes, XESImportOptions},
        stream_xes::stream_xes_from_path,
    },
    import_ocel_xml_file, import_xes_file, OCEL,
};

fn main() {
    let xes_path = "./src/event_log/tests/test_data/BPI Challenge 2018.xes.gz";

    // Parsing XES
    println!("==Parsing XES==");

    // Default XES parsing
    let now = Instant::now();
    let log = import_xes_file(xes_path, XESImportOptions::default()).unwrap();
    println!(
        "Parsed XES with {} cases in {:#?}",
        log.traces.len(),
        now.elapsed()
    );

    // Streaming XES Parsing (only counting number of traces)
    // Streaming enables very low memory consumption and sometimes also faster processing
    let now = Instant::now();
    let (mut trace_stream, _log_data) =
        stream_xes_from_path(xes_path, XESImportOptions::default()).unwrap();
    println!(
        "Streamed XES counting {} traces in {:#?} ",
        trace_stream.count(),
        now.elapsed()
    );

    // Streaming XES Parsing (constructing a primitive [EventLogActivityProjection])
    // Streaming enables very low memory consumption and sometimes also faster processing
    let now = Instant::now();
    let st_res = stream_xes_from_path(
        xes_path,
        XESImportOptions {
            ignore_event_attributes_except: Some(build_ignore_attributes(vec![ACTIVITY_NAME])),
            ignore_trace_attributes_except: Some(build_ignore_attributes(Vec::<&str>::new())),
            ignore_log_attributes_except: Some(build_ignore_attributes(Vec::<&str>::new())),
            ..XESImportOptions::default()
        },
    );
    match st_res {
        Ok((mut st, _log_data)) => {
            let projection: EventLogActivityProjection = (&mut st).into();
            if let Some(e) = st.check_for_errors() {
                eprintln!("Error: {}", e);
            }
            println!(
                "Streamed XES into Activity Projection ({} variants) in {:#?} (Only parsing concept:name event attributes)",
                projection.traces.len(),
                now.elapsed()
            );
        }
        Err(e) => {
            eprintln!("Error while streaming parsing: {}", e);
        }
    }

    // Writing XES
    println!("\n==Writing XES==");

    // Streaming: Stream-parsing XES and stream-writing XES to .xes.gz (with very low memory footprint!)
    let now = Instant::now();
    let (mut stream, log_data) =
        stream_xes_from_path(xes_path, XESImportOptions::default()).unwrap();
    let file = File::create("/tmp/streaming-export.xes.gz").unwrap();
    export_xes_trace_stream_to_file(stream.into_iter(), log_data, file, true).unwrap();
    println!("Streamed from .xes to .xes.gz in {:?}", now.elapsed());
    std::fs::remove_file("/tmp/streaming-export.xes.gz").unwrap();

    // First Parsing XES completely, then writing XES to .xes.gz file
    let now = Instant::now();
    let log = import_xes_file(xes_path, XESImportOptions::default()).unwrap();
    export_xes_event_log_to_file_path(&log, "/tmp/non-streaming-export.xes.gz").unwrap();
    println!("Read .xes & Wrote to .xes.gz in {:?} total", now.elapsed());
    std::fs::remove_file("/tmp/non-streaming-export.xes.gz").unwrap();


    // Parsing XML OCEL files:
    println!("\n==Parsing XML OCEL==");

    let now = Instant::now();
    let ocel = import_ocel_xml_file("./src/event_log/tests/test_data/order-management.xml");
    println!(
        "Imported OCEL2 XML with {} objects and {} events in {:#?}",
        ocel.objects.len(),
        ocel.events.len(),
        now.elapsed()
    );

    // Parsing JSON OCEL files
    println!("\n==Parsing JSON OCEL==");

    let now = Instant::now();
    let ocel: OCEL = serde_json::from_reader(BufReader::new(
        File::open("./src/event_log/tests/test_data/order-management.json").unwrap(),
    ))
    .unwrap();
    println!(
        "Imported OCEL2 JSON with {} objects and {} events in {:#?}",
        ocel.objects.len(),
        ocel.events.len(),
        now.elapsed()
    );
}

示例输出

==Parsing XES==
Parsed XES with 43809 cases in 12.643408724s
Streamed XES counting 43809 traces in 11.814082231s 
Streamed XES into Activity Projection (28457 variants) in 7.366106006s (Only parsing concept:name event attributes)

==Writing XES==
Streamed from .xes to .xes.gz in 22.778810621s
Read .xes & Wrote to .xes.gz in 20.944550225s total

==Parsing XML OCEL==
Imported OCEL2 XML with 10840 objects and 21008 events in 101.156023ms

==Parsing JSON OCEL==
Imported OCEL2 JSON with 10840 objects and 21008 events in 108.422759ms

XES导入/导出:正常与流式传输

对于事件日志的导入/导出,可以使用正常API(例如,[import_xes_file])或流式API(例如,[stream_xes_from_path])。这里的 流式传输 指的是支持 遍历跟踪的迭代器

在内部,XES导入导出功能仅实现了流式版本。正常的API在底层使用流式实现,为常见情况提供了方便的包装器(例如,将XES文件作为完整的[EventLog]结构导入内存)。

在解析时,最初只解析包含日志级别信息的输入XES文件的一部分(具体来说:解析将在第一个trace之前停止)。其余部分由迭代器包装,并且只有在下一个trace可用时才会懒惰地解析。

在大多数情况下,正常API和流式API之间的性能差异不大。然而,在导入大量数据时,内存消耗有显著差异:流式导入导出函数仅使用日志级别信息和一条trace的信息(即,最后解析的那条trace)。因此,流式方法也允许读取和写入不适合系统内存的XES事件日志。

例如,如果您想转换从大型XES文件中读取的事件日志的trace属性,并将结果再次导出为文件,您可以选择

  1. 导入完整的事件日志,转换,导出
  2. 流式传输完整的事件日志,就地转换流式trace,导出结果trace流

在这种情况下,流式传输显然是一个更好的选择,因为可以轻松地单独转换trace。

内存消耗差异

对于BPI Challenge 2018事件日志XES文件(作为压缩的.xes.gz为150.9 MiB或作为普通.xes为1.8 GB),解析整个日志然后导出到.xes.gz文件时,峰值内存消耗高达3.3 GB。当使用XES导入导出的流式函数时,内存消耗峰值仅为5.7 MiB

无流式传输的内存使用(3.3GB 流式传输的内存使用(5.7MiB
Plot of Heap usage with a peak at 3.3GB Plot of Heap usage with a peak at 5.7MiB

依赖项

~4–14MB
~179K SLoC