【Pm4py第六讲】关于合规性检查

合规性检查,也叫一致性检查。pm4py合规性检查模块包含在pm4py中实现的合规性检查算法,比如基于Token重演的,基于对齐的,基于足迹矩阵的拟合度和精确度指标的计算。

1.函数概述

 本次主要介绍Pm4py中一些常见的合规性检查函数,总览如下表:

函数名说明
check_is_fitting(*args[, activity_key])检查轨迹是否拟合流程模型
conformance_declare(log, declare_model[, ...])对DECLARE模型应用合规性检查
conformance_diagnostics_alignments(log, *args)在日志和流程模型之间应用对齐算法
conformance_diagnostics_footprints(*args)使用足迹矩阵提供合规性检查诊断
conformance_diagnostics_token_based_replay(...)应用基于token重演方法进行一致性检查分析。
conformance_log_skeleton(log, log_skeleton)使用日志skeleton执行一致性检查
conformance_temporal_profile(log, ...[, ...])使用提供的时态配置文件在提供的日志上执行一致性检查。
fitness_alignments(log, petri_net, ...[, ...])使用alignment计算拟合度
fitness_footprints(*args)使用足迹矩阵计算拟合度
fitness_token_based_replay(log, petri_net, ...)使用基于token方法重演计算拟合度。
precision_alignments(log, petri_net, ...[, ...])计算模型的精确度
precision_footprints(*args)使用足迹矩阵计算精确度
precision_token_based_replay(log, petri_net, ...)使用基于token重演方法计算精确度
replay_prefix_tbr(prefix, net, im, fm[, ...])使用基于token重演方法在给定的接受Petri网上重演前缀(活动列表)。

2.函数方法介绍

2.1 基于Token重演方法的合规性检查

pm4py.conformance.conformance_diagnostics_token_based_replay(log: Union[EventLog, DataFrame]petri_net: PetriNetinitial_marking: Markingfinal_marking: Markingactivity_key: str = 'concept:name'timestamp_key: str = 'time:timestamp'case_id_key: str = 'case:concept:name'return_diagnostics_dataframe: bool = Falseopt_parameters: Optional[Dict[Any, Any]] = None) → List[Dict[str, Any]]

说明:将基于token重演方法应用于一致性检查分析。这些方法返回完整的基于token重演诊断。
token重演方法从初始库所开始匹配轨迹和Petri网模型,以发现执行了哪些变迁,以及给定流程实例在哪些库所有剩余或丢失的令牌。基于token重演方法对一致性检查很有用:事实上,如果在轨迹执行过程中,可以在不需要插入任何丢失的令牌的情况下触发变迁,则轨迹会根据模型进行拟合。如果强制达到最终标记,那么如果轨迹达到最终标记而没有任何丢失或剩余的标记,则该轨迹是拟合的。

trace_is_fit:当轨迹根据模型时为True的布尔值(True/False)。
activated_transitions:通过基于Token的重演在模型中使能的变迁列表。
reached_marking:在重演结束时达到的标记。
missing_tokens:丢失的令牌数。
consumed_tokens:已消费的令牌数量。
remaining_tokens:剩余的令牌数。
produced_tokens:生成的令牌数。

输入参数:

     log–事件日志
    petri_net(PetriNet)–petri网
    initial_marking(标记)–初始标记
    final_marking(标记)–最终标记
    activity_key(str)–要用于活动的属性
    timestamp_key(str)–用于时间戳的属性
     case_id_key(str)–要用作案例标识符的属性
     return_diagnosis_dataframe(bool)–如果可能,返回一个带有诊断的数据帧(而不是通常的输出)
      opt_parameters–基于Token重演方法的可选参数,包括:          *reach_mark_through_hidden:决定我们是否应尝试通过静默变迁到达最终标记的布尔值*stop_immediate_unfit:决定我们在检测到不符合项时是否应立即停止的布尔值*walk_throuh_hidden_trans:决定是否应通过隐藏转换来实现可见变迁的布尔值。*places_shortest_path_by_hidden:隐藏转换的位置之间的最短路径

*is_reduction:表示是否在减少尝试中调用基于Token的重演

*thread_maximum_ex_time:对齐线程允许的最大执行时间

*cleanig_token_flood:决定是否应操作令牌洪泛的清除

*disable_variants:disable variations grouping*return_object_names:决定是否返回名称而不是对象指针

返回对象

      List[Dict[str, Any]]

示例代码:

import pm4py

net, im, fm = pm4py.discover_petri_net_inductive(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
tbr_diagnostics = pm4py.conformance_diagnostics_token_based_replay(dataframe, net, im, fm, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

2.2 基于对齐方法的合规性检查

pm4py.conformance.conformance_diagnostics_alignments(log: Union[EventLog, DataFrame], *args, multi_processing: bool = False, activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name', variant_str: Optional[str] = None, return_diagnostics_dataframe: bool = False) → List[Dict[str, Any]]
说明:在日志和流程模型之间应用对齐算法。这些方法返回完全对齐诊断。
基于对齐的重演旨在找到轨迹和模型之间的最佳对齐方式之一。对于每个轨迹,对齐的输出是一个耦合列表,其中第一个元素是(轨迹的)事件或»,第二个元素是变迁(模型的)或»。对于每对元素,可以提供以下分类:
        同步移动:事件的分类对应于变迁标签;在这种情况下,轨迹和模型在回放过程中都以相同的方式前进。
        在日志上移动:对于第二个元素为»的每一对元素,它对应于轨迹中未在模型中模拟的回放移动。这种移动是不合适的,并且表示轨迹和模型之间存在偏差。
        模型上移动:对于第一个元素为»的每一对元素,它对应于模型中未在轨迹中模仿的回放移动。对于模型上的移动,我们可以有以下区别:
          在涉及静默变迁的模型上移动:在这种情况下,即使不是同步移动,移动也是合适的。
          在不涉及静默变迁的模型上移动:在这种情况下,移动是不合适的,并且表示轨迹和模型之间存在偏差。
          对于每个轨迹,都会关联一个字典,其中包含以下信息:
           对齐:包含对齐(同步移动、日志移动、模型移动)成本:根据提供的成本函数拟合度包含对齐的成本:如果轨迹完全拟合,则等于1。

输入参数

     log–事件日志
     args–流程模型的规范
     multi_prrocessing(bool)–启用多处理的布尔值
     activity_key(str)–要用于活动的属性
     timestamp_key(str)–用于时间戳的属性
     case_id_key(str)–要用作案例标识符的属性
     variant_str–变量规范(用于Petri网比对)
     return_diagnosis_dataframe(bool)–如果可能,返回一个带有诊断的数据帧(而不是通常的输出)

返回对象

        List[Dict[str, Any]]

示例代码:

import pm4py

net, im, fm = pm4py.discover_petri_net_inductive(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
alignments_diagnostics = pm4py.conformance_diagnostics_alignments(dataframe, net, im, fm, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

2.3 基于Token重演方法的拟合度计算

pm4py.conformance.fitness_token_based_replay(log: Union[EventLog, DataFrame], petri_net: PetriNet, initial_marking: Marking, final_marking: Marking, activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name') → Dict[str, float]

说明:使用基于Token重演方法计算拟合度。拟合度是在基于日志的水平上计算的。
基于Token重演方法从初始变迁开始匹配轨迹和Petri网模型,以发现执行了哪些变迁,以及给定流程实例在哪些库所有剩余或丢失的令牌。基于Token重演对一致性检查很有用:事实上,如果在轨迹执行过程中,可以在不需要插入任何丢失的令牌的情况下触发变迁,则轨迹会根据模型进行拟合。如果强制达到最终标记,那么如果轨迹达到最终标记而没有任何丢失或剩余的标记,则该轨迹是拟合的。
在PM4Py中,有一种基于Token重演方法的实现,它能够跨越静默变迁(计算库所之间的最短路径),并且可以与具有唯一可见变迁和静默变迁的任何Petri网模型一起使用。当需要触发可见变迁,并且预设中的所有库所都没有提供正确数量的标记时,从当前标记开始,检查某个库所是否存在可以触发的静默变迁序列,以便启用可见变迁。然后激发静默变迁,并到达允许启用可见变迁的标记。该方法描述在:Berti, Alessandro, and Wil MP van der Aalst. “Reviving Token-based Replay: Increasing Speed While Improving Diagnostics.” ATAED@ Petri Nets/ACSD. 2019.
重演拟合度的计算旨在计算日志中有多少行为被流程模型所接受。我们提出了两种计算重演拟合度的方法,分别基于基于Token的重演和对齐。
对于基于Token的重演,将返回完全拟合的轨迹的百分比,以及根据科学贡献计算的拟合值

输入参数
    log–事件日志
   petri_net(PetriNet)–petri网
   initial_marking(标记)–初始标记
   final_marking(标记)–最终标记
   activity_key(str)–要用于活动的属性
   timestamp_key(str)–用于时间戳的属性
    case_id_key(str)–要用作案例标识符的属性

输出:

    Dict[str, float]

示例代码:

import pm4py

net, im, fm = pm4py.discover_petri_net_inductive(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
fitness_tbr = pm4py.fitness_token_based_replay(dataframe, net, im, fm, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

2.4 基于对齐方法的拟合度计算

pm4py.conformance.fitness_alignments(log: Union[EventLog, DataFrame], petri_net: PetriNet, initial_marking: Marking, final_marking: Marking, multi_processing: bool = False, activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name', variant_str: Optional[str] = None) → Dict[str, float]
说明:使用对齐方法计算拟合度。基于对齐的方法旨在找到轨迹和模型之间的最佳对齐方式之一。对于每个轨迹,对齐的输出是一个耦合列表,其中第一个元素是(轨迹的)事件或»,第二个元素是转换(模型的)或»。对于每对元素,可以提供以下分类:
        同步移动:事件的分类对应于变迁标签;在这种情况下,轨迹和模型在回放过程中都以相同的方式前进。
        在日志上移动:对于第二个元素为»的每一对元素,它对应于轨迹中未在模型中模拟的回放移动。这种移动是不合适的,并且表示轨迹和模型之间存在偏差。
        模型上移动:对于第一个元素为»的每一对元素,它对应于模型中未在轨迹中模仿的回放移动。对于模型上的移动,我们可以有以下区别:
          在涉及静默变迁的模型上移动:在这种情况下,即使不是同步移动,移动也是合适的。
          在不涉及静默变迁的模型上移动:在这种情况下,移动是不合适的,并且表示轨迹和模型之间存在偏差。
          对于每个轨迹,都会关联一个字典,其中包含以下信息:
           对齐:包含对齐(同步移动、日志移动、模型移动)成本:根据提供的成本函数拟合度包含对齐的成本:如果轨迹完全拟合,则等于1。
        重演拟合度的计算旨在计算日志中有多少行为被流程模型所接受。我们提出了两种计算重演拟合度的方法,分别基于基于Token的重演和对齐。
对于路线,将返回完全拟合的轨迹的百分比,以及计算为单个轨迹的拟合度值的平均值。

输入参数

       log–事件日志
       petri_net(PetriNet)–petri网
       initial_marking(标记)–初始标记
       final_marking(标记)–最终标记
       multi_prrocessing(bool)–启用多处理的布尔值
       activity_key(str)–要用于活动的属性
       timestamp_key(str)–用于时间戳的属性
      case_id_key(str)–要用作案例标识符的属性
      variant_str–变体规范

返回类型

   Dict[str, float]

示例代码:

import pm4py

net, im, fm = pm4py.discover_petri_net_inductive(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
fitness_alignments = pm4py.fitness_alignments(dataframe, net, im, fm, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

2.5 基于Token重演的精确度计算

pm4py.conformance.precision_token_based_replay(log: Union[EventLog, DataFrame]petri_net: PetriNetinitial_marking: Markingfinal_marking: Markingactivity_key: str = 'concept:name'timestamp_key: str = 'time:timestamp'case_id_key: str = 'case:concept:name') → float


说明:使用基于Token重演方法计算精确度。基于Token重演方法从初始库所开始匹配轨迹和Petri网模型,以发现执行了哪些变迁,以及给定流程实例在哪些库所有剩余或丢失的令牌。基于Token重演方法对一致性检查很有用:事实上,如果在轨迹执行过程中,可以在不需要插入任何丢失的令牌的情况下使能变迁,则轨迹会根据模型进行拟合。如果强制达到最终标记,那么如果轨迹达到最终标记而没有任何丢失或剩余的标记,则该轨迹是合适的。
在PM4Py中,有一种令牌重放器的实现,它能够跨越静默变迁(计算库所之间的最短路径),并且可以与具有唯一可见变迁和静默变迁的任何Petri网模型一起使用。当需要触发可见变迁,并且预设中的所有库所都没有提供正确数量的标记时,从当前标记开始,检查某个位置是否存在可以触发的静默变迁的序列,以便启用可见变迁。然后激发静默变迁,并到达允许启用可见变迁的标记。该方法描述在:Berti, Alessandro, and Wil MP van der Aalst. “Reviving Token-based Replay: Increasing Speed While Improving Diagnostics.” ATAED@ Petri Nets/ACSD. 2019.
基于TBR的精度(ETConformance)的参考文献是:“A fresh look at precision in process conformance.” International Conference on Business Process Management. Springer, Berlin, Heidelberg, 2010.
在这种方法中,日志的不同前缀会在模型上重演(如果可能的话)。在到达标记时,将流程模型中启用的变迁集与前缀后面的活动集进行比较。集合的差异越大,精度值就越低。集合越相似,精度值就越高。

输入参数

    log–事件日志
    petri_net(PetriNet)–petri网
    initial_marking(标记)–初始标记
    final_marking(标记)–最终标记
    activity_key(str)–要用于活动的属性
    timestamp_key(str)–用于时间戳的属性
    case_id_key(str)–要用作案例标识符的属性

返回类型:

    float

示例代码:

import pm4py

net, im, fm = pm4py.discover_petri_net_inductive(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
precision_tbr = pm4py.precision_token_based_replay(dataframe, net, im, fm, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

2.6 基于对齐方法的精确度计算

pm4py.conformance.precision_alignments(log: Union[EventLog, DataFrame], petri_net: PetriNet, initial_marking: Marking, final_marking: Marking, multi_processing: bool = False, activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name') → float
说明:使用对齐方式计算模型相对于事件日志的精确度。基于对齐的重演旨在找到轨迹和模型之间的最佳对齐方式之一。对于每个轨迹,对齐的输出是一个耦合列表,其中第一个元素是(轨迹的)事件或»,第二个元素是转换(模型的)或»。对于每对元素,可以提供以下分类:
        同步移动:事件的分类对应于变迁标签;在这种情况下,轨迹和模型在回放过程中都以相同的方式前进。
        在日志上移动:对于第二个元素为»的每一对元素,它对应于轨迹中未在模型中模拟的回放移动。这种移动是不合适的,并且表示轨迹和模型之间存在偏差。
        模型上移动:对于第一个元素为»的每一对元素,它对应于模型中未在轨迹中模仿的回放移动。对于模型上的移动,我们可以有以下区别:
          在涉及静默变迁的模型上移动:在这种情况下,即使不是同步移动,移动也是合适的。
          在不涉及静默变迁的模型上移动:在这种情况下,移动是不合适的,并且表示轨迹和模型之间存在偏差。
          对于每个轨迹,都会关联一个字典,其中包含以下信息:
           对齐:包含对齐(同步移动、日志移动、模型移动)成本:根据提供的成本函数拟合度包含对齐的成本:如果轨迹完全拟合,则等于1。
          基于对齐的精确度(Align ETConformance)的参考文件是: Adriansyah, Arya, et al. “Measuring precision of modeled behavior.” Information systems and e-Business Management 13.1 (2015): 37-67.
        在这种方法中,日志的不同前缀会在模型上重播(如果可能的话)。在到达标记时,将流程模型中启用的变迁集与前缀后面的活动集进行比较。集合的差异越大,精确度值就越低。集合越相似,精确度值就越高。

输入参数

    log–事件日志
    petri_net(PetriNet)–petri网
    initial_marking(标记)–初始标记
    final_marking(标记)–最终标记
    multi_prrocessing(bool)–启用多处理的布尔值
    activity_key(str)–要用于活动的属性
    timestamp_key(str)–用于时间戳的属性
    case_id_key(str)–要用作案例标识符的属性

返回类型:

    float

 示例代码:

import pm4py

net, im, fm = pm4py.discover_petri_net_inductive(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
precision_alignments = pm4py.precision_alignments(dataframe, net, im, fm, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

2.7 基于Token重演的前缀活动

pm4py.conformance.replay_prefix_tbr(prefix: List[str], net: PetriNet, im: Marking, fm: Marking, activity_key: str = 'concept:name') → Marking

说明:使用基于Token重演方法在给定的可接受Petri网上Replay前缀活动(活动列表)。

输入参数:

     prefix–活动列表
    net(PetriNet)–Petri网
    im(标记)-初始标记
    fm(标记)-最终标记
    activity_key(str)–要用作活动的属性

返回类型

    Marking

 示例代码:

import pm4py

net, im, fm = pm4py.read_pnml('tests/input_data/running-example.pnml')
marking = pm4py.replay_prefix_tbr(['register request', 'check ticket'], net, im, fm)

2.8 临时配置文件的一致性检查

pm4py.conformance.conformance_temporal_profile(log: Union[EventLog, DataFrame], temporal_profile: Dict[Tuple[str, str], Tuple[float, float]], zeta: float = 1.0, activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name', return_diagnostics_dataframe: bool = False) → List[List[Tuple[float, float, float, float]]]

说明:对提供的日志与提供的临时配置文件执行一致性检查。结果是每种情况下基于时间的偏差列表。例如,如果应用一致性的日志如下(1种情况):A(时间戳:2000-01)B(时间戳,2002-01)A和B的时间戳之差为两年。如果指定了时间剖面:{('A','B'):(1.5个月,0.5个月),('A'、'C'):“5个月,0),('A','D'):”(2个月,零)},并且zeta设置为1,则上述情况将发生偏差(考虑到两个活动('A'和'B',)),因为2年>1.5个月+0.5个月。

输入参数:

    log–log对象
    temporal_file–临时配置文件。例如,如果日志有两种情况:A(时间戳:1980-01)B(时间戳(timestamp:1980-03)C(时间戳)1980-06;A(时间戳:1990-01)B(时间戳;1990-02)D(时间戳,1990-03);时间配置文件将包含:{('A','B'):(1.5个月,0.5个月),('A'、'C'):
    zeta(浮动)–允许与平均值之间的标准偏差数。例如,zeta=1允许AVERAGE-STDEV和AVERAGE+STDEV之间的每个时间戳。
    activity_key(str)–要用于活动的属性
    timestamp_key(str)–用于时间戳的属性
    case_id_key(str)–要用作案例标识符的属性
    return_diagnosis_dataframe(bool)–如果可能,返回一个带有诊断的数据帧(而不是通常的输出)

返回类型

List[List[Tuple[float, float, float, float]]]

 示例代码:

import pm4py

temporal_profile = pm4py.discover_temporal_profile(dataframe, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
conformance_temporal_profile = pm4py.conformance_temporal_profile(dataframe, temporal_profile, zeta=1, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

 2.9 Declare模型的一致性检查

pm4py.conformance.conformance_declare(log: Union[EventLog, DataFrame], declare_model: Dict[str, Dict[Any, Dict[str, int]]], activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name', return_diagnostics_dataframe: bool = False) → List[Dict[str, Any]]

说明:对DECLARE模型应用一致性检查。
参考论文:F. M. Maggi, A. J. Mooij and W. M. P. van der Aalst, “User-guided discovery of declarative process models,” 2011 IEEE Symposium on Computational Intelligence and Data Mining (CIDM), Paris, France, 2011, pp. 192-199, doi: 10.1109/CIDM.2011.5949297.

输入参数:

    log–事件日志
   declare_mode–declare模型
    activity_key(str)–要用于活动的属性
    timestamp_key(str)–用于时间戳的属性
    case_id_key(str)–要用作案例标识符的属性
    return_diagnosis_dataframe(bool)–如果可能,返回一个带有诊断的数据帧(而不是通常的输出)

返回类型

   List[Dict[str, Any]]

示例代码:

import pm4py

log = pm4py.read_xes("C:/receipt.xes")
declare_model = pm4py.discover_declare(log)
conf_result = pm4py.conformance_declare(log, declare_model)

2.10 日志Skeleton的合规性检查

pm4py.conformance.conformance_log_skeleton(log: Union[EventLog, DataFrame], log_skeleton: Dict[str, Any], activity_key: str = 'concept:name', timestamp_key: str = 'time:timestamp', case_id_key: str = 'case:concept:name', return_diagnostics_dataframe: bool = False) → List[Set[Any]]

说明:使用日志Skeleton执行一致性检查。参考文件:Verbeek, H. M. W., and R. Medeiros de Carvalho. “Log skeletons: A classification approach to process discovery.” arXiv preprint arXiv:1806.08247 (2018).
        日志Skeleton是一个声明性模型,它由六个不同的约束组成:

    -“directly_follows”:为某些活动指定了对直接跟随的活动的一些严格限制。例如  “A应该直接后跟B”,“B应该直接后跟C”。
    “always_beater”:指定只有当某些其他活动在somewhen before执行时,才能执行某些活动。在案件的历史上。例如,“C应始终以A开头”
     “always_after”:指定某些活动应始终触发某些其他活动的执行
在案件的未来历史中。例如,“A应始终后跟C”
     “equivalence”:指定一对给定的活动应该在内部发生相同次数的情况下发生一个案例。例如,“B和C应该总是发生相同的次数”。
    “never_together”:指定在案例的历史中,给定的两个活动不应该一起发生。例如,“不应该同时包含C和D的情况”。
    “activ_ecurrences”:指定每个活动允许的出现次数:例如,允许A被执行1或2次,允许B被执行1、2或3或4次。

输入参数:    

    log–log对象
    log_skeleton–log-skeleton对象,表示为六个约束(never_together、          always_beater…)的字典以及发现的规则。
    activity_key(str)–要用于活动的属性
    timestamp_key(str)–用于时间戳的属性
    case_id_key(str)–要用作案例标识符的属性
    return_diagnosis_dataframe(bool)–如果可能,返回一个带有诊断的数据帧(而不是通常的输出)

返回类型

       List[Set[Any]]

示例代码:

import pm4py

log_skeleton = pm4py.discover_log_skeleton(dataframe, noise_threshold=0.1, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')
conformance_lsk = pm4py.conformance_log_skeleton(dataframe, log_skeleton, activity_key='concept:name', case_id_key='case:concept:name', timestamp_key='time:timestamp')

如需了解更多,欢迎加入流程挖掘交流群QQ:671290481.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北冥有鱼zsp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值