用Python编写用户定义的函数
2018-01-21 10:38
309 查看
用Python编写用户定义的函数
除了DDlog中的正常派生规则之外,DeepDive还支持用于数据处理的用户定义函数(UDF)。UDF可以是任何从标准输入采用TAB分隔的JSON(TSJ)格式或TAB分隔值(TSV或PostgreSQL的文本格式)的程序,并将相同的格式输出到标准输出。TSJ在每行中以固定顺序放置固定数量的JSON值,并用TAB分隔。TSJ可以被认为是一种更为高效的编码方式,而不是简单地在每行中放置一个JSON对象,每行必须重复字段名称,特别是每行的固定数据模式已知并提前修复时。以下各节介绍了DeepDive在Python中编写UDF的建议方式,以及它们如何在DDlog程序中使用。
在DDlog中使用自定义函数(UDF)
要在DDlog中使用用户定义的函数,必须首先声明它们,然后使用特殊语法调用它们。首先,我们来为我们的运行示例定义两个关系的模式。
article( id int, url text, title text, author text, words text[] ). classification( article_id int, topic text ).
在这个例子中,我们假设我们想写一个简单的UDF,
article通过向关系中添加元组,使用UDF分类成不同的主题
classification。以下两节详细说明如何声明这样的函数以及如何在DDlog中调用它。
函数声明
函数声明包括输入/输出模式以及指向其实现的指针。function <function_name> over (<input_var_name> <input_var_type>,...) returns [(<output_var_name> <output_var_type>,...) | rows like <relation_name>] implementation "<executable_path>" handles tsj lines.
在我们的例子,假设我们将只使用
author和
words每一个
article来确定其标识的主题
id,并实施将保持在相对路径的可执行文件
udf/classify.py。下面显示了这个函数的确切声明。
function classify_articles over (id int, author text, words text[]) returns (article_id int, topic text) implementation "udf/classify.py" handles tsj lines.
请注意,关系的列定义
classification在该
returns子句中重复。这可以通过使用
rows like下面的语法来省略。
function classify_articles over (id int, author text, words text[]) return rows like classification implementation "udf/classify.py" handles tsj lines.
另请注意,函数输入与
articles关系相似,但是有些列缺失。这是因为函数不会像前面提到的那样使用其余的列,所以放弃不必要的效率值是个好主意。下一节将展示如何导出这些输入元组并将其输入到一个函数中。
函数调用规则
可以调用上面声明的函数来为输出类型的另一个关系派生元组。函数调用的输入元组使用类似于正常派生规则的语法来导出。例如,下面显示的规则调用classify_articles函数来
classification使用关系中的一个子集来填充
articles关系。
classification += classify_articles(id, author, words) :- article(id, _, _, author, words).
函数调用规则可以被视为具有不同头部语法的常规推导规则的特殊情况,其中
+=头部关系名称附加了函数名称。
例如:
transaction_feature += extract_transaction_features( p1_id, p2_id, p1_begin_index, p1_end_index, p2_begin_index, p2_end_index, doc_id, sent_index, tokens, lemmas, pos_tags, ner_tags, dep_types, dep_tokens ) :- company_mention(p1_id, _, doc_id, sent_index, p1_begin_index, p1_end_index), company_mention(p2_id, _, doc_id, sent_index, p2_begin_index, p2_end_index), sentences(doc_id, sent_index, _, tokens, lemmas, pos_tags, ner_tags, _, dep_types, dep_tokens).
备注:把sentences表和mention表做join,得到的结果输入函数,输出到transaction_feature表中
用Python编写UDF
DeepDive提供了一种用Python编写用户定义函数的模板化方法。它提供了几个Python函数装饰器,以分别简化分析和格式化输入和输出。在每一个输入行被调用的Python生成器都应该被装饰@tsj_extractor,即在该
def行
@tsj_extractor被放置之前。(Python生成器是一个Python函数,它用来
yield代替
return每个调用产生多个结果的迭代)生成器期望的输入和输出列类型可以分别使用参数默认值和
@returns装饰器声明。他们告诉输入解析器和输出格式化器应该如何工作。
让我们看一个现实的例子来描述如何在代码中使用它们。下面是一个接近完成的代码,
udf/classify.py声明为DDlog函数的实现
classify_articles。
#!/usr/bin/env python from deepdive import * # Required for @tsj_extractor and @returns compsci_authors = [...] bio_authors = [...] bio_words = [...] @tsj_extractor # Declares the generator below as the main function to call @returns(lambda # Declares the types of output columns as declared in DDlog article_id = "int", topic = "text", :[]) def classify( # The input types can be declared directly on each parameter as its default value article_id = "int", author = "text", words = "text[]", ): """ Classify articles by assigning topics. """ num_topics = 0 if author in compsci_authors: num_topics += 1 yield [article_id, "cs"] if author in bio_authors: num_topics += 1 yield [article_id, "bio"] elif any (word for word in bio_words if word in words): num_topics += 1 yield [article_id, "bio"] if num_topics == 0: yield [article_id, None]
这个简单的UDF根据作者在已知类别中的成员资格为文章分配一个主题。如果作者无法识别,我们会尝试查找出现在预定义集合中的单词。最后,如果什么都不匹配,我们只是把它放到另一个全面的话题。请注意,这里的主题本身是完全由用户定义的。
请注意,要使用这些Python装饰器,你需要有
from deepdive import *。另请注意,输入列的类型可以用与生成器参数相同的方式声明为默认值
@returns。
@tsj_extractor装饰器
对于@tsj_extractor,将采取一个输入行的
ab09
时间和所述第一装饰
yield零个或多个输出行作为值的列表。这基本上让DeepDive知道在运行Python程序时要调用哪个函数。(对于TSV,有
@tsv_extractor装饰,但强烈推荐TSJ。)
注意事项
一般来说,这个生成器应该放在程序的底部,除非在处理完所有的输入行之后有一些清理或拆卸任务。修饰的生成器使用的任何函数或变量都应该出现在修饰器之前,因为@tsj_extractor修饰器会立即开始解析输入并调用生成器。发电机不应该
sys.stdout.write任何会损坏标准输出的东西。相反,
print >>sys.stderr或者
sys.stderr.write可以用来记录有用的信息。更多的信息可以在debugging-udf部分找到
参数的默认值和@returns装饰
为了将输入的TSJ行正确地解析为Python值,并将@tsj_extractor在TSJ中正确生成的值格式化,需要在Python程序中写入列类型。它们应该与DDlog中的函数声明一致。输入列的类型可以直接在
@tsj_extractor生成器的签名中声明为参数默认值,如上例所示。
@returns装饰器的参数可以是名称和类型对的列表,也可以是所有参数的默认值都设置为其类型的函数。使用
lambda是优选的,因为对的列表需要更多符号来使得声明混乱。例如,比较以上
@returns([("article_id", "int"), ("topic", "text")])。原因
dict(column="type", ... )还是
{ "column": "type", ... }不工作是因为Python用这些语法忘记了列的顺序,这对TSJ解析器和格式化程序是至关重要的。传递的函数永远不会被调用,所以主体可以被保留为任何值,比如空的list(
[])。DeepDive还
@over为输入列对称提供装饰器
@returns,但不建议使用这种装饰器,因为它会产生冗余声明。
运行和调试UDF
一旦写入UDF的第一个剪辑,就可以使用deepdive do和
deepdive redo命令运行。例如,
classify_articles我们的运行示例中的派生
classification关系的函数可以使用以下命令运行:
deepdive redo classification
这将调用Python程序
udf/classify.py,输入包含
article表的三列的TSJ行,并将其输出添加到
classification数据库中的表中。
有专门的页面描述有关运行这些UDF和调试这些UDF的更多细节。
相关文章推荐
- 编写一个程序,其main()调用一个用户定义的函数(以光年值为参数,并返回对应天文单位 的值).该程序按下面的格式要求用户输入光年值
- Python实现一元二次方程的定义是:ax2 + bx + c = 0 请编写一个函数,返回一元二次方程的解。
- Python实现一元二次方程的定义是:ax2 + bx + c = 0 请编写一个函数,返回一元二次方程的解。
- 我的python学习笔记.用户输入.函数input()的工作原理
- [代码]如何使用用户定义的表值函数(LINQ to SQL)
- 零基础学python-15.3 函数的定义、调用与多态
- python函数定义
- 各种python 函数参数定义和解析
- 编写一个Shell脚本,脚本中定义两个函数fun1和fun2
- Python中定义函数时参数有默认值的小陷阱
- python里函数定义的理解
- PYthon 中函数编写时需要注意的地方
- Python入门教程5. 字典基本操作【定义、运算、常用函数】[原创]_python_脚本之家
- 使用Python的base64 编码模块,编写去除 “=”的解码函数
- 如何用python编写函数把摄氏温度转化为华氏温度-学习笔记1
- Python 定义函数
- Python函数定义练习:解一元二次方程
- Python定义函数默认参数
- python调用matlab编写的函数
- python定义函数