,`的自变量的路由。 在以下相同的 HTML 片段中定义。
## 使用`pd_entity`属性调用 PixieDust `display()` API
当使用`pd_options`属性创建内核请求时,PixieApp 框架将当前 PixieApp 类用作目标。 但是,您可以通过指定`pd_entity`属性来更改此目标。 例如,您可以指向另一个 PixieApp,或更有趣的是,指向`display()` API 支持的数据结构,例如 Pandas 或 Spark `DataFrame`。 在这种情况下,如果您提供了`display()` API 预期的正确选项,则生成的输出将为图表本身(对于 Matplotlib,为图像;对于 Mapbox,为 IFRAME;对于 Mapbox,则为 SVG 散景)。 一种获取正确选项的简单方法是在其自己的单元格中调用`display()` API,使用菜单将其配置为所需的图表,然后通过单击**编辑元数据**复制可用的单元格元数据 JSON 片段。 按钮。 (您可能首先需要使用菜单**视图 | 单元格工具栏 | 编辑元数据**来启用按钮)。
您也可以指定`pd_entity`,不带任何值。 在这种情况下,PixieApp 框架将使用传递为用于启动 PixieApp 应用的`run`方法的第一个参数的实体。 例如,以`cars`为 Pandas 的`my_pixieapp.run(cars)`或通过`pixiedust.sampleData()`方法创建的 Spark `DataFrame`。 `pd_entity`的值也可以是返回实体的函数调用。 当您要在渲染之前动态计算实体时,这很有用。 与其他变量一样,`pd_entity`的范围可以是 PixieApp 类或在笔记本中声明的任何变量。
例如,我们可以在其自己的单元格中创建一个函数,该函数将前缀作为参数并返回 Pandas `DataFrame`。 然后我们将其用作我的 PixieApp 中的`pd_entity`值,如以下代码所示:
```py
def compute_pdf(key):
return pandas.DataFrame([
{"col{}".format(i): "{}{}-{}".format(key,i,j) for i in range(4)} for j in range(10)
])
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode9.py)。
在前面的代码中,我们使用 [Python 列表推导](https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions)快速基于`key`参数生成模拟数据。
### 注意
Python 列表推导是我最喜欢的 Python 语言功能之一,因为它们使您可以使用表达简洁的语法来创建,转换和提取数据。
我可以然后创建一个 PixieApp,使用`compute_pdf`函数作为`pd_entity`将数据呈现为表格:
```py
from pixiedust.display.app import *
@PixieApp
class TestEntity():
@route()
def main_screen(self):
return """
Simple PixieApp with dynamically computed dataframe
"""
test = TestEntity()
test.run()
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode10.py)。
在前面的代码中,为简单起见,我将键硬编码为`'prefix'`,然后将其作为练习使用输入控件和`$val()`指令使其可用户定义。
值得注意的另一件事是在显示图表的 DIV 中使用`pd_render_onload`属性。 此属性告诉 PixieApp 在将元素加载到浏览器 DOM 中后立即执行该元素定义的内核请求。
以下屏幕快照显示了先前 PixieApp 的结果:
![Invoking the PixieDust display() API using pd_entity attribute](img/00057.jpeg)
在 PixieApp 中动态创建`DataFrame`
回到我们的`Github Tracking`应用,现在让我们将`pd_entity`值应用于从 GitHub Statistics API 加载的`DataFrame`。 我们创建了一个称为`load_commit_activity,`的方法,该方法负责将数据加载到 Pandas `DataFrame`中,并将其与显示图表所需的`pd_options`一起返回:
```py
from datetime import datetime
import requests
import pandas
def load_commit_activity(owner, repo_name):
response = requests.get(
"https://api.github.com/repos/{}/{}/stats/commit_activity".format(owner, repo_name),
auth=(github_user, github_token)
).json()
pdf = pandas.DataFrame([
{"total": item["total"], "week":datetime.fromtimestamp(item["week"])} for item in response
])
return {
"pdf":pdf,
"chart_options": {
"handlerId": "lineChart",
"keyFields": "week",
"valueFields": "total",
"aggregation": "SUM",
"rendererId": "bokeh"
}
}
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode11.py)。
前面的代码将 GET 请求发送到 GitHub,并使用在笔记本开始时设置的`github_user`和`github_token`变量进行认证。 响应是一个 JSON 有效负载,我们将使用它创建一个 Pandas `DataFrame`。 在创建`DataFrame`之前,我们需要将 JSON 有效负载转换为正确的格式。 现在,有效负载如下所示:
```py
[
{"days":[0,0,0,0,0,0,0],"total":0,"week":1485046800},
{"days":[0,0,0,0,0,0,0],"total":0,"week":1485651600},
{"days":[0,0,0,0,0,0,0],"total":0,"week":1486256400},
{"days":[0,0,0,0,0,0,0],"total":0,"week":1486861200}
...
]
```
我们需要删除`days`键,因为它不需要显示图表,并且,为了正确显示图表,我们需要将`week`键的值(它是 Unix 时间戳)转换为 Python `datetime`对象 。 这种转换是通过 Python 列表理解和简单的代码行完成的:
```py
[{"total": item["total"], "week":datetime.fromtimestamp(item["week"])} for item in response]
```
在当前实现中,`load_commit_activity`函数在其自己的单元格中定义,但我们也可以将其定义为 PixieApp 的成员方法。 最佳实践是使用自己的单元非常方便,因为我们可以对功能进行单元测试并对其进行快速迭代,而不会产生每次运行完整应用的开销。
要获取`pd_options`值,我们可以简单地使用示例回购信息运行该函数,然后在单独的单元格中调用`display()` API:
![Invoking the PixieDust display() API using pd_entity attribute](img/00058.jpeg)
在单独的单元格中使用`display()`获取可视化配置
要获取上述图表,您需要选择**折线图**,然后在**选项**对话框中,将`week`列拖放到**键**框和`total`列到**值**框。 您还需要选择 Bokeh 作为渲染器。 完成后,请注意,PixieDust 将自动检测到`x`轴为日期时间,并将相应地调整渲染。
使用**编辑元数据**按钮,我们现在可以复制图表选项 JSON 片段:
![Invoking the PixieDust display() API using pd_entity attribute](img/00059.jpeg)
捕获`display()` JSON 配置
然后在`load_commit_activity`有效载荷中返回它:
```py
return {
"pdf":pdf,
"chart_options": {
"handlerId": "lineChart",
"keyFields": "week",
"valueFields": "total",
"aggregation": "SUM",
"rendererId": "bokeh"
}
}
```
现在,我们准备在`RepoAnalysis`类中实现`do_analyse_type`路由,如以下代码所示:
```py
[[RepoAnalysis]]
@route(analyse_type="*")
@templateArgs
def do_analyse_type(self, analyse_type):
fn = [analysis_fn for a_type,analysis_fn in analyses if a_type == analyse_type]
if len(fn) == 0:
return "No loader function found for {{analyse_type}}"
vis_info = fn[0](self._analyse_repo_owner, self._analyse_repo_name)
self.pdf = vis_info["pdf"]
return """
{{vis_info["chart_options"] | tojson}}
"""
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode12.py)。
路由有一个名为`analyse_type,`的参数,我们将其用作在`analyses`数组中查找`load`函数的键(注意,我再次使用列表推导来快速进行搜索)。 然后,我们调用传递回购所有者和名称的此函数来获取`vis_info` JSON 有效负载,并将 Pandas `DataFrame`存储到名为`pdf`的类变量中。 然后,返回的 HTML 片段将`pdf`用作`pd_entity`值,将`vis_info["chart_options"]`用作`pd_optio` `ns`。 在这里,我使用[`tojson` Jinja2 过滤器](http://jinja.pocoo.org/docs/templates/#list-of-builtin-filters)来确保在生成的 HTML 中正确进行了转义 。 即使已在栈上声明了`vis_info`变量,也允许我使用它,因为我为函数使用了`@templateArgs`装饰器。
在测试改进的应用之前,要做的最后一件事是确保主要的`GitHubTracking` PixieApp 类继承自`RepoAnalysis` PixieApp:
```py
@PixieApp
class GitHubTracking(RepoAnalysis):
@route()
def main_screen(self):
<
>
@route(query="*")
@templateArgs
def do_search(self, query):
<>
@route(page="*")
@templateArgs
def do_retrieve_page(self, page):
<>
app = GitHubTracking()
app.run()
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode13.py)。
“回购分析”页面的屏幕快照如下所示:
![Invoking the PixieDust display() API using pd_entity attribute](img/00060.jpeg)
GitHub 回购提交活动可视化
### 注意
[如果您想进一步试验,可以在此处找到 GitHub 跟踪应用第 2 部分的完整笔记本](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/GitHub%20Tracking%20Application/GitHub%20Sample%20Application%20-%20Part%202.ipynb)。
## 使用`pd_script`调用任意 Python 代码
在此部分中,我们研究`pd_script`定制属性,该属性使您可以在触发内核请求时运行任意 Python 代码。 有一些规则可以控制 Python 代码的执行方式:
* 该代码可以使用`self`关键字以及在笔记本中定义的任何变量,函数和类访问 PixieApp 类,如以下示例所示:
```py
```
* 如果指定了`pd_target`,则在`target`元素中将输出使用`print`函数的任何语句。 如果不存在`pd_target`,则不是这种情况。 换句话说,您不能使用`pd_script`进行整页刷新(您必须使用`pd_options`属性),例如:
```py
from pixiedust.display.app import *
def call_me():
print("Hello from call_me")
@PixieApp
class Test():
@route()
def main_screen(self):
return """
"""
Test().run()
```
### 注意
[您可以在此处找到的代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode14.py)。
* 如果代码包含多行,建议使用`pd_script`子元素,它使您可以使用多行编写 Python 代码。 使用此格式时,请确保代码遵循缩进的 Python 语言规则,例如:
```py
@PixieApp
class Test():
@route()
def main_screen(self):
return """
"""
Test().run()
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode15.py)。
`pd_script`的一种常见用例是在触发内核请求之前更新服务器上的某些状态。 通过添加复选框在折线图和数据统计摘要之间切换可视化,让我们将此技术应用于`Github Tracking`应用。
在`do_analyse_repo`返回的 HTML 片段中,我们添加了用于在图表和统计信息摘要之间切换的复选框元素:
```py
[[RepoAnalysis]]
...
return """
"""
```
在`checkbox`元素中,包含`pd_script`属性,该属性根据`checkbox`元素的状态修改服务器上的变量状态。 我们使用`$val()`指令检索`show_stats_{{prefix}}`元素的值,并将其与`true string`进行比较。 当用户单击复选框时,服务器上的状态将立即更改,并且当用户下次单击菜单时,将显示统计信息而不是图表。
现在,我们需要更改`do_analyse_type`路由以动态配置`pd_entity`和`chart_options`:
```py
[[RepoAnalysis]]
@route(analyse_type="*")
@templateArgs
def do_analyse_type(self, analyse_type):
fn = [analysis_fn for a_type,analysis_fn in analyses if a_type == analyse_type]
if len(fn) == 0:
return "No loader function found for {{analyse_type}}"
vis_info = fn[0](self._analyse_repo_owner, self._analyse_repo_name)
self.pdf = vis_info["pdf"]
chart_options = {"handlerId":"dataframe"} if self.show_stats else vis_info["chart_options"]
return """
{{chart_options | tojson}}
"""
```
### 注意
[您可以在这里找到文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode16.py)。
`chart_options`现在是一个局部变量,如果`show_stats`为`true`,则包含显示为表格的选项;如果不是,则包含常规折线图选项。
`pd_entity`现在设置为`get_pdf()`方法,该方法负责基于`show_stats`变量返回适当的`DataFrame`:
```py
def get_pdf(self):
if self.show_stats:
summary = self.pdf.describe()
summary.insert(0, "Stat", summary.index)
return summary
return self.pdf
```
### 注意
[您可以在此处找到代码文件](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode17.py)。
我们使用 Pandas [`describe()`方法](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.describe.html)返回包含摘要的`DataFrame`统计信息,例如计数,均值,标准差等。 我们还确保此`DataFrame`的第一列包含统计信息的名称。
我们需要做的最后一个更改是初始化`show_stats`变量,因为如果不这样做,那么第一次检查它时,我们会得到`AttributeError`异常。
由于使用`@PixieApp`装饰器的内部机制,因此无法使用`__init__`方法来初始化变量。 相反,PixieApp 编程模型要求您使用一种称为`setup,`的方法,该方法可以确保在应用启动时被调用:
```py
@PixieApp
class RepoAnalysis():
def setup(self):
self.show_stats = False
...
```
### 注意
**注意**:如果您有一个从其他 PixieApps 继承的类,则 PixieApp 框架将使用它们的出现顺序自动从基类中调用所有`setup`函数。
以下屏幕截图显示了正在显示的摘要统计信息:
![Invoking arbitrary Python code with pd_script](img/00061.jpeg)
GitHub 存储库的摘要统计信息
### 注意
[您可以在此处找到`Github Tracking`应用第 3 部分的完整笔记本](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/GitHub%20Tracking%20Application/GitHub%20Sample%20Application%20-%20Part%203.ipynb)。
## 使用`pd_refresh`使应用响应更快
我们希望通过使**显示统计信息**按钮直接显示统计信息表格,而不是让用户再次单击菜单来改善用户体验。 类似于加载**提交活动**的菜单,我们可以向复选框添加`pd_options`属性,其中`pd_target`属性指向`analyse_vis{{prefix}}`元素。 无需在触发新显示的每个控件中复制`pd_options`,我们可以将其添加到`analyse_vis{{prefix}}`一次,并使用`pd_refresh`属性对其进行更新。
下图显示了两种设计之间的差异:
![Making the application more responsive with pd_refresh](img/00062.jpeg)
有和没有`pd_refresh`的序列图
在这两种情况下,步骤 1 都是在服务器端更新某些状态。 在步骤 2 中显示的**控件**调用路由的情况下,请求规范存储在控件本身中,触发步骤 3,该步骤将生成 HTML 片段并将其注入目标元素中 。 使用`pd_refresh`,控件不知道`pd_options`来调用路由,相反,它仅使用`pd_refresh`来向目标元素发信号,从而依次调用路由。 在这种设计中,我们只需要在目标元素中指定一次请求,并且用户控件只需要在触发刷新之前更新状态即可。 这使实现更易于维护。
为了更好地理解两种设计之间的差异,让我们比较`RepoAnalysis`类中的两种实现。
对于**分析**菜单,更改如下:
之前,控件触发了`analyse_type`路由,将`{{analysis}}`选择作为内核请求的一部分传递给了`analyse_vis{{prefix}}`:
```py
{{analysis}}
```
之后,控件现在将选择状态存储为类字段,并要求`analyse_vis{{prefix}}`元素刷新自身:
```py
{{analysis}}
```
同样,**显示统计信息**复选框的更改如下:
在复选框之前,只需在类中设置`show_stats`状态即可; 用户必须再次单击菜单以获得可视化效果:
```py
```
之后,由于具有`pd_refresh`属性,因此一旦选中该复选框,可视化文件就会更新:
```py
```
最后,`analyse_vis{{prefix}}`元素的更改如下:
之前,该元素不知道如何更新自身,它依靠其他控件将请求定向到适当的路由:
```py
```
之后,元素将携带内核配置以进行自我更新。 任何控件现在都可以更改状态并调用刷新:
```py
```
### 注意
[您可以在以下位置找到`Github Tracking`应用第 4 部分的完整笔记本](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/GitHub%20Tracking%20Application/GitHub%20Sample%20Application%20-%20Part%204.ipynb)。
## 创建可重用的小部件
PixieApp 编程模型提供了一种机制,用于将 HTML 和复杂 UI 构造的逻辑打包到一个小部件中,可以轻松地从其他 PixieApps 中调用该小部件。 创建窗口小部件的步骤如下:
1. 创建一个将包含小部件的 PixieApp 类。
2. C 创建一个带有特殊`widget`属性的路由,如示例所示:
```py
@route(widget="my_widget")
```
这将是小部件的起始路径。
3. 创建从小部件 PixieApp 类继承的使用者 PixieApp 类。
4. 通过使用`pd_widget`属性从``元素调用窗口小部件。
这是的示例,介绍如何创建小部件和使用者 PixieApp 类:
```py
from pixiedust.display.app import *
@PixieApp
class WidgetApp():
@route(widget="my_widget")
def widget_main_screen(self):
return "
Hello World Widget
"
@PixieApp
class ConsumerApp(WidgetApp):
@route()
def main_screen(self):
return """
"""
ConsumerApp.run()
```
### 注意
[您可以在此处找到代码](https://github.com/DTAIEB/Thoughtful-Data-Science/blob/master/chapter%203/sampleCode18.py)。
# 总结
在本章中,我们介绍了 PixieApp 编程模型的基本构建模块,使您可以直接在笔记本中创建强大的工具和仪表板。
我们还通过展示如何构建`Github Tracking`示例应用(包括详细的代码示例)来说明 PixieApp 的概念和技术。 最佳做法和更高级的 PixieApp 概念将在第 5 章,“最佳做法和高级 PixieDust 概念”中进行介绍,包括事件,流和调试。
到目前为止,您应该希望对 Jupyter 笔记本,PixieDust 和 PixieApps 如何使数据科学家和开发人员能够通过单一工具(例如 Jupyter 笔记本)进行协作来帮助弥合数据科学家和开发人员之间的差距有所了解。
在下一章中,我们将展示如何从笔记本中释放 PixieApp 并使用 PixieGateway 微服务服务器将其发布为 Web 应用。