建模杂谈系列190 APIFunc 调用 APIFunc

本文介绍了一种名为APIFunc的工作单元,并探讨了如何利用链式调用来实现其功能的复用及模块化开发。重点讲解了APIFunc的结构、列式处理方式及其在深度学习等场景的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明

从工作的划分和复用上,这种模式是必须

还是积木拼凑的思维,之前的APIFunc实现时,已经发现有一些过程是会被经常复用的。这些连续的过程,可以称之为会话,或者说是链,都是一个意思。

APIFunc在定义的时候,需要在字典中定义若干不重名的函数,所以之前有一个思路是:将一个会话视为root,然后去修改在不同变量或者流程下的名称。

但是从另一个角度考虑,这样的改动会对原来的对象结构以及运行机制产生比较大的变化。另一个思路是,把APIFunc当成是一个可以嵌套的单元来设计,这样不用动目前的对象结构,只是要考虑怎么样增加一个连接的机制。

内容

1 APIFunc的结构

这里提到的APIFunc,更准确的说是是逻辑核心;APIFunc的数据基础(服务流)在之前的文章讨论了(模板镜像 + 自定义配置 + 自定义Worker)。

从顶层来看,APIFunc是一个瀑布图(有向无环图)

我们假设要处理一个表数据,那么这个表数据里有多个变量,每个变量会有长短不一的链,自上而下的看,就像瀑布。

从工作单元来划分,链(会话)是这个图的基础,链有两种类型(一字型、Y型)。其中Y可以进一步分为YMerge和YSplit两个类型。其中一字型是复用的关键,因为流程通常都比较长,是管道。YM和YS也很重要,但是其类型容易穷举,是连接件。

一字型与逻辑的深度,以及并行调度关系较大。

Y型与流的控制关系较大。

对一个瀑布图进行拆解,我们会发现工作量由一些独立的点(长度为一的链),和一些可以复用的链构成,并通过Y型链进行连接。目前还是串行,但之后可以通过BFS进行扫描,分成若干层并行。

2 列式处理

行式处理是指,设计对一个元素进行的处理,然后通过广播机制发送到所有的元素;列式处理是指,直接对所有元素处理。

当前的APIFunc有两种规则函数,一种是行式的,一种是列式的。行式的不多说,简单,使用率很高。

关于列式,之前是定位在深度学习、矩阵计算的应用上。这种处理要求直接处理一个一维向量或者二维矩阵,不能随元素广播。

如果把某个APIFunc视为一个函数,那么这个函数本质上是列式的

再考虑到APIFunc的频繁复用,那么说起来也不能说APIFunc绝大部分是行式处理的函数。我想了想,这个和Kettle里子变换(Mapping)的概念很像。

3 列式处理的格式

行式处理通过RuleMSG对函数产生的信息进行了封装:

序号变量解释
1name规则的名称,需要保证唯一性
2statusTrue/False 代表程序是否正常运行
3rule_result如果为空,代表规则失败;否则就是1和0(未来可能有-1)
4msg对应的消息,目前没怎么用到
5data数据,可以是标量,列表,也可以是字典

在列式处理中,要自己去准备这些变量,仅此而已。在调用APIFunc(视为一个函数时),里面的信息应该是可以“照搬”的,所以和矩阵的的列处理不同。

例:深度学习的行处理。因为这个流程如果能计算,status就是True,所以就是统一赋值。

	...
    rule_hits = (col_base_df['PER'] !=',') | (col_base_df['ORG'] !=',')
    rule_hits *=1
    # # 3 output
    res_df = pd.DataFrame()
    res_df['data'] = cols2s(col_base_df, ['PER','ORG'],['PER','ORG'])
    res_df['status'] = True 
    res_df['msg'] = 'ok'
    res_df['rule_result']=rule_hits
    res_df['name']='ner_predict'
    return res_df

现在因为是引用其他APIFunc的结果,所以没有必要特定的赋值,而是进行引用。那么,如何引用呢?

假设有两个af,第一个af定义了一个标准的链,第二个af以一个列函数的形式去调用第一af。

AF1 被调用的链

样例数据

gs_id = 'tem_id'
sample_dataoflist1 = [{gs_id:1 ,'content':'阿丽亚朵\r\n '}, {gs_id:2 ,'content':'阿美 利箭\r \n '}]

创建实例并增加函数

af1 = APIFunc('af1', listofdict= sample_dataoflist1, key_id=gs_id)
chain_funcname_dict = {}
import re 
# 删除换行回车
@af1.route('/enter_replace', is_force=True)
def enter_replace(input_dict = None, para_dict = None):
    msg = RuleMSG('enter_replace')
    
    # 1 检查字段
    need_cols = ['some_str']
    input_cols = list(input_dict.keys())
    if not (set(need_cols) <= set(input_cols)):
        msg.status = False 
        msg.rule_result = None 
        msg.msg = 'InputSetError'
        msg.data = None  
        return msg.to_dict()
    
    try:
        some_str = input_dict['some_str']
        some_str1= re.sub('\r?\n','',some_str)
        
        msg.msg ='ok'
        msg.status = True
        msg.data = some_str1
        if some_str!=some_str1:
            msg.rule_result = 1
        else:
            msg.rule_result = 0
    except:
        msg.status = False
        msg.rule_result = None
        msg.data = None
        
    return msg.to_dict()

enter_replace = {}
enter_replace['subline_dict'] = None
enter_replace['main_dict'] = {'key_id':gs_id, 'depend_cols':['content'], 'input_cols':['some_str']}
enter_replace['para_dict'] = None
chain_funcname_dict['enter_replace'] = enter_replace

链的提取

BaseClean_session = ['enter_replace']

BaseClean_session_list = BaseClean_session
BaseClean_session_dict = {}
for k in BaseClean_session_list:
    BaseClean_session_dict[k] = chain_funcname_dict[k]

执行

af1.run_chain(chain_funcname_list=BaseClean_session_list,chain_funcname_dict=BaseClean_session_dict)
running >>> enter_replace
            func  recs  duration
0           init     2      0.00
1  enter_replace     2      0.01
True

这样一条最简单的链就开发好了,可以认为,此时的对象被存放在一个py文件,可以被导入。

AF2 调用链

AF2可以视为我们当前的工作点,要调用其他的链之前要先进行导入。我们可以假设导入时存为这样的字典

import_session_para_dict = {'BaseClean_session': 
			{'af':af1, 
			'session_list':BaseClean_session_list,
			'session_dict':BaseClean_session_dict,
			'gs_id':gs_id ,
			'depend_cols':['content'] }}

通常来说,新的链和旧的链很可能是不同gs_id,所以实例化新的af时,我们用一个新的gs_id。

gs_id2 ='tem2_id'
sample_dataoflist2 = [{gs_id2:1 ,'content':'我是\r\n '}, {gs_id2:2 ,'content':'全栈开发者\r \n '}]
af2 = APIFunc('af2', listofdict= sample_dataoflist2, key_id=gs_id2)

此时定义一个列函数来调用AF1

import pandas as pd 
@af2.route('/content_BaseClean', is_force=True,is_col_base = True)
def content_BaseClean(input_listofdict = None, para_dict = None):
    current_rule_name = 'content_BaseClean'
    # 1 需要的字段 == 不太需要改
    # 当元素是ndarray时没有办法再很好的DataFrame;只能假设进来的列都是相同的字段
    # 这里不符合直接就是None
    need_cols = ['content']
    sample_input_dict = input_listofdict[0]

    input_cols = list(sample_input_dict.keys())
    if not (set(need_cols) <= set(input_cols)):
        res_df = pd.DataFrame()
        res_df['status'] = [False]* len(input_listofdict) 
        res_df['rule_result'] = None 
        res_df['msg'] = 'InputSetError'
        res_df['data'] = None  
        res_df['name']=current_rule_name
        return res_df

    session_name = para_dict['session_name']
    
    cur_gs_id = para_dict['cur_gs_id']
    acquired_rule = para_dict['acquired_rule']
    
    import_session_para_dict = para_dict['import_session_para_dict']
    the_session_dict = import_session_para_dict[session_name]
    
    
    tem_af = the_session_dict['af']
    tem_session_list = the_session_dict['session_list']
    tem_session_dict = the_session_dict['session_dict']
    tem_session_gs_id = the_session_dict['gs_id']
    
    depend_cols = the_session_dict['depend_cols']
    
    cur_data_df = pd.DataFrame(input_listofdict.tolist())
    if tem_session_gs_id != cur_gs_id:
        cur_data_df[tem_session_gs_id] = cur_data_df[cur_gs_id]
    
    subcall_cols = sorted(list(set([tem_session_gs_id] + depend_cols)))
    cur_data_listofdict = cur_data_df[subcall_cols].to_dict(orient='records')
    print(cur_data_listofdict)
    
    
    tem_af.reinit_data(listofdict= cur_data_listofdict, key_id=tem_session_gs_id)
    
    tem_af.run_chain(chain_funcname_list=tem_session_list,chain_funcname_dict=tem_session_dict)
    
    # 按tem_af.g的结果拼凑
    keep_cols = [tem_session_gs_id,acquired_rule]
    data_keep_cols = [tem_session_gs_id,acquired_rule +'.data']
    
    res_df_status = tem_af.g['status_frame'][keep_cols]
    res_df_status.columns = [cur_gs_id,'status']
    
    res_df_ruleresult_frame = tem_af.g['ruleresult_frame'][keep_cols]
    res_df_ruleresult_frame.columns = [cur_gs_id, 'rule_result']
    
    res_df_msg_frame = tem_af.g['msg_frame'][keep_cols]
    res_df_msg_frame.columns = [cur_gs_id,'msg']
    
    res_df_data_frame = tem_af.g['data_frame'][data_keep_cols]
    res_df_data_frame.columns = [cur_gs_id,'data']
    
    merge_df = pd.merge(res_df_status,res_df_ruleresult_frame,on=cur_gs_id,how='left')
    merge_df1 =pd.merge(merge_df,res_df_msg_frame,on=cur_gs_id,how='left')
    merge_df2 =pd.merge(merge_df1,res_df_data_frame,on=cur_gs_id,how='left')
    merge_df2['name'] = current_rule_name
    return merge_df2
    
content_BaseClean = {}
content_BaseClean['subline_dict'] =None
content_BaseClean['main_dict'] = {'key_id':gs_id2,
                            'depend_cols':['content'],
                            'input_cols': ['content']}

content_BaseClean['para_dict'] = {'import_session_para_dict': import_session_para_dict,'session_name':'BaseClean_session', 'cur_gs_id':gs_id2, 'acquired_rule':'enter_replace'}
chain_funcname_dict['content_BaseClean'] = content_BaseClean

接下来进行调用

NestedClean_session = ['content_BaseClean']

NestedClean_session_list = NestedClean_session
NestedClean_session_dict = {}
for k in NestedClean_session_list:
    NestedClean_session_dict[k] = chain_funcname_dict[k]

af2.run_chain(chain_funcname_list=NestedClean_session_list,chain_funcname_dict=NestedClean_session_dict)

running >>> content_BaseClean
[{'content': '我是\r\n ', 'tem_id': 1}, {'content': '全栈开发者\r \n ', 'tem_id': 2}]
初始化状态 0
running >>> enter_replace
            func  recs  duration
0           init     2       0.0
1  enter_replace     2       0.0
                func  recs  duration
0               init     2      0.00
1  content_BaseClean     2      0.03

af2.g['data_frame']

tem2_id	content	content_BaseClean.data
0	1	我是\r\n	我是
1	2	全栈开发者\r \n	全栈开发者\r

可以看到,af2调用了af1的方法解决了问题。

4 总结

  • 1 通过链调用链的方式,可以实现分步的开发和复用
  • 2 后续通过Web进行开始时,还有一些关于组装的问题(如何生成需要的链的py文件,并在新的链py文件中进行导入)
  • 3 前一个链,要注意传入的变量(depend_cols)有哪些,新的链要声明现有的gs_id和需要的导出的规则(acquire_rule)
  • 4 在列函数中,大部分都是在做映射,所以这是一类特殊的列函数(可以认为是基于模板的列函数)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值