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 在 数据结构
187 每月下载量
260KB
5.5K SLoC
process_mining
此crate包含过程挖掘的基本数据结构、函数和实用工具。
有关此crate的模块、结构体和函数的完整文档可在 docs.rs/process_mining/ 找到。
由于此crate仍在非常活跃的开发中,因此预期在次要(甚至修补)版本更新中也会有较大的API更改。
特性
- 事件日志
- 事件日志结构体 (
EventLog
) - 快速XES解析(还包括 流式XES导入,内存占用非常低)
- XES导出(也支持流式传输)
- 事件日志结构体 (
- 以对象为中心的事件日志(OCEL 2.0)
- OCEL结构体
- 快速OCEL XML & JSON解析
- Petri网
- PNML导出
- PNML导入
- 图像导出(SVG、PNG等;需要
graphviz-export
功能)
- Alpha+++过程发现
入门
要开始,您可以尝试使用以下代码片段导入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属性,并将结果再次导出为文件,您可以选择
- 导入完整的事件日志,转换,导出
- 流式传输完整的事件日志,就地转换流式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 ) |
---|---|
依赖项
~4–14MB
~179K SLoC