TowardsDataScience 博客中文翻译 2020(四百九十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

为您的 SQL 数据库构建回归测试框架

原文:https://towardsdatascience.com/how-to-regression-test-your-sql-database-c6c8d66848c5?source=collection_archive---------12-----------------------

了解如何使用 Python 构建 SQL 回归框架

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片

当谈到测试软件时,有无数种方法可以做。回归测试就是其中的一种方法,它本质上是一种比较软件的两个不同版本的输出的方法。

比较软件的两个版本的输出将很快突出相同点和不同点。如果软件中的一个变化导致了一个意想不到的不同,它会立即被突出显示。这同样适用于预期有差异但没有发现的情况。

随着解决方案规模的增长,以及它们的输出在数量和复杂性上的增加,必须建立一个框架来允许这种类型的测试快速、轻松地执行。手动测试容易出错且耗时。

这个想法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

活动创作者Unsplash 上拍摄的照片

这个想法非常简单。构建一个 python 脚本,允许我们快速有效地比较两个 SQL 数据库。该脚本需要尽可能可扩展和可配置。

更具体地说,该脚本应该允许多环境设置和多数据集比较。换句话说,用户应该能够设置不同的环境和要执行的 SQL 命令,然后通过几个配置更改就可以运行它。

事不宜迟,让我们进入细节。

魔力

连接到 SQL Server

连接到 SQL server 很简单,但是根据 SQL server 的类型,您需要使用不同的库。对于这个例子,我使用的是 Microsoft SQL server,因此使用的是 pyodbc 库。

import pyodbc
import pandas as pd
Env1 = pyodbc.connect('Driver = {SQL Server}; Server = ServerName; Database = DatabaseName; Trusted_Connection = yes;')
SQL = 'SELECT * FROM Table1'
print(pd.read_sql_query(SQL, con = Env1)

比较两个数据帧

这里没有必要重新发明轮子;我们将使用现有的库来比较数据集。

import datacompy
import pandas as pd
df1 = pd.read_csv('FL_insurance_sample.csv')
df2 = pd.read_csv('FL_insurance_sample - Copy.csv')
compare = datacompy.Compare(
    df1,
    df2,
    join_columns='policyID',  #You can also specify a list of columns eg ['policyID','statecode']
    abs_tol=0, #Optional, defaults to 0
    rel_tol=0, #Optional, defaults to 0
    df1_name='Original', #Optional, defaults to 'df1'
    df2_name='New' #Optional, defaults to 'df2'
)
print(compare.report())

有趣的是

这个练习有趣的部分是把所有的东西放在一起。本质上,我们需要创建一个要执行的 SQL 语句列表,以及用于比较的结果的相关数据键。

一旦定义好,我们就可以遍历 SQL 语句列表,在我们的两个环境中执行每个语句。一旦我们得到结果,使用我们的比较库,我们可以评估是否有任何差异。为了提高效率和减少返回的数据,我们将在尝试获得更细粒度的反馈之前,首先进行“哑”的同类比较。

代码

import pyodbc, datacompy
import pandas as pd###############Set up your Environment connections#########################
Env1 = pyodbc.connect('Driver = {SQL Server}; Server = ServerName; Database = DatabaseName; Trusted_Connection = yes;')
Env2 = pyodbc.connect('Driver = {SQL Server}; Server = ServerName2; Database = DatabaseName2; Trusted_Connection = yes;')########Environment Set Up###################################
Original = Env1
Target = Env2######SQL Statements to Execute#############################
SQLStatement = [
    #[ReportName, Stored Proc, [data keys]]
    ['Report1','EXEC StoredProc1 [@Var1](http://twitter.com/Var1) = "Woop" , [@Var2](http://twitter.com/Var2) = "Woop2"', ['Key1', 'Key2']],
    ['Report2','EXEC StoredProc2 [@Var1](http://twitter.com/Var1) = "Woop", [@Var2](http://twitter.com/Var2) = "Woop2"', ['Key3','Key4']]
]###############################
def compareResults(reportName, df1, df2, joinOnKeys):
    if df1.equals(df2) == True:
        print('###########################################################')
        print(reportName, 'Success')
    else:
        print('###########################################################')
        print(reportName, 'Fail')
        compare = datacompy.Compare(
            df1,
            df2,
            join_columns= joinOnKeys,
            abs_tol = 0, 
            rel_tol = 0,
            df1_name= 'Original',
            df2_name= 'Target'
        )
        print(compare.report(sample_count=20))for i in SQLStatement:
    results1 = pd.read_sql_query(i[1], con = Original)
    results2 = pd.read_sql_query(i[1], con = Target)
    compareResults(i[0], results1, results2, i[2])

示例输出

###########################################################
Report1 Success###########################################################
Report2 FailDataComPy Comparison
--------------------
DataFrame Summary
-----------------
DataFrame  Columns   Rows
0  Original       18  36634
1       New       18  36634Column Summary--------------
Number of columns in common: 18
Number of columns in Original but not in New: 0
Number of columns in New but not in Original: 0Row Summary
-----------
Matched on: policyid
Any duplicates on match values: No
Absolute Tolerance: 0
Relative Tolerance: 0
Number of rows in common: 36,634
Number of rows in Original but not in New: 0
Number of rows in New but not in Original: 0
Number of rows with some compared columns unequal: 2
Number of rows with all compared columns equal: 36,632Column Comparison
-----------------
Number of columns compared with some values unequal: 2
Number of columns compared with all values equal: 16
Total number of values which compare unequal: 2Columns with Unequal Values or Types
------------------------------------
Column Original dtype New dtype  # Unequal   Max Diff  # Null Diff
0  eq_site_limit        float64   float64          1  190601.40            0
1  hu_site_limit        float64   float64          1   79375.76            0Sample Rows with Unequal Values
-------------------------------
policyid  eq_site_limit (Original)  eq_site_limit (New)
2    206893                  190724.4                123.0
policyid  hu_site_limit (Original)  hu_site_limit (New)
3    333743                  79520.76                145.0

结论

好了,伙计们!用不到 50 行代码,您就有了一个完整的 SQL 回归测试框架来帮助您确定下一个候选版本的有效性。

您所需要做的就是选择正确的两个环境,并定义您想要执行的存储过程的列表。

你会用不同的方式做事情吗,或者对改进这段代码有什么建议吗?让我知道!

如果你觉得这篇博客有用,你可能也会喜欢:

[## 构建用于比较数据的 Python UI

如何快速让您的非技术团队能够比较数据

towardsdatascience.com](/building-a-python-ui-for-comparing-data-13c10693d9e4) [## 用 Python 中的 PyAutoGUI 实现自动化 UI 测试

回归测试你的用户界面的一个快速简单的方法

towardsdatascience.com](/automate-ui-testing-with-pyautogui-in-python-4a3762121973) [## 从 Excel 和 Pandas 数据框架到 SQL

如何使用您的 SQL 知识来利用 Python 的熊猫

medium.com](https://medium.com/financeexplained/from-excel-and-pandas-dataframes-to-sql-f6e9c6b4a36a)

如何删除拥有内容的 Tableau 服务器用户

原文:https://towardsdatascience.com/how-to-remove-tableau-server-users-who-own-content-bb84fbd6948d?source=collection_archive---------52-----------------------

TABLEAU REST API: TABLEAU-API-LIB 教程

使用 Python 和 REST API 彻底删除内容所有者

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过几个 REST API 调用彻底打破这种局面(由 Unsplash 上的 Kelly Sikkema 拍摄)

这个在我们的 YouTube 频道(Devyx)也有报道: 看看

分手很难。Tableau 用户来来去去,幸运的是,我们手边有一些工具,可以帮助我们彻底摆脱处理孤立内容时令人不快的细微差别。

本文探索了一种使删除 Tableau 服务器用户变得轻松的方法。无论您的团队是手动添加和删除用户,还是采用一点自动化,本教程中涵盖的主题都有助于简化这一过程,确保被删除的用户不再拥有服务器上的任何内容,防止用户进入“未经许可”的墓地,并确保服务器上的内容不属于未经许可的用户。

本教程介绍了如何使用 Python tableau-api-lib 包,并且是如何像使用小桶一样使用 tableau 服务器的系列教程的一部分,让您能够控制 Tableau 服务器的 REST API。

这些教程假设您已经安装了 Python 3。如果你还没有 Python 3,这将帮助你入门:安装 Python 的指南

搭建舞台

当你删除一个不拥有任何内容的 Tableau 用户时,这是小菜一碟。不会弹出任何错误,您所看到的只是一条愉快的确认消息,表明用户已被成功删除。

但是,如果您尝试删除拥有内容的 Tableau 用户,您将收到一条错误消息,通知您不能删除该用户,因为他们在服务器上被列为内容的所有者。

当我们试图删除拥有内容的用户时,该用户反而变成了某种 Tableau 僵尸,被放逐到“未经许可”的用户角色,但仍然拥有其内容的所有权。Tableau 不会让任何内容成为孤儿,因此所有者的精神会在 Tableau 服务器上徘徊,直到其未完成的业务(内容所有权)得到处理,用户可以完全移除。

构建解决方案

那么我们能做些什么呢?在本教程中,我们将演示如何使用 REST API 来完成以下任务:

  1. 确定被删除的用户是否拥有任何内容
  2. 如果用户拥有内容,请指定另一个用户作为新的所有者
  3. 删除用户

让我们也强调一下用户在 Tableau 服务器中可以拥有哪些对象:

  1. 工作簿
  2. 数据源
  3. 项目

目前,Tableau Server REST API 不支持更改项目所有者。如果您的用户拥有一个项目,您将需要在删除用户之前手动更改该项目的所有权(对不起,请不要杀死 messenger)。

如果你像我一样,希望能够使用 REST API 来改变项目所有者,请出去在 Tableau 论坛上投票支持这个想法。

步骤 1:确保你已经安装了 tableau-api-lib

即使你是这些教程的专家,帮自己一个忙,下载最新版本的库。

pip install --upgrade tableau-api-lib

不熟悉 Python 这个东西?别担心,你会很快明白的。跟随这个入门教程。该教程将引导您使用 tableau-api-lib 连接到 Tableau 服务器。

步骤 2:进入 Tableau 服务器环境

使用下面的代码作为连接到您的服务器的模板。在接下来的步骤中,我们将使用一次性代码行来构建这个 boiler 板。在本文的最后,您会发现一个合并的代码块,为了方便起见,您可以复制/粘贴它。

from tableau_api_lib import TableauServerConnection
from tableau_api_lib.utils.querying import queryingtableau_server_config = {
        'my_env': {
                'server': '[https://YourTableauServer.com'](https://tableaupoc.interworks.com%27/),
                'api_version': '<YOUR_API_VERSION>',
                'username': '<YOUR_USERNAME>',
                'password': '<YOUR_PASSWORD>',
                'site_name': '<YOUR_SITE_NAME>',
                'site_url': '<YOUR_SITE_CONTENT_URL>'
        }
}conn = TableauServerConnection(tableau_server_config, env='my_env')
conn.sign_in()

有趣的事实:你也可以使用个人访问令牌,假设你在 Tableau Server 2019.4 或更新版本上。如果你对访问令牌很感兴趣,请查看我的文章了解如何使用它们的细节。

步骤 3:建立服务器上所有内容所有者的列表

这个难题的第一部分是为我们自己建立一个服务器上所有内容所有者的列表。一旦我们有了这个,我们可以做一个简单的检查,看看我们计划删除的用户是否是服务器上任何内容的所有者。

为了建立内容所有者的完整列表,我们需要:

  1. 获取所有项目所有者
  2. 获取所有工作簿所有者
  3. 获取所有数据源所有者
  4. 获取所有流所有者
  5. 整合上述各项的相关信息

完整的代码作为 GitHub 要点提供在本文的最后,所以我不会在这里详细描述每个步骤,但是让我们强调一下 tableau-api-lib 在这里是如何派上用场的。

让我们以工作簿为例。这里有一个模式,我们可以用它来提取所有工作簿的信息,这也允许我们提取关于工作簿所有者的信息。

workbooks_df = querying.get_workbooks_dataframe(conn)

结果 workbooks_df 将如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,放大“所有者”栏,我们希望从中提取一些有价值的信息。这个金块就是拥有内容的用户的“id”值。让我们通过使用 tableau-api-lib 的 flatten_dict_column util 函数来提取嵌套的 dict / JSON 值。

workbooks_df = flatten_dict_column(workbooks_df, keys=['id'], col_name='owner')

如果您看一看文章末尾的完整代码,您会注意到我们还为创建的每个数据帧添加了一个“content_type”列。

workbooks_df['content_type'] = 'workbook'

这只是为了当我们迭代用户拥有的所有内容时,我们可以识别要使用的适当的 REST API 端点。更新工作簿、数据源和流有不同的端点,我们需要根据更新的内容类型调用正确的 tableau-api-lib 方法。

如果我们对我们的工作簿、数据源、流程和项目遵循这种模式,那么我们可以很容易地组合结果数据(请参见文章末尾的代码中的例子)。

步骤 4:指定一个将接受孤立内容的用户

因为我们要更改内容的所有权以便删除一个用户,所以我们需要指定另一个 Tableau 用户来获得内容的所有权。这个过程有几种有效的方法。

您可以随机选择任何一个服务器管理员用户,并将内容分配给他们中的一个,您可以按名称选择用户,或者您可以挑选用户 ID 值,并选择您喜欢的任何一个。

对于这个例子,我的测试环境总共有…两个用户。所以我只是叫出了另一个用户的名字,就像这样:

new_owner_id = list(users_df[users_df['name'] == 'estam']['id']).pop()

用户名为“estam”的用户将成为被删除用户以前拥有的任何内容的新所有者。

说到被删除的用户,我也在我的工作流中进行了定义:

user_id_to_remove = list(users_df[users_df['name'] == 'estam2']['id']).pop()

同样,有许多有效的方法来定义这些值。重要的是,您有一个新所有者的用户 ID,以及一个被删除用户的用户 ID。

步骤 5:定义一个改变内容所有权的函数

我运行的实现遍历被删除用户拥有的所有内容。对于该用户拥有的每一条内容,都会调用一个名为的函数 change_content_owner

关于如何定义该函数的确切细节,请参见本文末尾的完整代码。

总之,该函数包含一些 if 语句,这些语句根据正在处理的内容类型执行不同的 REST API 更新调用。如果工作簿需要所有权变更,那么将调用’更新工作簿 ’ REST API 端点。如果一个流需要一个所有权,那么就调用’ Update Flow ’ REST API 端点。等等,等等。

现在,鉴于项目还不能通过 REST API 调用来更新,这对我们来说有点不方便。在将这种能力添加到 REST API 的可能性领域之前,我们将需要手动更改这种所有权。如果您真的对自动化感兴趣,您可以修改我在这里定义的函数,以便在拥有项目的用户被删除时触发 Slack 消息或电子邮件。该消息可能会提醒您的服务器管理员团队,该用户需要一些手动操作才能从服务器中成功删除。

第六步:证明流程有效

下面是我的流程运行的一些示例输出。在本演示中,被删除的用户拥有一个工作簿和一个数据源。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tableau 服务器的 JSON 响应确认了我们的所有权交换是成功的。

步骤 7:总结局限性

在我们结束之前,让我们回顾一下这个过程的局限性以及这些局限性来自哪里。

  1. 您不能删除拥有内容的用户
  2. 在成功删除用户之前,您必须更改用户拥有的所有内容的所有权
  3. 您不能通过 REST API 调用来改变项目的所有权(希望这个功能将很快被添加进来;去投票吧!)
  4. 试图删除拥有内容的用户将导致用户的角色被修改为“未经许可”,但他们仍将拥有内容。
  5. 当您更改工作簿或数据源的所有者时,任何嵌入的凭据都将失效。

想了解更多?查看 Tableau 关于改变内容所有权的文档

包装它

这就是关于删除拥有内容的 Tableau 用户的教程。归根结底,在内容所有权方面不能留下任何漏洞。

如果你的用户不拥有任何内容,这是小菜一碟!

如果您的用户确实拥有内容,请更改您的工作簿、数据源和流的所有权。如果您的用户拥有项目,您需要手动更改所有者,然后才能从服务器中成功删除用户。

感谢收听!如果您的团队需要 Tableau Server REST API 自动化方面的帮助,请联系我们。

合并代码

使用这个 GitHub 要点作为模板,将本教程中的概念整合到你自己的工作流程中。

如何用 21 行 Python 在 Mac 上重命名截图

原文:https://towardsdatascience.com/how-to-rename-screenshots-on-mac-with-21-lines-of-python-c3c2366f6eec?source=collection_archive---------85-----------------------

大蟒

使用操作系统和日期时间模块,通常重复的任务现在只需点击一下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小姐姐为这个项目创作的原创图片

在 Mac 上截屏很简单,只需点击 Command-Shift-3 进行全屏截图,或者点击 Command-Shift-4 进行部分屏幕截图。你也可以用终端为你的截图设置一个自定义标签,但是有时候你需要更多的自定义。

作为一个为不同项目拍摄批量截图的人,我经常需要重命名截图以适应这种格式:

project name _ date created _ screenshotcount . png

只有 21 行 Python 代码,现在我只需点击桌面上的一个可执行文件,所有的截图都会被重命名以匹配上面的格式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Python 脚本在 Mac 上重命名屏幕截图

每当我需要重命名一批 20-30 个截图时,这可以节省我大约一分钟的时间。但是随着时间的推移,这种情况会越来越多,所以我决定使用 Python 来自动化这些枯燥的东西!

下面看看这个脚本是如何工作的,以及如何让它在 Mac 上可执行。

使用 Python 的 os 和 datetime 模块在 Mac 上重命名屏幕截图

在写这个脚本之前,我没有改变任何预先构建的截图设置。默认情况下,我的截图如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我电脑上的截图示例

因为我为不同的项目拍摄了一批截图,所以我希望每个文件名都以项目标签开头。我还想保留截图拍摄的原始日期,但我不需要时间。最后,为了确保没有重复,每个截图的文件名后面都应该有一个数字。

配置您的截图标签和当前工作目录

首先,我们需要导入必要的模块:

import os
from datetime import datetime

操作系统模块允许我们通过 Python 与您的操作系统交互。使用这个模块,我们可以读写文件、操作路径等等。 datetime 模块也内置在 Python 中,它为我们提供了在脚本中操作日期和时间的类。

接下来,我们将添加一个项目名称提示,用于标记每个截图的文件名:

project = input('Enter the project name: ')

然后,我们需要启用我们的脚本来找到截图。为此,我们将当前工作目录更改为我们系统的桌面。我们可以手动找到个人桌面的路径,并将其硬编码到脚本中,但为了避免这样做,我们可以使用这两行代码:

desktop = os.path.join(os.environ['HOME'], 'Desktop')
os.chdir(desktop)

我们来分析一下。

os.environ ”将返回我们的“环境变量”的字典,这些变量是我们的 Python 脚本运行所在的系统的特性。写出" os.environ[‘HOME’] "将返回字典的一个子集,其中只有我们的主目录的路径名。在 Mac 上,根据您配置机器名称的方式,您将得到类似于以下内容的内容:

/Users/firstname.lastname

要获得桌面的路径,我们需要在上面的字符串中添加“/Desktop”。现在,我们可以把两个字符串加在一起,但是一旦你开始把两个以上的字符串加在一起,记住在哪里放反斜线就变得复杂了。

为了避免硬编码反斜杠,我们使用了" os.path.join() "函数。这将我们提供给函数的路径合并成一个文件路径,而不必手动添加必要的反斜杠。我们提供的两个参数是“os.environ[‘Home’]”,它将给出您的系统的主目录,以及字符串“Desktop”。

运行 os.path.join()函数将得到以下结果:

/Users/firstname.lastname/Desktop

接下来,我们需要用“ os.chdir() ”函数改变我们的工作目录,提供“桌面”变量,该变量包含我们机器的桌面路径。这将允许我们的脚本在桌面上搜索截图,我们现在就这样做。

在桌面上查找和重命名屏幕截图

首先,为了避免重复的文件名,我们将把“count”变量赋给 0。在下面的 for 循环中,我们将在每次找到并重命名屏幕截图时给这个变量加 1。

count = 0

现在,这里是 for 循环:

for original_file in os.listdir():
    if 'Screen Shot' in original_file:
        count += 1
        birth_time = os.stat(original_file).st_birthtime
        birth_time = datetime.fromtimestamp(birth_time)
        birth_time = birth_time.strftime("%d-%m-%Y")
        new_file = project + "_" + birth_time + "_" + str(count) + ".png"
        os.rename(original_file, new_file)

让我们把这个循环分解成几个部分。

" os.listdir() "输出给定目录中所有条目的文件名列表。默认情况下,它将在我们当前的工作目录中查找所有条目,我们之前将该目录指定为桌面。

例如,如果我们的桌面上有一个 Excel 文件和一个文本文件,os.listdir()会给出:

['samplefile.xlsx', 'samplefile2.txt']

for 循环的目的是遍历每个条目,看看它们的名称中是否有“Screen Shot”。这是因为在 Mac 上,截屏文件名的默认命名约定是总是“截屏”,然后是截屏拍摄的日期和时间。为此,我们只需检查“截图”是否在我们从“os.listdir()”中找到的文件名中,如下所示:

if 'Screen Shot' in original_file:

如果满足该条件,脚本将开始重命名该文件的过程。在 if 语句下的第一行,我们写“count+=1 ”,将前面初始化的计数器变量加 1。这将在以后添加到文件名中。

然后,为了获取截屏拍摄的日期,我们使用" os.stat() "来获取文件的一些元数据,并作为参数传递。

birth_time = os.stat(original_file).st_birthtime

这里,我们传入了“original_file”变量,该变量只包含符合“屏幕截图”条件的每个文件的名称。我们对“st_birthtime”结果特别感兴趣,它返回文件创建时的时间戳。

为了将时间戳转换成更容易阅读的文件名,我们将使用“fromtimestamp()”datetime 类方法将其转换成 datetime 对象。

birth_time = datetime.fromtimestamp(birth_time)

接下来,我们将把我们的 datetime 对象重新格式化为我们希望它打印在屏幕截图的文件名上的样子。为此,我们使用“ strftime ”或“来自时间的字符串”,其中我们获取一个 datetime 对象并解析它以获得我们需要的部分。在本例中,我希望将类似“2019 年 12 月 1 日”的日期显示为“2019 年 1 月 12 日”。为此,我们使用以下 strftime 运算符:

birth_time = birth_time.strftime("%d-%m-%Y")

现在我们有了文件名的所有组成部分。为了把它们放在一起,我们要用这条线把所有的变量结合起来:

new_file = project + "_" + birth_time + "_" + str(count) + ".png"

这里,我们有从一开始的输入中获得的“项目”标签,截图的“出生时间”格式的字符串,以及转换为字符串的“计数”变量。为了使名称看起来更好,我们还在文件名部分之间添加了下划线。然后,我们加上”。png”作为图像扩展名。

为了实际重命名文件,我们使用“ os.rename() ”,它有两个参数:原始文件名和替换文件名。

os.rename(original_file, new_file)

仅此而已!

这个循环将遍历我们桌面上的每个文件,并根据我们刚刚遍历的过程编辑每个名称中带有“Screen Shot”的文件。

使 Python 文件可执行

我们可以从我们的终端运行该脚本,但是要让我们只需单击桌面上的一个文件来重命名我们的屏幕截图,只需遵循以下步骤:

  1. 把这个添加到你的 Python 脚本的第一行:
#!/usr/bin/env python3

这被称为“shebang ”,它允许执行 python 脚本,而不必在终端的文件名前键入“Python”。

2。将 Python 脚本的文件扩展名更改为“”。命令”。

在我的例子中,Python 文件被称为“screenshots.py”,所以我将其重命名为“screenshots.command”。

3。在终端上,运行下面两行来创建您的"。命令“文件可执行:

cd Desktop
chmod +x filename.command

这假定您已经放置了您的"。命令”文件放在桌面上。如果您将它存储在不同的文件夹中,请将该文件夹的文件路径放在“cd”之后。

现在,您只需点击文件,您的 Python 脚本就会运行!

完整的代码可以在这里看到:

屏幕截图重命名脚本

我发现这是一种快速简单的方法,可以练习使用 os 模块来检查系统上的文件,同时集成 datetime 来定制屏幕截图名称。有很多方法可以进一步缩短这个脚本,比如删除第 7 行和第 8 行,将桌面的路径直接传递给 os.listdir()。为了清楚起见,我按照本文中的解释保留了它,但是当您在自己的任务中使用它时,可以随意调整脚本。

您也可以使用相同的步骤模板来转动一个”。py“将文件转换成可执行文件”。command”文件,每当你需要一个可点击的脚本。

祝你截图好运!

如何渲染 3D 足球传球网络

原文:https://towardsdatascience.com/how-to-render-3d-football-pass-networks-bf889f5ab1b0?source=collection_archive---------32-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用 Python 和 Cinema4D 实现体育数据可视化

传球网络分析长期以来一直是足球分析师中流行的可视化方法。那么,为什么又有一篇关于它的文章呢?嗯,一个下雨的周日,我认为在我的电脑上运行别人的全面 Python 脚本来提升我的科学自尊是一个好主意,并为自己是一名多么伟大的数据科学家而沾沾自喜。毫不奇怪,该脚本在第一次尝试时成功执行——显然不是因为我的编程天才,而是因为我使用的脚本的优秀质量,即用于使用 Matplotlib 构建可定制的传球网络的 Sergio Llana 的 GitHub 库。所以在一键按下*【运行脚本】*后,我骄傲地盯着我的第一个“自编程序”的 pass 网络:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2D 通行证网络。图片作者。科迪由塞尔吉奥利亚纳

但随后一个念头涌上心头:“如果我能沉浸在网络中,看看到底打了多少个低传球或高传球,那不是很好吗?”。就在那时,我开始尝试 Cinema4D 的内置 Python 模块。令我自己惊讶的是,我能够不费吹灰之力就实现我的想法。在这篇文章中,我想分享我的(可能是非传统的)可视化方法。

免责声明:如果你是来寻求分析见解的,恐怕你会失望。这篇文章是关于如何将传统的 2D 传球网络转换成 3D 的。

如何开始

如前所述,我之所以能够取得我的成果,是因为有人慷慨地分享了他/她的代码。我用于数据准备的许多代码段直接取自 Sergio Llana 的 GitHub 页面(特别是statspamb 的开放数据仓库的导入和处理)。人们毕竟是懒惰的,所以复制粘贴代码没有什么可耻的!*…对吗?*也就是说,你可以在我的 GitHub 上找到我的 Python 脚本和 C4D 项目文件。

数据采集

我很惊讶现在有这么多的球探数据是公开的。对于这个项目,我使用了发生在 2018 年世界杯小组赛期间的比赛葡萄牙 vs 西班牙(3:3) ,。你可以在 StatsBomb 找到 2018 年世界杯所有比赛的赛事数据。另一个值得一提的重要来源是最近在《自然》杂志上发表的一篇文章,其中附有时空匹配事件的公共数据集。

网络基础

足球传球网络由一组通过“边”(即传球)连接的圆形“节点”(即球员)组成。从现在开始,我将使用术语“样条线”来代替“边缘”。以后你就明白为什么了。节点的大小通常代表玩家在游戏中完成的传球总数。每个球员的场地位置(也称为战术阵容)可以通过以下方法获得:(a)根据每个球员的时空跟踪数据(如果可用)计算平均 XY 坐标,或者(b)根据每个球员的侦察事件(即传球)计算平均 XY 坐标。传统上,边的重量(厚度)代表两个球员之间完成传球的次数。

准备数据

我将跳过如何解析 StatsBomb 的 JSON 文件并将完整的 pass 连接提取到单独的数据帧中的部分。塞尔吉奥的剧本非常管用,所以我只做了些小调整。我事先知道我要渲染每个节点,一个一个的通过。这就是为什么我创建了两个数据帧,可以保存为文本文件,并使用内置的 Python 连接器导入 Cinema4D:

  • 对于玩家节点,我们需要每个玩家的名字,平均场坐标,以及完成的总遍数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

玩家节点表。图片作者。

  • 对于传球样条,我们需要一个列表,包含(a)所有球员配对*【pair _ key】,* (b)每个球员配对完成的低传球和高传球的数量,以及©传球和接球球员的平均场地坐标:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

传递值表的一部分。图片作者。

使用 Cinema4D 进行网络渲染

Cinema4D (C4D)是一款 3D 设计、动画和渲染软件,主要用于运动图形、建模和纹理;但是,它附带了一个全面的 Python 模块。也就是说,几乎每个用户操作都可以通过 Python 命令来执行,从而允许设计过程的自动化。下面是一个我自定义的用户界面的例子,显示了三维视口(左上),对象管理器(右上),和材质管理器(右中)。介于两者之间的是 Python 脚本管理器,允许以编程方式创建单个对象以及整个场景。接下来的两段将展示只用几行代码创建任何 3D 对象是多么容易。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Cinema4D Python 接口。图片作者。

生成玩家节点

我将用一个简单的圆盘来代表一个玩家的节点。在 C4D,通过 Python 命令创建一个磁盘并更改其属性非常简单。下面的代码示例在原点(0,0,0)生成一个外径为 100 厘米的圆盘:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Cinema4D 磁盘示例。图片作者。

生成刀路轨迹

我不想显示球员之间的单个加权边(代表传球次数),而是想单独渲染每个传球。为了达到这种效果,我们必须引入一些随机性…

我发现渲染两个球员之间传球轨迹最简单的方法是创建一个样条对象。最初,我认为每个样条应该只包括起点(传球手)和终点(接球手),但这将导致完美的直线——正如我们从传统传球网络中所知的那样。相反,我想创造这样一种印象:( a)每个通道轨迹都略有不同,( b)高通被渲染为一个弧形。这就是为什么我最终为每个通道轨迹使用三个样条点,这使我有可能向高通添加飞行曲线,并向中心样条点引入一些随机偏移(创建每个通道单独显示的假象)。下面的代码示例在两点之间生成十个随机样条线对象。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Cinema4D 随机通过轨迹示例。图片作者。

运行循环

既然我们知道了如何生成球员节点和传球轨迹,那么是时候用我们球员特有的值替换默认值了。这最适合使用一个循环来遍历播放器节点文件,并为每个播放器创建一个名称和磁盘。第二个循环通过传球值文件进行迭代,并在球员配对之间生成随机传球轨迹——取决于他们之间完成的传球次数。

收尾

现在剩下要做的就是添加场线,选择你最喜欢的颜色(又名材料),添加一些信息文本,找到一个合适的相机角度,根据你的喜好调整渲染设置——然后点击*【渲染】*按钮:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

关键外卖?

对于典型的足球分析师来说,可以说很少。但是如果你像我一样,你的兴趣介于数据科学和图形设计之间,你会惊讶于编程语言(Python)和 3D 设计软件(C4D)的结合所能实现的东西。

诚然,C4D 不一定被认为是数据科学家的标准工具,你也需要投入一些时间来学习基础知识。与传统的 2D 网络相比,3D pass 网络是否提供了任何附加值,这仍然是一个有争议的问题。尽管如此,我还是很喜欢在 C4D 尝试 Python,并且我将来肯定会更频繁地集成它。

接下来是什么?

动画! C4D 展示了它在动画方面的真正实力,这很棒,因为有些人可能会认为静态通行证网络提供了对游戏相当肤浅的洞察。相反,展示一个团队的网络动态如何随时间变化(以及它如何对对手的网络做出反应)可以提供更深入的见解。让我们看看有什么可能。我会随时通知你的!

感谢您阅读我的文章!如果您有任何问题或反馈,或者您只是想联系我,请不要犹豫,请联系我。

如何完全在浏览器中呈现交互式天气模型

原文:https://towardsdatascience.com/how-to-render-interactive-weather-models-entirely-in-the-browser-5ff2db881cb8?source=collection_archive---------10-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

WPC 概率冬季降水指导 72 小时降雪模型,在浏览器中渲染并添加到传单地图。

完全使用 JavaScript 将多维地理空间栅格渲染到 web 地图上的技术和用例。

气象爱好者、气象学家和风暴追逐者都知道网络上有无数的气象模型可视化网站。这些平台允许你查看各种参数、级别和几十种不同天气模型的预报时间。例如,当准备一天的风暴追逐时,追逐者可能会查看某个区域的几个参数-早上的露点、当天晚些时候的海角以及下午的 HRRR 模拟反射率。通常提供菜单、按钮和滑块来切换预测时间、模型和参数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

北美模型在离地面两米处的温度,使用杜佩奇学院的模型观察器。

通常,所有这些天气模型查看器都是用户访问预渲染的非空间天气模型的简单方式,尽管原始数据实际上是地理空间数据。尽管界面不同,但天气模型查看器中的许多内容是一致的:

  • 地理区域是固定的。您只能查看预设区域,例如城市、州或地区列表。输出图像的大小也是固定的。一些查看器允许您放大某些区域并等待新的渲染。
  • 地理图层和信息稀缺或不存在。在天气模型图像上,您可能只有道路或县是可见的,这可能会使特定区域难以精确定位。
  • 许多提供者拥有通常不可查询的查看器,这意味着要获得任何给定点的实际值,需要将数据的颜色与图例进行比较。
  • 模型层组合和配色方案是静态的。例如,您不能在另一个变量上叠加特定高度的风流线,除非提供商提供了该组合。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TropicalTidbit 的模型查看器。不想让地图上的所有图层同时出现?太糟糕了。

尽管许多提供者已经对数据的呈现进行了认真的考虑和思考,但是上述缺点在某些情况下可能是显著的和恶化的。例如,考虑到降雪量在几英里范围内可能会有几十英寸的差异,如果您只有县边界来确定自己的方位,评估山区的预测降雪量可能会非常具有挑战性。

考虑下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

即使作为一个拥有地理学位的预测者,我也很难从这张图表中获得有用的数据。看看科罗拉多州:

  1. 汽船和北科罗拉多黄等带的关系在哪里?它是蓝色的还是绿色的?(这将是 2 英寸和 6 英寸雪的区别)。
  2. 在 I-70 上,从韦尔山口以东到以西的降雪量分布如何变化?
  3. 戈尔山脉和它南边的十英里山脉相比,降雪量有多少?

如果你在科罗拉多州做了足够多的预测,你最终会记住精确到英里的县轮廓与山脉的关系,因为当你在许多模型查看器上查看整个州的天气分布时,这通常是你必须做的全部工作。

也就是说,提到这些平台的好处是很重要的——最明显的是使用预先渲染的静态图像。这些都很快,很容易在几秒钟内加载一个模型的所有预测小时数,并且您通常可以很容易地让服务器生成数据的动画 GIF。固定区域防止了数据的“误用”——例如放大到超出天气模型适用范围的分辨率。这两个因素允许一致的天气模型可视化,如果您正在下载图像并将其存档以供比较或重新分发,这将很有帮助。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在杜佩奇学院的浏览器上下载整个模型的预测既快又容易。

构建您自己的模型可视化工具—概述

在上一篇文章中,我讨论了如何构建和自动化自己的天气模型可视化工具。这项技术有一个有趣的变化,尽管服务器仍在渲染数据,但它将数据作为地理空间层(OGC·WMS)提供服务,可以添加到交互式 web 地图中。这在预渲染静态图像的性能和完全地理空间数据的交互性之间取得了良好的平衡。我将向您展示如何更进一步,完全在浏览器中进行渲染,不需要服务器!根据我上面的链接,这篇文章将假设你有 GeoTIFF 格式的天气模型,并投影到 EPSG:4326。只需一个 GDAL 命令,即可将原始天气模型数据(GRIB2 格式)转换为 GeoTIFF。

使用 web 浏览器在客户端呈现天气模型数据的缺点很多,但是这种技术有一些潜在的使用案例。2020 年的 Web 开发似乎已经足够成熟和强大,可以让它成为一个实际可行的提议。以下是我们将会遇到的主要问题:

  • 缓慢。JavaScript 并不是将原始多维栅格转换成 RGB 图像或矢量等值线的理想语言。
  • 数据使用。原始天气模型数据不像简单的 JPEG 那样紧凑。
  • 兼容性。我们编写的最终代码无法在 Internet Explorer 中运行,在较旧的硬件上运行起来也很糟糕。

相比之下,以下是我们将享受的一些好处:

  • 在完全交互式的 web 地图上呈现数据的能力,这意味着我们可以实时添加任意数量的地理数据层(或其他模型参数),并按照我们想要的方式设置样式。
  • 可以直接访问数据,例如查询原始模型数据的点或区域。这不仅仅是单击地图并获取某一点的数据,一个用例可能是定义一个位置列表或一组点,以表格格式显示数据或在地图上叠加数据。
  • 以我们选择的任何方式渲染天气模型数据或数据组合的能力,如栅格、等值线、等值线(等值线/等高线)、流线等。渲染、样式(例如,轮廓的宽度)和色带都可以由用户修改(如果需要),并即时更新可视化。各种模型甚至可以在数学上组合起来,以产生特定的可视化效果,而不是必须提前设置自动化处理脚本来实现。

我们也有一些策略来缓解一些棘手问题:

  • 使用 web workers 来处理和渲染地理标记。这将把密集的计算分散到用户 CPU 的各个核心上。
  • 使用云优化地理标签 (COGs)或 WCS 标准将原始数据传输给用户,仅限于他们感兴趣的区域和范围。此外,对土工格栅施加适当的压缩可以使它们减小到合理的尺寸。

记住这几点,你能想出一些可以享受客户端渲染好处的用例吗?对我来说,可视化的质量立即浮现在脑海中。我曾经使用定制的 QGIS 项目来导出带有天气模型数据的漂亮地图,但是仍然需要手工劳动。借助一个外观精美的 web 浏览器可视化平台,我可以快速地对多个模型、参数和预报时间进行分析,同时只需点击一下鼠标,就可以导出天气模型图形和地图进行重新分发。

在我之前提到的关于使用 MapServer 分发天气模型数据的文章中,我也遇到了一些具体的问题。这些问题都与可视化产品有关,在这些产品中,图形被从数据本身中大量移除,需要将各种栅格渲染为最终的 RGB 图像(例如,风速栅格上的流线或风倒钩),这与我打算在 MapServer 中采用的比例和样式不可知的模型数据分布策略模型不兼容。然而,当在 web 浏览器中处理时,这些问题很容易解决:

  • 天气模型组合计算。几个流行的天气模型可视化是不可能的,简单地分发转换后的数据,而不渲染一个 RGB 图像。最好的例子是复合反射率+降水类型,它显示了您可能期望的模拟雷达——雨显示为绿色-黄色-红色的典型色阶,而雪显示为蓝色阴影。这实际上需要至少三个不同天气模型参数栅格的组合,或者如果要处理所有降水类型(冰粒和冻雨),则需要五个。据我所知,没有办法在 MapServer 中即时完成这项工作,因为需要根据降水类型使用不同的色标,并组合成最终的图像。一个更简单的例子是,大多数模型中的风向不是单个参数,而是两个栅格(水平和垂直矢量分量),需要结合三角学来计算实际方向和速度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

降水类型,由杜佩奇学院渲染。

  • 风的流线。MapServer 能够根据栅格生成等值线,甚至可以生成穿过栅格字段的方向箭头,但流线是不可能的。然而,许多受欢迎的产品依靠流线来显示像中层大气风这样的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

流线在 weather.us 的渲染中很明显。

在上面的例子中,你可能已经注意到了一件事,那就是尽管可视化效果很好,但看起来并不那么好。图像很小,数据也很狭窄,这对预测者来说并不容易。当重新发布给天气预报读者时,它们甚至更麻烦,这些读者可能不是数据极客,只是想知道他们下周末应该去哪里滑雪。我想人们可能会承认,他们 90 年代后期的像素化审美确实给人一种纯粹的数据权威的感觉,除了传达原始数据,他们当然不会浪费任何时间做任何事情。

构建您自己的模型可视化工具——在实践中

让我们来看看天气模型完全在 web 浏览器中呈现时会是什么样子。这些是从我的网站上摘下来的,用在了我的预测中。首先,这是我用 Angular 为它建立的界面:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下半部分被切掉,以隐藏我在设计中尚未有效实现的按钮墓地:)

一些示例输出图像:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,让我们放大交互式地图,并启用其他一些层。当我说可调整时,我的意思是这可以由用户在网页上实时完成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有流线的图层怎么样?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

是的,流线型箭头在工具提示的顶部。我会修好的。

我们甚至有可怕的复合反射率+类型层工作正常。你可以看到落基山脉上空的雪是深蓝色的,内布拉斯加州的小雨是灰色的(嗯,很抱歉有点误导人的浅蓝色)。这都是在浏览器中计算出来的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我住的地方没有太多有趣的天气,这是我唯一能找到的两种降水类型的框架。

以下是完成客户端天气模型渲染所需的主要步骤,前提是您已经准备好了 web 地图。我使用传单,但最终需要扩展它这么多,如果你是从零开始,OpenLayers 可能更适合。

  1. Fetch适当的模型、时间戳、参数、级别和预报小时的 GeoTIFF 数据,并将流作为ArrayBuffer读取。
  2. 解析ArrayBuffer以获得地理信息和原始数据的嵌套数组。Geotiff.js 是这方面的首选库,尽管它不能很好地与 Angular 配合使用,除非您导入缩小版。您需要缓存这些数据,以防需要对其进行查询或重新呈现可视化效果。
  3. *提取适当波段的数据(对于多维数据,例如一个 TIFF 中的多个预测小时或一个中的多个参数),并使用 web workers 对嵌套数据数组运行行进正方形算法,您可以使用该算法生成等值线(多边形)或等值线(折线),具体取决于您是否想要填充/着色数据或等值线。你可以使用像光栅行进正方形这样的库。对于流线,可以使用光栅流线库。**这是计算量最大的一步。*一旦计算完成,您将需要缓存所有这些数据。
  4. 在创建等值线和等值线时,对其应用必要的地理变换,然后将其提供给 web 地图。例如,在传单中,我将这些数据作为一个L.GeoJSON层。确保为每个波段或线条提供数据值,以便可以适当地设置样式。把它添加到地图上。
  5. 添加其他功能,如点查询和可视化下载(“另存为 JPEG”)。

让我们深入研究一下细节吧!我将直接从我的代码库中使用 TypeScript 和 ES6 语法,它使用 Angular 框架。

1.检索地理信息数据

这是最简单的部分。一些策略可能是:

  • 只需从网络服务器下载文件。
  • 创建一个 API,为模型、参数、预测时间、地理区域等检索适当的文件。
  • ✔️使用云优化地理标志
  • 使用 MapServer 或其他地理数据服务器处理 WCS 请求,并将数据作为 GeoTIFF 返回。

想想你希望你的地理结构是怎样的。为了节省数据,您可能希望为每个模型、参数、级别、预测时间等返回一个 TIFF。但是,如果客户希望预加载所有内容(例如,使用滑块查看所有预测小时数),您可以将多个预测小时数存储为一个 TIFF 中的几个区域。这将创建一个初始的,较长的下载时间,但会稍微加快整体解析时间。

不要忘记压缩。虽然你可以对 TIFFs 使用 JPEG 压缩,把它们的文件大小压缩到和 JPEG 差不多,但是这不是对数据的负责任的使用。这些不是图像,而是原始数据的数组,在渲染之前应该尊重其完整性。另外,我不认为geotiff.js库能够处理这种压缩方法。我发现高水平(zlevel = 9)的deflate在不严重影响解析性能的情况下减少了 50%的大小。

您可能会用fetch或使用geotiff.js的内置函数来检索 GeoTIFF。该库可以原生读取云优化的 GeoTIFFs!如果使用常规的 GeoTIFF,您需要将fetch请求流(如果您创建了一个)作为ArrayBuffer读取:

*const response = await fetch( <the url of your tiff> );
const arrayBuffer = await response.arrayBuffer();*

2.解析 GeoTIFF 数据

按照上面的例子,我们可以使用geotiff.jsArrayBuffer解析成一个GeoTiff对象。我们可以从该对象中检索实际的图像数据本身:

*const geoTiff = await fromArrayBuffer( arrayBuffer );
const geoTiffImage = await geoTiff.getImage();*

不要忘记获取图像元数据和地理数据,我们稍后会用到它们:

*const tiffWidth = geoTiffImage.getWidth();
const tiffHeight = geoTiffImage.getHeight();
const tiepoint = geoTiffImage.getTiePoints()[ 0 ];
const pixelScale = geoTiffImage.getFileDirectory().ModelPixelScale;
const geoTransform = [ 
    tiepoint.x, 
    pixelScale[ 0 ], 
    0, 
    tiepoint.y, 
    0, 
    -1 * pixelScale[ 1 ] 
];*

如果图像中有多个波段,您可以轻松获得它们的确切数量:

*const bands = await geoTiffImage.getSamplesPerPixel();*

3.根据数据构建等值线/等值线/流线

除了风倒刺(这对天气预报员来说可能很好,但对普通公众来说几乎没用),除了等深线、等值线或流线,我想不出任何其他方法来呈现天气模型数据。等深线是属于特定数据范围的多边形。您可以使用它们来生成彩色的温度或积雪地图。等值线是在数据区域边界绘制的等高线。流线有助于可视化既有速度又有方向的数据。这些都可以创建为矢量并添加到地图上。

我们需要破解 GeoTIFF 数据来构建我们的地理向量。您也可以将它作为图像渲染到画布上,但我不会在这里介绍(参见致谢部分)。

获取适当波段的图像数据(其中i是波段号):

*const bandData = await geoTiffImage.readRasters({ samples: [ i ] });*

构建原始数据的嵌套数组:

*const dataArr = new Array( tiffHeight );
for ( let i = 0; i < tiffHeight; i++ ) {
    dataArr[ i ] = new Array( tiffWidth );
    for ( let ii = 0; ii < tiffWidth; ii++ ) {
        dataArr[ i ][ ii ] = bandData[ 0 ][ ii + i * tiffWidth ];
    }
}*

我们将使用raster-marching-squares库来生成我们的特性,但是出于性能考虑,我想作为一个 web worker 来运行它,这样用户界面就不会锁定。如果你正在使用 Angular,运行ng generate web-worker就可以很容易地用 Angular-CLI 设置一个 web worker。

在 web worker 内部,我们将把raster-marching-squares作为rastertools导入:

*import * as rastertools from 'raster-marching-squares';*

这将等深线和等值线渲染为rastertools.isobands()rastertools.isolines()。这两个函数都需要以下参数:

  • 嵌套数据数组(dataArr)
  • 光栅的投影(geoTransform)
  • 数据的中断/间隔时间。这些间隔将始终出现在这些函数的输出中,而不管这些函数中实际上是否有任何特征。例如,如果您将[-5,0,5]作为温度模型的区间,您将会收到三条与-5 度、0 度和 5 度相关的等深线或等值线。如果天气模型具有这些范围内的数据,那么这些等值线或等深线将具有包含这些区域的几何图形。否则,它们将是空特征。

我们最终会想要设计等值线的样式或在等值线上放置标签,所以我做的一件事是为我想要可视化的所有各种参数设置色标,为特定值传递一种颜色。然后,我设置了一个函数,它将从色标中产生固定数量的间隔(比如 100——平滑,但需要一段时间来计算),并使用tinygradient来产生用于图例的色标的 CSS 表示。例如,积雪的色标如下所示:

*"SnowAccumM": [
    { value: 1.8288, color: "#ffcc01" },
    { value: 1.2192, color: "#ff4501" },
    { value: 0.6096, color: "#e51298" },
    { value: 0.3048, color: "#9f0ded" },
    { value: 0.0762, color: "#5993ff" },
    { value: 0.00254, color: "#d6dadd" },
    { value: 0, color: "#ffffff" }
],*

interval 创建函数也用Number.MAX_SAFE_INTEGER和它的负等价物来限定 intervals 数组。如果做得好,并在其中加入一些单位转换,就可以在一个紧凑的类中处理 isoband 的创建、样式和图例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

积雪的等深线,以米为单位,转换成英寸。这有 100 个间隔,其中大部分没有被使用,因此没有被优化。希望很快会有一场用掉所有 100 个间隔的暴风雪…

运行等值线或等值线函数的工作线程将监听三个必要的参数。我们还将传入一个type参数来告诉它使用哪个函数:

*addEventListener( 'message', ( { data } ) => {
    try {
        const features = rastertools[type]( 
            data.data, 
            data.geoTransform, 
            data.intervals 
        );
        postMessage ( { features } );
    }
    catch ( err ) {
        postMessage( { error: err.message } );
    }
}*

该函数被包装在一个try...catch中,这是一个好主意,但在实践中没有用,因为当函数出错时,它往往会陷入一个do...while循环中,直到内存耗尽。这可能是由于我一天转换的数千个天气模型栅格中的一些被破坏了,我需要修复这些栅格,但是…有人请求吗?

现在我们可以调用 worker,向它传递必要的参数,并等待响应。您不必使用Promise,它只适合我的特定工作流程:

*const type = 'isobands';const rastertoolsWorker = new Worker(
    `../workers/rastertools.worker`,     // the worker class ☭
    { type: `module` } 
);new Promise( ( resolve, reject ) => {
    rastertoolsWorker.postMessage( { 
        dataArr, 
        geoTransform, 
        intervals,      // array of numbers
        type 
    } ); rastertoolsWorker.addEventListener( 
        'message', 
        event => resolve( event.data )
    );
} ).then (...)          // the isobands are generated!*

如果你做 100 次间歇,这可能需要一点时间。你的笔记本电脑粉丝可能会以惊人的凶猛速度踢你。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 web 浏览器中从栅格生成矢量?多么明智的想法。这很好。

注意,我没有提到流线。如果您使用raster-streamlines库,这个计算会非常快,并且可能不需要放在 web worker 中。

4.把它放在地图上

如果你用了raster-marching-squares,这部分就全给你搞定了。基本上,矢量要素是从栅格中生成的,需要与这些像素的实际地理位置联系起来。我们可以使用由geotiff.js提供的geoTransform来获取转换信息(希望您的 GeoTIFF 最初位于 EPSG:4326)。我们还需要 GeoJSON 形式的信息,这是一个不可知的几何集合。同样,库提供了这个集合,我们只需要用L.geoJSON(<the features>)包装它来创建一个我们可以添加到地图的层。raster-streamlines也是如此。

该库还在每个特性的properties中提供 isoband 的数据范围。我们可以用它来设计等值线的样式或者给等值线添加标签。这是这个过程的另一个棘手的部分,我将很快介绍它。

您还需要考虑希望 web 地图使用哪种渲染器。默认情况下,传单使用 SVG。如果你有巨大的,详细的等带,性能将是糟糕的。相反,我选择了使用基于画布的渲染器,这带来了一些麻烦,但是一旦特性被绘制到画布上,就可以获得可接受的性能。

我们现在准备好样式我们的层。

造型等带

这些是最容易造型的。您只需要传递一个样式函数:

*L.geoJSON(<the features>, { style: feature => {
    return {
        fillColor: getColor( feature.properties[ 0 ].lowerValue ),
        weight: 1.5,
        opacity: 1,
        color: getColor( feature.properties[ 0 ].lowerValue ),
        fillOpacity: 1
    };
});*

getColor()是我写的一个函数,它获取一个值,并从前面提到的适当色标中获得插值颜色(使用tinygradient)。由于raster-marching-squares,已经用数值范围填充了feature.properties

你会注意到我还设置了 1.5 的描边/轮廓权重,因为如果多边形没有轮廓的话,它们之间通常会有很小的缝隙。有了轮廓,一个重要的警告是,如果你降低图层的不透明度(通过样式功能),填充和轮廓的重叠将变得明显。相反,您需要将图层放在它自己的地图窗格上,并更改整个窗格的不透明度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这篇文章不包括适当的制图色标,尤其是色盲。

设计等值线

设计等值线本身非常简单:

*return feature => {
    return {
        weight: 1.5,
        color: '#000000'
    }
}*

但是标签呢?这其实很棘手。因为我们使用的是画布渲染器,所以绘制折线标签的传单插件是不可能的,因为它只适用于 SVG。我也不想对每一层使用不同的渲染器,比如在等值线上叠加等高线。

在这种情况下,我发现了一个名称尴尬的插件(fleet . street labels),它通过实际扩展渲染器,在画布渲染器中沿着折线绘制标签(因此它不是一个逐层解决方案)。它有几个依赖项(其中一个不能通过 npm 获得),所以设置它并不简单。

要使标签正常工作,有许多必要的修改。

  1. 默认情况下,插件不能解析feature.properties,因为等值线函数将它放在一个数组中。它在寻找一个键,所以它期望feature.properties是一个对象。
  2. 这个插件有一个过滤器,这样它就不会试图在所有的特征上画标签,只是那些有适当属性对象的特征。出于某种原因,我想可能是由于 GeoJSON 文件可以在野外找到,它通过检查属性是否被设置为字符串值'undefined'来检查属性是否未定义。因此,如果该键根本不存在,它仍然会尝试在该特征上绘制,这会产生一个错误。我需要修改插件来做到这一点(你可以在这个过程中处理第一个问题)。
  3. 绘图问题。最大的问题是它喜欢倒着画标签(即使是在其 GitHub 页面的截图中),这是糟糕的制图。一个较小的问题是,标签遵循轮廓的曲线,这很好,除了在标签变得不可读的急转弯处。

最后,我将代码和先决条件下载到我的代码库中,这样我就可以修改插件来更有效地过滤和读取特性属性。为了解决标签颠倒的问题,我修改了基本依赖关系( Canvas-TextPath ),如果所有字母颠倒,就将它们翻转 180 度,同时颠倒它们的顺序。我还在轮廓旋转的最大值上加了一个大夹子,这样就不会在曲线上产生不可读的标签。有效的等高线标注是有争议的——完全水平的标注将等高线一分为二也不一定有什么错。

也有一些关于设置自定义字体的“todo”评论,所以我将 Roboto 放入画布绘制命令中,以匹配我网站布局的其余部分。也许有一天我会提出一个拉取请求:)

对于上面列出的第 1 点,我们可以通过在 isobands GeoJSON 层的初始化中做一些工作来规避数组的问题:

*L.geoJSON( ..., {
    style,
    onEachFeature: ( feature, layer ) => {
        if ( !feature.properties[ 0 ] ) return;
        const contour = feature.properties[ 0 ].value;
        feature.properties = {
            contour // probably want to convert unit, add suffix :)
        }
    }
}*

最后,在渲染器的初始化中,我让它过滤掉路径过小的形状(压扁的标签),打开标签碰撞,并设置填充/描边颜色。这就是 TypeScript 令人讨厌的地方,因为这些粗略的插件没有定义(尽管我很欣赏它们的存在)。

*const options: any = {
    collisionFlg: true,
    propertyName: 'contour',
    fontStyle: {
        ...
    }
}return new ( L as any ).ContourLabels( options );*

这一切都很烦人,而且非常糟糕,但是渲染器实际上工作得很好——当你平移和缩放时,它会重新绘制标签。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

真的有用!(覆盖在相同数据的等带之上)。

造型流线

流线稍微容易一点,因为有一个传单插件,允许沿着折线放置任意形状或标记(但不是文本,这就是为什么我们不得不采用上述等高线策略)。

插件是传单。PolylineDecorator 它做得很好。一旦生成了 streamline 层,就可以解析它以获得插件期望的LineString特性:

*const lines = layer.getLayers()
    .filter( ( layer: any ) => { 
        return layer.feature.geometry.type === "LineString"; 
    } )
    .map( (layer: any ) => {
        const coordinates = layer.feature.geometry.coordinates;
        coordinates.forEach( coordinate => { 
            coordinate.reverse(); 
        } );
        return L.polyline( coordinates );
    } );*

是的,你可以看到我的代码库中有一些打字问题。

我们还需要为装饰者设置模式,这可能是沿着这条线的某种箭头。我猜插件会自动处理装饰器的旋转。很好。

*const pattern = {
    offset: '10%',
    repeat: '20%',
    symbol: L.Symbol.arrowHead( {
        pixelSize: 8,
        polygon: true,
        pathOptions: {
            stroke: false,
            fill: true,
            fillColor: '#000000',
            fillOpacity: opacity
        }
     } )
}*

现在把它传递给插件:

*const streamlineArrows = L.polylineDecorator( lines, {
    patterns: [ pattern ]
} );*

最后,为了便于管理,您可能希望将装饰器和流线层本身(姑且称之为layer)合并成一个单一的LayerGroup

*L.layerGroup ([layer, streamlineArrows]);*

搞定了。我遇到的唯一问题是,插件总是将装饰器放在叠加地图窗格上(即使我试图绕过它),这意味着如果你有基于窗格的图层排序,你会遇到一些 z 索引问题。也许需要另一个拉取请求…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10m 风流线覆盖在表面阵风等带上。

5.添加其他功能

我们的地图现在工作得很好,只要你从上面的例子代码中推断出来。但是如果我们想要分发可视化效果呢?我们当然可以使用截图工具(或等效工具)手动截图,但这是不一致的。不幸的是,我们没有什么好的选项来下载我们看到的地图,因为并不是所有的东西都被渲染到画布上(工具提示、注释等等)。).

本着保持̶s̶k̶e̶t̶c̶h̶y̶的精神…我的意思是,在客户端,我们可以进一步增加我们的node_modules目录的密度,达到一个临界质量,并使用像 dom-to-image 这样的库,有希望将我们地图容器的整个 dom 树呈现为一个图像。这实际上开箱即用就能很好地工作,但是对于使用 CORS 的外部 web 字体和地图图层来说会有困难,所以要小心。

一旦你解决了这些问题,你就可以非常无缝地渲染地图了,但是你需要文件保护程序将图像保存到磁盘上。

*import domtoimage from 'dom-to-image';
import { saveAs } from 'file-saver';const image = await domtoimage.toJpeg( document.getElementsByClassName( 'map-container' )[ 0 ], { bgcolor: '#ffffff' } )saveAs( image, `my_weather_model_map_works.jpg` );*

domtoimage的性能不是很好,所以如果你希望自动创建动画 GIF,这似乎不是一个可行的方法,尽管也许有一种方法可以用 web workers 来实现。不幸的是,在使用 JavaScript 做任何事情时,我们肯定会遇到性能限制的阻碍。

你可以用这些数据做很多其他的事情,也许最著名的例子是那些花哨的动画风地图。您可以查询它(提示:使用geoTransform)、生成统计数据、编写一个 Discord 服务器机器人…但是当然,您可以在服务器上更容易地完成这些工作。客户端渲染的亮点在于天气模型的可视化和交互性。除此之外,很难证明在 web 浏览器中做任何事情的开销和性能问题是合理的。

鸣谢和来源

这当然不是处理事情的唯一方式。查看正在做的事情传单。比如 CanvasLayer.Field 。这项技术的潜力正在迅速发展,我相信在未来几年内,将会有更健壮、更高效的工具问世。事实上,甚至还有 GDAL 的 WebAssembly 包装器!

我要感谢 Roger Veciana i Rovira 提供了精彩的资源,让我看到了这一切的可能性,让我不用编写低级的光栅处理算法就可以立即开始构建。

如果你试图挖掘我的网站,它不是生产就绪,甚至没有客户端渲染可用。然而,所有代码和示例图像都来自一个工作测试版本。有一天…

如何将 Tableau 图表呈现为 GIF 文件

原文:https://towardsdatascience.com/how-to-render-your-tableau-viz-as-a-gif-file-b0a11ed6acf9?source=collection_archive---------38-----------------------

将动态数据可视化打包为 GIF 的简单步骤

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

本例的数据来源:

数据中的我们的世界:冠状病毒(新冠肺炎)病例

《世界人口评论:按国家分列的基尼系数》

撰写本文时考虑的时间表:2010 年 3 月 30 日至 2010 年 7 月 26 日

介绍

本文将向您展示两个简单快捷的步骤,您可以通过它们将已经动态的 Tableau 可视化制作成 GIF 文件。

如今,动态和有效的数据通信的激增也使得图表越来越倾向于以与大多数人交流的数字媒体类型相一致的交互方式进行交流。在这方面,gif 无疑是一种趋势,当涉及到增加数据图表的交互性以及向相关受众展示数据图表的方式时,gif 可能会非常有效。

因此,我将通过一个简单的示例向您展示如何将 Tableau 图表转换为 GIF 文件,然后可以通过报告/演示共享该文件,从而为您将与网络共享的任何内容添加一点交互性。

步骤 1:生成动态 Tableau 图表

本图表的目的是检验收入不平等(由基尼系数估计)与一个国家每百万居民的新冠肺炎病例总数(数据来源见页面顶部)之间的相关性,并观察这种相关性如何随着时间的推移而发展。为此,您将利用两个数据集和 Tableau 的 Pages Shelf 向图表中添加第一个交互元素。

首先,您可以输入本例所需的两个数据源,并使用 Tableau 的 data source 选项卡连接它们,如下图所示。您可以使用基尼系数表中的“国家列”与第二个数据源进行左连接,从而得到一个包含基尼系数和新冠肺炎相关列(按日期排列的病例数、测试数等)的单一表格。

为了防止您继续使用这个例子,请注意,您可能需要预处理 country 列的格式(即基尼系数表上国家名称的大写),以便正确执行左连接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

一旦这样做了,你就可以打开一张空白纸,画出你的图表。我在下面分享我的图表,但是你可以根据你自己的喜好随意发展它!

在下面的视图中,你可以画出基尼指数与每个国家每 100 万居民新冠肺炎病例总数的散点图。您可以给图上的每个点涂上颜色,以标出该国的大陆,并根据每个国家的人口来确定点的大小。

然后,您可以使用日期列过滤感兴趣的相关时间段的数据(您还可以过滤掉任何偶然的空值)以及特定洲的数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

你现在已经画出了图表的基础。为了增加交互性,您可以使用 Pages shelf 来分解该时间段内每一天的散点图(表示 shelf 中的单个“页面”),从而分析相关性如何随时间发展。为了跟踪页面名称(在这种情况下,它实际上是您在页面架的特定框架的图表上看到的日期),您可以添加一个注释来可视化每个散点图框架的日期细分。

为此,右键单击图表上的任意位置,转到“批注”>“区域”>“插入”>“页面名称”;完成后,您还可以根据自己的喜好设置注释的格式。

因此,Pages 搁板是制作图表动画的绝佳方式。这样做会导致以下结果:

来源:作者

现在您已经通过 Pages shelf 绘制了图表并制作了动画,让我们看看如何将它转换为 GIF 文件格式。

步骤 2:录制动画

要记录由 Pages shelf 启用的 Tableau 动画,您可以使用您选择的任何屏幕记录器来视频捕捉图表随时间的发展。我个人使用 MangoApps 的tiny take,但是在这里可以根据你的个人喜好随意选择。使用它,您可以选择屏幕的一部分来录制动画的简短视频。这就是这一步的全部内容!现在,您可以在本地保存您的视频文件,并进入第 3 步,在这里您将把它转换成 GIF!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

第三步:将视频转换成 GIF

在这最后一步,你最终把你的视频文件(MP4,WebM,AVI,MPEG,FLV,MOV,3GP 和其他视频文件)转换成 GIF 文件格式。

一个我觉得很方便的在线工具是ezgif.com,它允许你对你的视频进行不同的、有用的编辑。

你可以快速上传你的录像到他们的视频到 GIF 转换器页面。然后你可以调整视频的开始时间、结束时间、大小和帧速率,这些将决定 GIF 运行的最终长度和循环次数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

在这一步之后,最后一个有用的调整是利用网站的速度转换器,让你的 GIF 循环更快和/或更慢。

你现在可以下载并保存你的文件为 GIF 格式,并分享到你喜欢的任何地方!

你可能想在你的数据报告或仪表板、Power Point 演示文稿以及任何其他相关材料和/或网页上添加 GIF 风格,比如这个,我把我的 GIF 作为静态特色图像的简洁替代。

你完了!希望这些简短的步骤将有助于为你的 Tableau 作品增加一点 GIF 的乐趣和互动性。感谢阅读!

访问我的免费数据科学资源清单 这里

[## 通过我的推荐链接加入 Medium-Edoardo Romani

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

edo-romani1.medium.com](https://edo-romani1.medium.com/membership)

如何在新冠肺炎疫情时期重新开启经济

原文:https://towardsdatascience.com/how-to-reopen-an-economy-during-the-covid-19-pandemic-e93923163e03?source=collection_archive---------63-----------------------

试析佛罗里达州重新开放经济的尝试及其对其他州的启示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

南佛罗里达州是令人惊叹的市中心场馆和原始海滩的所在地,但新冠肺炎相关的封锁使它们在今年夏天更难进入。图片来源:安东尼奥·库埃拉尔Unsplash 上拍摄

南佛罗里达是一个标志性的地方,充满了新鲜的海鲜餐厅,清澈的海滩和穿着宽松的人们。离开学校的暑假是一种享受——我可以整天和一群朋友在一起,在拉斯奥拉斯大道的商店和海滩上放松。

今年对佛罗里达人来说不幸的是,酒吧和海滩的封锁让这个夏天变得不那么令人兴奋,越来越大的压力迫使佛罗里达州的政治家们过早地重新开放。6 月 5 日,州长罗恩·德桑蒂斯开始了他的逐步重新开放计划的第二阶段,该计划旨在减轻病毒的威胁。整整三个星期后,佛罗里达州宣布,他们将重新关闭酒吧,因为新冠肺炎病毒的传播仍在以惊人的速度增长。上周新增病例高达 63,552 例,比截至 6 月 6 日的一周高出 8 倍多!

增加测试并不是案件数量增加的唯一原因

上周末,当我和哥哥在门廊上享用着当地酿造的卡罗莱纳 IPA、一种佛罗里达葡萄柚和 90%黑巧克力,享受着一次远离社交的聚会时,我们争论着病例数量的增加是病毒真正传播的结果,还是增加检测的孤立影响。

我们同意可能会有一些上升,但由于测试的可用性一直在不断上升,真实的死亡率仍然难以捉摸。第二天早上,我在约翰·霍普斯金数据仪表板上做了一些研究,发现了一个奇怪的现象——阳性检测率上升到了 20%,几乎是 5 月低点的四倍!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

佛罗里达州在 6 月份大力加强了新冠肺炎检测,但阳性检测率却出现了无法解释的增长。归功于约翰·霍普斯金的。来源:COVID 跟踪项目

在某些情况下,初始关闭期间,阳性测试率稳定地徘徊在 5-10%左右。在这个初始阶段,由于检测短缺,只对极有可能的病例进行检测,这导致了相当高的阳性检测率,约为 11%。孤立地看,让大众更容易接受检测应该会继续降低阳性检测率,但显然还有其他变量在起作用。

这种病毒正在所有年龄组中传播

大多数积极的测试结果是由 50 岁以下的人驱动的,这是一个低风险的人群,他们更有可能仍然在飞机上旅行,在室内餐厅就餐和在酒吧社交,甚至可能没有蒙面。这个群体的死亡率是 5 月份新增病例的 0.25%,所以他们如此自信地恢复正常生活是有道理的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在过去的几周里,所有年龄组的病例数量都增加了。来源:佛罗里达 DOH点击查看互动图表

不幸的是,低风险人群并不是生活在真空中,更脆弱的人群也在以惊人的速度被感染。自阵亡将士纪念日周末以来,新的每周感染人数在 50-70 岁人群中增加了 11.5 倍,在 71 岁以上人群中增加了 7.9 倍!

这种病毒仍然非常危险

正如我们从 3 月份以来所看到的,死亡率与病例数量成反比。进行的测试越多,确诊的病例越多,死亡率就越低。这听起来像是好消息,但是还不清楚是病毒越来越弱还是我们越来越擅长治疗它。更有可能的是潜在数据的变化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6 月份观察到的死亡率继续下降,但不一定表明 7 月份继续下降。来源:佛罗里达州卫生部点击查看互动图表

观察到的死亡率的下降实际上具有误导性,因为更严格的社会距离标准、免疫缺陷个体更加谨慎以及病例检出率的增加导致了行为的特定变化。

然而,过去死亡率的下降并不是未来死亡率将继续下降的指标。行为上正在发生的变化可能会再次扭曲受感染的人群:佛罗里达州的许多不同场所重新开放,现在仍然开放,对社会隔离和积极信号的疲劳可能会导致一些人取消预防措施,每周新病例创下历史新高,阳性检测率几乎是 4 月份高点的两倍。更不用说,如果严重病例的激增过快地填满医院,有限的医疗能力可能会使病人的结果更糟

这一切都好说,病毒还是很危险的。为了根据今天的病例数量预测死亡人数,我建立了两个主要假设:未来死亡率和死亡滞后时间。

假设#1:未来死亡率

自 4 月份以来,观察到的 60 岁以上人口的死亡率下降了一半以上,50 岁以下人口的死亡率几乎保持不变。然而,如上所述,新增病例的潜在人口统计数据的持续变化可能会推动死亡率上升,也可能会降低死亡率。

最终,只有时间能证明一切,我不会试图在这里做出复杂的预测。对于本分析中的基线预期,我选择假设死亡率等于 6 月前 3 周每个年龄组的平均死亡率。

假设#2:报告的死亡延迟时间

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源: Covid 跟踪

虽然病例数量的增加预示着一场迫在眉睫的危机,但在医院不堪重负和意大利式的大规模葬礼发生之前,这不是一场真正的危机。在预测佛罗里达何时会真正感受到今天更高的案件数量的后果时,将这一变量作为基础是至关重要的。

平均而言,受感染的患者在症状出现后大约 14 天死亡,直到他们死亡后 7 天才被报告。这些指标也有大量的可变性(例如,一些死亡在死亡后 21 天才被报告),但为了简单起见,我假设所有新病例的固定总滞后时间为 21 天。

佛罗里达州不会经历意大利级别的危机,但…

使用基于上述的假设,佛罗里达州新冠肺炎相关死亡的严重性和时间的预测看起来很严峻。在截至 8 月 1 日的一周中,我的模型预测每周死亡人数为 880 人,即每 10 万人口中约有 4 人死亡。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

据预测,到 8 月 1 日,每周死亡人数将翻两番,从 7 月 12 日的 266 人增至 880 人;点击查看互动图表

相比之下,在意大利最糟糕的一周,共有 5691 人死亡,约为每 10 万人口中有 9 人死亡。然而,尽管该州重新关闭已有两周,病例仍在增加。周日,佛罗里达州创下 15,300 例病例的新纪录,比之前 7 天的平均水平高出 66%。希望这是一个异常现象,但如果不是,佛罗里达州可能会发现自己陷入真正的危机比它讨价还价。

重启经济的经济学

关于世界各国政府是应该保持经济开放以保护 GDP,还是应该实施封锁以减少人员死亡,有两种难以置信的两极分化的观点。

许多人关注封锁的直接经济成本,因为它们更容易量化。但是衡量病毒给人类造成的损失也很重要。*但是你如何衡量一个人生命的价值呢?你如何将这与封锁造成的 GDP 损失进行比较?*信不信由你,政府已经悬赏捉拿你,我将采用现有的框架来衡量未来的最佳路径。

经济成本

2020 年第一季度美国 GDP 总量下降 4.8%(年化)。由于经济在本季度的前两个月运行平稳,估计 3 月份的封锁是这一下降的唯一驱动力。将这些指标外推到佛罗里达州,我假设每月保持完全封锁的经济成本等于其年度 GDP 的 5%,或大约 475 亿美元。

人力成本

你愿意为了经济而牺牲自己的生命吗?

我不这么认为。环境保护署利用统计生命框架的价值来衡量每个人的生命价值为 1000 万美元。这项措施是基于人们接受与特定工作相关的风险津贴的平均意愿。这一数值对所有人都是固定的,与性别、年龄、种族等无关。这种方法长期以来一直被用来确定一项特定法规的成本是否值得它所能挽救的生命。由于其与新冠肺炎难题的相关性,采用这一框架评估开放的总体经济影响是有意义的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:彭博

在这个时候,佛罗里达州实际上可以采取三种不同的方案来决定该如何进行:

**场景 A——重启“正常”经济:**佛罗里达州开始走上这条道路。但由于它只持续了 3 个星期前,锁定恢复,这种情况已被证明是不切实际的。然而,我已经把它包括在我的分析中,以量化如果佛罗里达州电力通过,这将招致的总成本。

**场景 B——向“低接触”经济转型:**这是佛罗里达州在重新关闭酒吧和一些室内场馆后的现状,但这尚未被证明是一个可行的解决方案。目前还没有这方面的数据,但这种情况将假设损失的经济成本是全面封锁的一半,案件量将降至 5 月低点和当前高点之间的中点。

**情景 C -恢复 4 月/5 月的封锁:**这种选择对经济来说极其可怕,尤其是在不确定何时重新开放是安全的情况下。这种疫苗开始看起来像是摆脱疫情的最佳方式,但它可能还需要 6-18 个月才能开发和分发,更不用说最近的一项调查表明只有 50%的美国人会接种疫苗。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据佛罗里达州处理新增病例数量的能力,截至 8 月底,每周死亡人数可能在 136 至 1254 人之间;点击查看互动图表

基于这些假设,经济上的最佳解决方案是在八月份过渡到低接触经济(见下表)。低接触经济将产生 575.6 亿美元的最低人力成本和 GDP 成本。最不可取的结果是经济恢复正常,因为这种情况下仅人力成本就将产生 1118.8 亿美元。

这两种情况下总成本的差异令人震惊,并要求优先考虑的顺序是首先减少病毒的威胁,然后重新启动经济。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

恢复 5 月份实施的全面封锁将最大限度地减少死亡和 GDP 损失带来的负面经济影响;点击查看表格

我对一个严重两极分化的政治问题的非政治性结论

作为低风险人群中的一员,我理解失去工作或被自己的政府禁止与朋友一起喝酒而对我的福祉几乎没有风险是多么令人沮丧。但重要的是退一步,从整体上看待经济。匆忙回归“正常”经济可以让年轻人维持生计,而限制经济活动可以让老年人维持生活。上表反映了这两种后果的权重差异。我们必须要有耐心,因为在 T2 至少 70%的人口通过疫苗或传染病隔离产生抗体之前,这场健康危机不会得到解决。佛罗里达州已经证明,过早启动经济引擎可能会带来致命的后果,迫使他们已经踩下刹车,开始逆转。如果其他正在观望的国家要吸取什么教训的话,那就是谨慎行事。

对于那些对分析感兴趣的人,请随意查看我在 GitHub 上的代码。

如何更快重开,少受点苦

原文:https://towardsdatascience.com/how-to-reopen-faster-and-suffer-less-efac65a0af9a?source=collection_archive---------50-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

估计不同的群体活动何时是安全的,病毒传播的不同下降率在 0.5 和 1 之间。美国大部分地区目前处于或高于 0.92。

当我在 Playdom 领导数据工程时,我使用高级疾病建模进行病毒式增长营销。这篇文章来自那个背景和我过去十年的工作*【数据帮助做出更好的决策(在斯坦福、Playdom /迪士尼和绿色厨师)*

随着新冠肺炎病毒的传播,患病和死亡人数的指数增长令许多人感到惊讶。这种疾病每隔几天就会增加一倍,多一周的无所作为意味着应对和急诊室超负荷之间的差异。

在美国,大部分讨论都集中在如何度过高峰期,以便病例和死亡人数开始下降。或者换句话说,让 R 小于 1。大多数州几乎没有做到这一步。

这篇文章有一个不同的焦点——它希望给你一个高层次的直觉,如果我们进一步减少传播会发生什么。0.5 到 1 之间的 R 值如何影响我们重新开放的速度?

正如比尔·盖茨指出的那样,“指数下降”比指数上升更不直观。减少一点传播有助于提前几个月重新开放,减少风险和痛苦。

指数下降的直觉

想象一个虚构的地方,上周有 1000 人死于新冠肺炎。如果措施持续抑制传播,那么每个感染者感染的新感染者就会减少,几周后,死亡率就会下降。

如果传播速度足够慢,从一周到下一周的死亡率是 0.5 倍,那么就会发生这种情况:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每周死亡人数和周间比率为 0.5 倍的累计死亡人数图表

从每周 1000 例死亡到 1 例死亡需要 10 周时间。

现在,让我们将 0.5 倍与其他三种周环比比率进行比较:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图表显示了四种不同的周与周之间的比率如何在时间上进行比较,以达到每周低于 1 例死亡和整个疫情期间的总死亡数。

比率的微小变化,尤其是略低于 1 倍的变化,会在时间和痛苦上造成很大差异。

关键外卖

指数下跌的不寻常之处在于,目标阈值是多少并不重要。无论是每周一人死亡还是一百人死亡,同样的直觉也适用:

  • 0.71 倍需要两倍于 0.5 倍的时间达到阈值
  • 0.84 倍耗时 4 倍
  • 0.92 倍需要 8 倍的时间

美国现在在哪里?

在过去的一个月里,每个人都在减少传播方面做了出色的工作。尽管如此,超过一半的美国人生活在周周死亡率为 0.92 倍或以上的州。很少有州接近 0.5x。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

截至 2020 年 5 月 3 日,各州死亡人数的周比,显示人口。来自约翰霍普金斯大学的数据。

看看主要的大都市地区,情况大致相同:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

截至 2020 年 5 月 3 日,地铁死亡人数的周比,显示人口(对于人口超过 100 万的地铁)。数据来自约翰霍普金斯维基百科

我们什么时候能重新开业?

在美国,政策讨论的中心是如何重新开放。首先,社区需要足够的能力来检测和追踪每一个新病例,以防止新的疫情爆发。一旦这一点到位,社区将分阶段重新开放,首先从低风险的公共场所和商业开始,然后转移到高风险的商业。

降低传播率,使每周的病例和死亡减少一半,有助于我们更快地实现所有这些目标,减少人民和经济遭受的痛苦。

测试和跟踪能力

一组无党派专家估计,我们需要将联系人追踪能力提高 13 到 40 倍。我们还需要将测试能力提高 10 到 20 倍,以符合国际最佳实践。这使得检测有症状的人、检测无症状的接触者以及在人群中随机取样成为可能。

这是一项艰巨的任务。在整个 4 月份,我们只增加了 2 倍的测试。

不过还是有希望的,如果我们能减少感染人数,那么我们需要检测和接触的人就更少了。

让我们看看当案例以不同的速度下降时会发生什么。当测试和跟踪能力增加时,我们可以在测试和跟踪能力减少时绘制案例。一开始,假设我们需要 20 倍以上的容量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该图表显示当测试和跟踪能力每月翻倍时,何时有足够的能力(星形)来处理不同下降率的案例。

改变下降速度很重要。0.5x 和 0.92x 之间的差异意味着在有足够的容量之前需要额外的两个月时间。

开办高风险业务

一个群体中的人数越多,某人被感染的风险就越高。这就是为什么重开一个有 12 个蹒跚学步的孩子的托儿所比一个有 1000 个音乐爱好者的音乐会场地风险更小。

下降率如何影响中等和较高风险企业何时可以开业?

这张图表显示了一个类似于纽约的地方,从每周每百万人 2000 例开始。病例会随着时间的推移而减少,当对一个群体安全时会越过水平虚线:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图表显示了在不同的群体规模和每百万人新增病例下降率下,群体中有人被感染的几率低于 0.1%的情况

这是加州或德克萨斯州的图表,从每周每百万人 200 例开始:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于达到 0.5 倍比率的社区,餐馆等企业在 1-2 个月内变得相对安全。

对于比率保持在 0.84 倍以上的社区,餐馆、酒吧、教堂和健身房在秋季仍有风险。

要查看美国一个州或主要城市的风险图表和下降率趋势,请查看 Tableau Public 上的这个仪表盘。

我的社区能做什么?

社会距离有效地减少了整个美国的传播,因此在大约一半的州,病毒不再增长。但我希望这篇文章能说明为什么我们做得还不够。更显著地减少每个 COVID 阳性患者感染的人数意味着我们可以更快、更安全地重新开放,同时也拯救了生命。

即使各国考虑缓解社会距离的最具破坏性的方面,它们也应该采取尽可能多的步骤来进一步减少传播。

有许多有效且破坏性较小的步骤,例如

  • 测试和接触追踪是否足够
  • 在公共场所戴口罩
  • 确保重要员工拥有安全的工作环境
  • 帮助弱势群体保持社会距离
  • 继续避免有传播风险的活动

对于正在发生疫情的社区来说,“过了高峰期”是不够的。

疾病预防控制中心指南建议“14 天内记录病例的下降轨迹”是一个很好的开始,但我们可以做得更好。如果我们能够每周将病例和死亡人数减少一半,将会有足够的检测和追踪能力,我们可以更快地重新开放。

如果这篇文章对你有帮助,请分享。

更好的是,把这个分享给那些决定你的社区减少了多少传播的人。

旁白:关于 R 的复制数

最近一周,关于 R 的讨论很多( NYT《卫报》BBC金融时报)。r 是描述疾病模型中传播率的模型化量。这是不可直接测量的,但它导致了可测量的数量,如病例、住院和死亡的周比。

Rt.liveEpiforecasts.io 这样的网站试图模拟美国的生育数量。

为新冠肺炎建模很难,因此本文主要关注我们可以在公开数据中看到的可测量的数量。这里所有关于周环比和指数下降的直觉也适用于 r。

当 R 远高于 0.7 时,大多数欧洲和美国现在正计划重新开放。这种策略与将 R 保持在 0.5 以下直到极少情况的方法形成对比。这是几个亚洲国家使用的方法。

关于数据

关于美国各州和大都市死亡率下降的数据来自约翰霍普金斯新冠肺炎 Github repo最低限度处理以修复历史数据不一致(例如,从报告确诊死亡到可能死亡的变化)。

如何表示 3D 数据?

原文:https://towardsdatascience.com/how-to-represent-3d-data-66a0f6376afb?source=collection_archive---------6-----------------------

3D 创意

帮助在 3D 点云、网格、参数模型、深度图、RGB-D、多视图图像、体素中选择数据表示的可视化指南……

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3D 点云数据集的不同数据表示

我们的计算机化生态系统中的 3D 数据集——其中越来越多的数据集直接来自现实捕捉设备——以不同的形式存在,在结构和属性上都有所不同。有趣的是,由于它的规范性质,它们可以以某种方式成功地映射到点云。当绑定将云指向应用程序时,本文提供了主要的 3D 数据表示模式供您选择。

三维点云

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是 2014 年使用摄影测量学(Gerpho)获得的一座修道院的 3D 点云,位于我的家乡法国南部🙂。分辨率为 1 cm,表示为地面采样距离。

点云是三维坐标系中的一组数据点。这些点在空间上由X, Y, Z坐标定义,通常代表一个对象的包络。现实捕捉设备获取外部表面的三维信息以生成点云。这些通常是通过摄影测量(上面的例子)、激光雷达(陆地激光扫描、移动制图、下面模拟的空中激光雷达)、深度传感以及最近通过生成式对抗网络的深度学习来获得的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

航空激光雷达模拟点云。从自上而下的感知来看,这主要是 2.5D。

每种技术都有几个影响数据质量和完整性的特性,您已经可以看到完整 360°捕获与传统航空激光雷达采集之间的差异。这扩展了本文的范围,将在另一期或 3D 地理数据学院的信息中讨论:

[## 点云处理在线课程- 3D 地理数据学院

编队学习先进的点云处理和三维自动化。开发新的 python 地理数据技能和开源…

learngeodata.eu](https://learngeodata.eu/point-cloud-processor-formation/)

点云提供了简单而有效的 3D 数据表示,下面我总结了它们的主要操作、优点和缺点。

主要经营

  • 变换:可以用线性变换矩阵乘以点列表中的点。
  • 组合:“对象”可以通过合并点列表来组合。
  • 渲染:将点投影并绘制到图像平面上

主要好处

  • 快速渲染
  • 精确表示
  • 快速转换

主要缺点

  • 无数的点(obj。曲线,精确表示)
  • 高内存消耗
  • 有限组合操作

虽然快速渲染和转换使直接检查点云变得很方便,但它们通常不能直接集成到常用的三维应用程序中。然而,最近的发展显示了一种趋势,即使在纯基于网格的渲染平台中也有更好的支持,最近的一个例子是虚幻 4 游戏引擎。

一个常见的过程是使用合适的表面重建技术来导出网格。有几种将点云转换成三维显式表面的技术,其中一些将在下面的文章中介绍。

[## 使用 Python 从点云生成 3D 网格的 5 步指南

生成 3D 网格的教程(。obj,。ply,。stl,。gltf)自动从三维点云使用 python。(奖金)…

towardsdatascience.com](/5-step-guide-to-generate-3d-meshes-from-point-clouds-with-python-36bad397d8ba)

让我们进一步深入 3D 模型作为一种表示,以更好地把握可能性的范围。

3D 模型

几乎所有的 3D 模型都可以分为两类。

  • 固体:这些模型定义了它们所代表的物体的体积。实体模型主要用于工程和医学模拟,通常由构造性实体几何图形或体素集合构建。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过体素化的实体 3D 模型示例。

  • 边界 (B-Reps):这些模型代表表面,即物体的边界,而不是它的体积。几乎所有在现实捕捉工作流、游戏和电影中使用的视觉模型都是边界表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

修道院的贝壳代表的例子。

实体和壳建模可以创建功能相同的对象。它们之间的差异主要是创建和编辑它们的方式的变化,以及在各个领域中使用的惯例,以及模型和现实之间近似类型的差异。

三种主要策略允许通过 3D 模型描述点云。构造实体几何图形、隐式曲面(+参数化建模)和边界表示(B-Reps)。虽然构造性立体几何非常有趣,我们将很快讨论,但最常见的 3D 模型是 B-Reps 作为 3D 网格。让我们首先扩展这些论点。

三维网格

网格是一种几何数据结构,它允许用一组多边形来表示曲面细分。网格在计算机图形学中特别用于表示曲面,或者在建模中用于离散化连续或隐式曲面。网格由顶点(或顶点)组成,通过边连接,形成多边形的面(或小平面)。当所有的面都是三角形时,我们称之为三角形网格。这些是现实捕捉工作流中最常见的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从上到下:网格的顶点;将顶点连接在一起的边;由顶点和边形成的面填充,大多是三角形。

四边形网格也非常有趣,但通常通过网格优化技术来获得更紧凑的表示。也可以使用体积网格,它通过四面体、六面体(长方体)和棱柱连接顶点。这些所谓的网格是基于边界表示的,这取决于线框模型(对象由 3D 线简化,对象的每条边由模型中的一条线表示)。让我们扩展这个理论。

边界表示法

3D 模型的边界表示主要由两部分组成:拓扑(元素的组织)和几何(曲面、曲线和点)。主要的拓扑项目是面、边和顶点,我在一个立方体的简单 B-Rep 下面进行了图解。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结构下边界表征的图式化

作战

  • 变换:所有的点都像线框模型一样进行变换(将点列表中的点与线性矩阵相乘),此外,还可以变换曲面方程或法向量。
  • 组合:可以通过将点列表和边分组来组合对象;对多边形的操作(根据交点进行分割,删除多余的多边形,合并它们…
  • 渲染:可以使用隐藏表面或线算法,因为对象的表面是已知的,因此可以计算可见性。

好处

  • 被广泛采用的表示法
  • 通过“新一代扫描”生成模型
  • 转换既快速又简单

缺点

  • 高内存需求
  • 昂贵的组合
  • 弯曲的物体是近似的

网格是显示点云几何图形的一种很好的方式,并且通常允许大大减少作为顶点的所需点的数量。最重要的是,它允许通过面的连通性来了解对象之间的关系。然而,网格化是基点云几何的插值,并且只能在一定程度上表示数据,与网格的复杂性相关联。存在多种策略来最佳地网格化点云,但是这通常需要有一些理论背景,并且知道调整哪个参数来获得最佳结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

点云的 2.5D Delaunay 三角剖分示例。

基于体素的模型

体素可被视为可用于表示 3D 模型的 3D 基本立方体单位。它的 2D 类比是像素,最小的栅格单位。因此,基于体素的模型是“3D 像素”的离散化组合,通常与实体建模相关联。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在点云数据的情况下,可以将每个点表示为大小为x的体素,以获得点之间空白空间的“填充”视图。它主要与八叉树等数据结构相关联,并允许根据所需的细化级别对每个体素单位的一定数量的点进行平均(参见下图中的示例)。这非常有趣,我将在另一篇专门的文章中讨论理论和实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于由点云数据占据的空间的八叉树细分的体素一般化的例子。

虽然这对于渲染和平滑可视化是实用的,但是它近似于初始几何形状,并且伴随有混叠伪影,并且如果不恰当地使用体积信息,则可能给出错误的信息。然而,由于体素模型的非常结构化的网格布局,它可以非常方便地用于处理任务,例如通过 3D 卷积神经网络进行分类。

参数模型(CAD)

“参数化”用于描述通过将参数设置为修改基础几何图形的目标值来改变形状的能力。例如,如果您想通过设置“墙”的方向、长度、宽度和高度来对其建模,这将非常方便。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过交互设置参数来创建 BIM 模型(建筑信息建模)的墙建模示例

然后,参数化建模适合于使用计算能力,该计算能力可以以真实世界的行为为目标对组件属性进行建模。参数化模型使用基于特征(参数化,如后面部分所述)、实体和曲面建模的组合,以允许操纵模型的属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是一个自动生成的 CAD 模型,没有拓扑修复。

参数化建模的一个最重要的特性是,相互链接的属性可以自动更改值。换句话说,参数化建模允许定义整个“形状类别”,而不仅仅是特定的实例。然而,这通常需要底层点云几何的非常“智能”的结构化,以将模型实体分解成聚集在类中的子实体(例如,段)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这篇获奖的开放存取文章【0】中描述了自动分段的例子

这个过程极大地受益于对象检测场景和智能点云基础设施,如下文所定义。

[## 三维点云的未来:一个新的视角

被称为点云的离散空间数据集通常为决策应用奠定基础。但是他们能不能…

towardsdatascience.com](/the-future-of-3d-point-clouds-a-new-perspective-125b35b558b9)

通常,这些参数模型也可以通过组合插入点云形状的 2D CAD 绘图来组合或提取,并根据元素的类别对其进行分层。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分层前来自初始点云的几个原始 CAD 截面的示例。

这些参数模型的创建通常很费时,但却是给 3D 点云数据带来最大价值的模型。这些来自大量的语义丰富和对构成场景的物体之间的关系的额外触发。

深度图

现在,我们跳到基于光栅的点云表示。第一个是深度图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于俯视图的点云深度图

深度图是图像或“图像通道”,其包含与从单个视点构成场景的点的距离相关的信息。虽然我们习惯于处理 RGB 图像,但表达深度的最简单形式是在一个通道上用颜色编码,并带有强度值。亮像素具有最高值,暗像素具有最低值。就是这样。深度图像只是根据物体有多远来呈现值,其中像素颜色给出了离相机的距离。

💡 提示: 深度图与 Z-buffer 有关,其中“Z”与相机的中心轴方向有关,与绝对 Z 场景坐标无关。

如果您只需要链接到已知视点的表面信息,这种形式的点云表示就可以了。这是自动驾驶场景的情况,其中您可以通过 360°投影深度图非常快速地绘制每个位置的环境。然而,最大的不同是,你不是在处理 3D 数据,而是 2.5D 数据,因为你不能在视线上表示 2 个不同的值。下面是深度图的操作、优点和缺点:

操作

  • 变换:用线性变换矩阵乘以图像中的像素
  • 组合:可以通过合并点列表来组合对象。
  • 渲染:在图像平面上绘制像素

好处

  • 低内存需求
  • 非常熟悉光栅格式
  • 转换既快速又简单

缺点

  • 本质上是 2.5 维的表现
  • 无法单独描述完整的 3D 场景
  • 弱拓扑

特例:RGB-D

第三,由于 RGB-D 传感器的普及,近年来将 3D 数据表示为 RGB-D 图像已经变得流行。RGB-D 数据通过将深度图与 2D 颜色信息(RGB)一起附加来提供关于捕获的 3D 对象的 2,5D 信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是 RGB 光栅影像

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是相关的深度通道

除了价格低廉之外,RGB-D 数据还是 3D 对象的简单而有效的表示,可用于不同的任务,如身份识别[1]、姿态回归[2]和通信[1]。与点云或 3D 网格等其他 3D 数据集相比,可用的 RGB-D 数据集的数量非常庞大,因此是通过大量训练数据集训练深度学习模型的首选方式。

特例:预测

其次,将 3D 数据投影到另一个 2D 空间是原始 3D 数据的另一种表示,其中投影的数据封装了原始 3D 形状的一些关键属性[3]。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

点云的大范围变形圆柱投影示例

存在多个投影,其中每个投影将 3D 对象转换成具有特定信息的 2D 网格。将 3D 数据投影到球形和圆柱形区域(例如[4])已经成为以这种格式表示 3D 数据的常见实践。这种投影有助于投影数据对于围绕投影主轴的旋转是不变的,并且由于所得投影的欧几里德网格结构,简化了 3D 数据的处理。然而,由于投影中的信息损失,这种表示对于复杂的 3D 计算机视觉任务不是最佳的,例如密集对应[5]。

隐性表征

现在,我们转到点云的较小的视觉组件:隐式表示。它只是一种通过一组形状描述符来表示点云的方法,如[6,7]中的文章所述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于颜色的点云垂直特征可视化

这些可以被视为 3D 形状的签名,以通过捕捉一些关键属性来提供 3D 对象的紧凑表示,从而简化处理和计算(例如,表示为. csv 文件)

x      y      z   surface   volume   omn.  ver.       
9.9   30.5   265.3   334.5   103.3   4.6   0.0       
-27.0   71.6   274.2   18.2   12.5   1.3   0.4       
-11.8   48.9   273.8   113.2   620.4   3.7   0.7       
26.9   43.8   266.1   297.1   283.6   3.9   0.0       
42.9   61.7   273.7   0.1   0.0   0.3   0.8       
-23.1   36.5   263.3   26.3   14.8   1.6   0.0       
-9.5   73.1   268.2   24.0   11.4   2.2   0.0       
32.2   70.9   284.0   36.0   139.1   1.7   0.8       
-20.5   20.7   263.2   34.0   3.4   1.8   0.8       
-2.3   73.6   262.2   28.2   15.6   2.6   1.0

该签名的性质和含义取决于所使用的形状描述符的特征及其定义。例如,全局描述符为整个 3D 形状提供了简明而信息丰富的描述,而局部描述符为形状中较小的小块提供了更局部化的表示。Kazmi 等人[6]、Zhang 等人[7]以及最近 Rostami 等人[8]的工作提供了关于这种 3D 形状描述符的综合调查。

作为处理管道的一部分,隐式表示非常方便,并且可以简化不同基础设施之间的数据传输。对于受益于难以直观表示的信息特征的高级流程,它也非常有用。

多视图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第五,我们可以从多视图图像中访问 3D 信息,多视图图像是基于 2D 的 3D 表示,其中通过匹配来自不同视点的同一物体的若干 2D 图像来访问信息。以这种方式表示 3D 数据可以导致学习多个特征集,以减少噪声、不完整、遮挡和照明问题对捕获数据的影响。然而,多少视图足以模拟 3D 形状的问题仍然是开放的,并且与摄影测量重建的获取方法相关联:视图数量不够少的 3D 对象可能无法捕捉整个 3D 形状的属性(尤其是对于 3D 场景),并且可能导致过拟合问题。体积数据和多视图数据都更适合分析变形最小的刚性数据。

机器学习和深度学习呢?

3D 数据对于构建机器学习系统,尤其是深度学习,有着巨大的潜力。然而,目前,关于另一种深度学习范例,需要考虑诸如 3D 网格的真实 3D 数据表示。

事实上,绝大多数深度学习都是在欧几里得数据上进行的。这包括 1 维和 2 维域中的数据类型。图像、文本、音频和许多其他数据都是欧几里得数据。其中,特别是 RGB-D 数据集,如果想要自动检测场景中的对象,现在能够建立在大规模标记库的基础上。但是网格或结构化的点云可以从利用它们丰富的潜在关系中受益。例如,这是通过将它们嵌入图结构(一种由与(关系)连接的节点(实体)组成的数据结构)来实现的,但这使得它们是非欧几里德的(网格本质上是这样的),因此不可用于经典的机器学习架构。

为此,一个名为几何深度学习(GDL) 的新兴领域旨在建立可以从非欧几里得数据中学习的神经网络。

正如一位科学家同事 Flawnson Tong 在这篇推荐文章中所说的:

关系、联系和共有财产的概念是自然存在于人类和自然界的概念。从这些联系中理解和学习是我们理所当然的事情。几何深度学习意义重大,因为它允许我们利用具有内在关系、联系和共享属性的数据。

因此,每种 3D 数据表示都可以在机器学习项目中使用,但有些将用于更多的实验项目(非欧几里得表示),而欧几里得数据可以直接在您的应用程序中获取

结论

如果你一直读到现在,那么恭喜你😆!总之,3D 数据表示世界非常灵活,您现在已经掌握了选择数据表示的明智决策:

  • 3D 点云简单高效但缺乏连通性;
  • 作为 3D 网格、参数模型、体素组件发现的 3D 模型提出了专用级别的附加信息,但近似于基本数据;
  • 深度图是众所周知的和紧凑的,但是本质上处理 2.5D 数据;
  • 隐式表示包含了上述所有内容,但几乎不是可视化的;
  • 多视图是互补的,并利用光栅图像,但在最佳视点选择的情况下容易失败。

和往常一样,如果你想超越,你会发现以下几个参考。您也可以今天就在地理数据学院开始卓越之旅。

[## 点云处理在线课程- 3D 地理数据学院

编队学习先进的点云处理和三维自动化。开发新的 python 地理数据技能和开源…

learngeodata.eu](https://learngeodata.eu/point-cloud-processor-formation/)

参考

0.波克斯,女;基于体素的三维点云语义分割:无监督的几何和关系特征与深度学习方法。 ISPRS 国际地理信息杂志 20198 ,213。

1.新泽西州埃尔多穆斯;Marcel,s.《2D 的欺骗:使用 3D 面具的人脸识别和使用 Kinect 的反欺骗》.《第六届国际生物统计学会议论文集:理论、应用和系统》(BTAS);IEEE,2013;第 1-6 页。

2.法内利公司;WeiseGall,j;来自消费者深度相机的实时头部姿态估计。 2011 ,101–110。

3.Houshiar,h.《3D 点云处理的文档和制图》,维尔茨堡大学,2012 年。

4.曹;黄;通过球形投影的三维物体分类。 2017

5.辛哈,a。白;使用几何图像深度学习 3D 形状表面。《欧洲计算机视觉会议论文集》(ECCV);2016 年德国阿姆斯特丹;第 223-240 页。

6.卡兹米,英国;你,我。和三维形状描述符的综述。第 10 届国际计算机图形学、成像和可视化会议录(CGIV);IEEE,2013;第 1-10 页。

7.申博士;福尔克斯,加州大学;像素、体素和视图:单视图三维物体形状预测的形状表示研究。 2018

8.罗斯塔米河;Bashiri,F.S .罗斯塔米湾;数据驱动的三维形状描述符综述。计算机图形学论坛 201800 ,1–38。

如何表现 2048 年的游戏状态

原文:https://towardsdatascience.com/how-to-represent-the-game-state-of-2048-a1518c9775eb?source=collection_archive---------21-----------------------

用极大极小算法玩 2048

以及如何以面向对象的方式实现它

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

在关于解决这个游戏的上一篇文章中,我已经在概念层面上展示了如何应用极大极小算法来解决 2048 游戏。但是为了将这些想法付诸实践,我们需要一种方法来表示游戏的状态并对其进行操作。我选择以面向对象的方式,通过一个我命名为Grid的类来这样做。这个类保存游戏状态,并为我们提供进一步实现极大极小算法所需的方法(在下一篇文章中)。

如果你错过了我以前的文章,这里是:

[## 如何将 Minimax 应用到 2048 年

2048 年——一个简单的游戏,但是给计算机编程来解决它并不简单

towardsdatascience.com](/playing-2048-with-minimax-algorithm-1-d214b136bffb)

现在,让我们开始用 Python 实现Grid类。

Grid类中,我们将游戏状态保存为一个矩阵,矩阵中有方块数,在有空方块的地方,我们将保存一个 0。在 Python 中,我们将使用一个列表列表,并将它存储到Grid类的matrix属性中。

这种表示法的一个例子如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

在我们的实现中,我们需要稍微传递一下这个矩阵;我们将从一个Grid对象获取它,然后用它实例化另一个Grid对象,等等。因此,为了避免通过引用传递它所带来的副作用,我们将使用deepcopy()函数,因此我们需要导入它。我们将导入的另一个东西是Tuple,以及来自typingList;这是因为我们将使用类型提示。

from copy import deepcopyfrom typing import Tuple, List

我们可以从创建用于设置和获取类的矩阵属性的方法开始。然后我们将创建一个在棋盘上放置瓷砖的方法;为此,我们只需将矩阵的相应元素设置为瓷砖的编号。输入行/列参数是 1 索引的,所以我们需要减去 1;图块编号按原样分配。

然后我们将定义__init__()方法,它将只是设置矩阵属性。

对于 minimax 算法,我们需要测试Grid对象的相等性。当两个对象的矩阵相同时,我们将认为两个Grid对象是相等的,我们将使用__eq__()魔法方法来做到这一点。我们遍历 2 个矩阵的所有元素,一旦有不匹配,我们就返回 False,否则最后返回 True。

接下来,我们创建一个实用方法。这个方法评估我们的游戏网格“有多好”。对此可能有许多可能的选择,但是这里我们使用下面的度量(如前一篇文章中所述):对矩阵的所有元素求和,然后除以非零元素的数量。

下一段代码有点棘手。我们需要检查 Max 是否能做以下动作之一:上、下、左、右。每一步的代码都非常相似,所以我将只解释其中的一步:up,它是在.canMoveUp()方法中实现的。

当我们想做“向上”移动时,事情只能垂直变化。棋盘的不同列之间没有交互作用。因此,我们可以为每一列独立运行代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我们可以在 for 循环中这样做:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我们不一定需要检查所有的列。一旦我们遇到一个允许在“向上”移动中改变某些东西的列,我们就返回 True。如果没有这样的列,我们最后返回 False。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

对于每一列,我们执行以下操作:从底部开始向上移动,直到遇到一个非空(> 0)元素。在我们看到这样一个元素之后,我们如何知道一个“向上”的移动是否改变了这个列中的一些东西呢?有两种可能的情况会产生变化:要么有一个空方块可以移动,要么有两个相邻的相同的方块。

举个例子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图片

下面突出显示的代码负责查找最下面的非空元素:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

下面突出显示的这段代码一旦找到可以移动方块的空方块或两个方块之间可能的合并,就返回 True。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

下面是所有这些方法的代码,它们与.canMoveUp()方法的工作方式相似。

我们将需要一个方法,返回最大和最小的可用移动。对麦克斯来说,这是动作的子集:上,下,左,右。我们将这些移动表示为整数;每个方向都有一个相关的整数:

  • 向上= 0
  • 向下= 1
  • 左= 2
  • 右= 3

.getAvailableMovesForMax()方法中,我们使用之前创建的方法检查我们是否可以在每个方向上移动,如果某个方向的结果为真,我们将相应的整数附加到一个列表中,我们将在方法结束时返回该列表。

.getAvailableMovesForMin()方法将返回网格上的空位置集和{2,4}集之间的叉积。该返回值将是(row,col,tile)形式的元组列表,其中 row 和 col 是空单元格的 1 索引坐标,tile 是{2,4}之一。

.getChildren()采用一个可以是“最大”或“最小”的参数,并使用前面两种方法之一返回适当的移动。这些是在极大极小算法的树中导致子游戏状态的移动。

对于极大极小算法,我们需要一种方法来确定一个游戏状态是否是终结的。正如我在上一篇文章中所说的,如果没有可用的移动,或者达到了一定的深度,我们将认为游戏状态是终结的。但是在极大极小算法内部检查深度条件会更容易,而不是在这个类内部。因此,通过.isTerminal()方法,我们将只检查最大值或最小值是否有可用的移动。一个简单的方法是使用.getAvailableMovesForMin().getAvailableMovesForMax()返回一个包含所有走法的列表,如果是空的则返回 True,否则返回 False。但是更有效的方法是,一旦我们“看到”一个可用的移动就返回 False,最后,如果没有返回 False,则返回 True。

.isGameOver()方法只是.isTerminal(who=”max”)的简写,它将在我们的游戏求解循环中用作结束条件(在下一篇文章中)。

下面的方法是用来上下左右移动的。这次我们实际做这些动作,不要只检查能不能做。每个移动方向的代码是相似的,所以,我将只解释向上移动。

每列都可以独立地向上移动。我们将有一个遍历列的 for 循环。对于每一列,我们将把变量wk初始化为 0。w保存下一个写操作的位置。k存储最后遇到的非空单元格的平铺值。

该算法运行如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

下面是一个给定列的工作原理示例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

下面是包含所有 4 种方法的代码:.up().down().left().right():

然后,我们围绕上述 4 个方法创建了一个包装器,并将其命名为.move(),它按照参数给定的方向移动。

我们需要的另一件事是移动的“逆”方法。.move()将方向代码作为参数,然后进行移动。现在,我们想要一个方法,它将另一个Grid对象作为参数,该对象被假定为对.move()的调用的直接子对象,并返回生成该参数的方向代码。我们将这种方法命名为.getMoveTo()

这个方法的工作原理是创建当前对象的副本,然后依次调用这些副本上的.up().down().left().right(),并根据方法的参数测试是否相等。当等式成立时,我们返回适当的方向代码。

下面是Grid类的完整代码:

这就是本文的全部内容。在下一部(这是关于 2048 年和 minimax 的最后一部)中,我们将看到我们如何控制这款游戏的网页版游戏板,实现 minimax 算法,并看着它比我们玩得更好(…或至少比我好)。

[## 如何控制 2048 的游戏板

…并完成最小最大算法的实现

towardsdatascience.com](/how-to-control-the-game-board-of-2048-ec2793db3fa9)

我希望这些信息对你有用,感谢你的阅读!

这篇文章也贴在我自己的网站这里。随便看看吧!

如何使用 LSTM 重塑数据并对时间序列进行回归

原文:https://towardsdatascience.com/how-to-reshape-data-and-do-regression-for-time-series-using-lstm-133dad96cd00?source=collection_archive---------5-----------------------

逐步解释如何使用 LSTM 进行时间序列回归,包括多对多和多对一架构的输入整形。

在过去的几年里,一般的递归神经网络,特别是 LSTM,在许多数据科学从业者中变得非常流行。尽管 LSTMs 的主要应用是自然语言处理(NLP ),但对于计算科学家和工程师来说,LSTMs 为数据中存在时间相关性的问题提供了创建强大回归模型的可能性。

你将学到什么

在本文中,您将学习如何:

  • 重塑 LSTM 培训的输入数据
  • Keras 拟合 LSTM 时间序列数据
  • 使用 LSTM 处理单变量和多变量数据集

输入数据形状混乱

撰写本文的主要原因之一是,在 Keras 等神经网络框架中,您需要以 3D 格式提供 LSTM 输入数据,这可能会让许多人感到非常困惑。更令人困惑的是 Keras 中输入维度的命名方式。 根据文档和源代码,Keras LSTM 输入数据的格式必须为:[batch_size,timesteps,input_dim]。 此外,可能不需要指定 batch_size。令人困惑。我知道。让我们看看如何将我们的 1D 和 2D 数据改造成 3D 数据形状,以便 LSTM 能够工作!

LSTM 架构以及与输入数据形状的关系

在开始数据重塑之前,让我们回顾一下存在哪些 LSTM 架构。我们来看下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1—http://karpathy.github.io/2015/05/21/rnn-effectiveness/ LSTM 网络架构(来源—)

我们不会逐一介绍。在我看来,对于时间序列问题,最有用的是多对一和多对多(图 1 中的最后一个),所以我们将更详细地讨论它们。

在 Keras 中,时间步长的数量等于 LSTM 单元的数量。这就是单词“时间步长”在形状的 3D 张量中的含义[batch_size,time steps,input_dim]。

为了更好地理解这一点,让我们考虑一个玩具(完全随机)数据集,如图 2 所示。在数据集中,我们有 2 个特征和 6 个时间步长(因为我们使用 Python,所以我们从 0 开始计数)。

首先,考虑多对一的例子。

在这种情况下,Keras 希望我们对数据进行整形,以便根据指定数量的先前和当前时间步长特征来计算时间步长 t 处的预测。 在右图所示的 LSTM 架构中,时间步数设置为 3。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2 —多对一架构示例

要素的数量等于 2D 数据集中的要素数量。因此,shape [batch_size,timesteps,input_dim]的 3D 张量中的单词“input_dim”表示原始数据集中的要素数量。在我们的例子中," input_dim"=2

为了将我们的原始 2D 数据整形为 3D“滑动窗口”形状,如图 2 所示,我们将创建以下函数:

def lstm_data_transform(x_data, y_data, num_steps=5):
    """ Changes data to the format for LSTM training 
for sliding window approach """ # Prepare the list for the transformed data
    X, y = list(), list() # Loop of the entire data set
    for i in range(x_data.shape[0]):
        # compute a new (sliding window) index
        end_ix = i + num_steps # if index is larger than the size of the dataset, we stop
        if end_ix >= x_data.shape[0]:
            break # Get a sequence of data for x
        seq_X = x_data[i:end_ix]
        # Get only the last element of the sequency for y
        seq_y = y_data[end_ix] # Append the list with sequencies
        X.append(seq_X)
        y.append(seq_y) # Make final arrays
    x_array = np.array(X)
    y_array = np.array(y) return x_array, y_array

形状的 3D 张量中的 batch _ size【batch _ size,timesteps,input_dim】可能不需要指定,可以指定只是为了加速训练过程。

现在,让我们看看我们需要如何为多对多 LSTM 模型重塑数据。为此,请看图 3。这里,我们有 2 个特征,数据集中的 6 个时间步长和 LSTM 神经网络中的 3 个时间步长。因此,为了将 2D 数据转换成所需的 3D 张量,我们可以如下使用 Numpy 功能:

num_steps = 3
num_features = 2
x_shaped = np.reshape(x, newshape=(-1, num_steps, num_features))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3—多对多架构示例

现在,让我们考虑两个例子,这样我们就可以完全理解如何重塑数据,并使用多对一和多对多 LSTM 模型。在第一个例子中,我们将有一个正弦波的一维自回归模型,而在第二个例子中,我们将有一个包含 3 个特征的 2D 数据集。

1D 示例—正弦波的自回归模型

在这种情况下,我们要做的是使用指定数量的先前时间步长预测时间步长 t 处的函数值。首先,让我们生成数据。

x = np.arange(0, 200, 0.5).reshape(-1, 1)
y = np.sin(x).reshape(-1, 1)

假设我们想要使用 sin 波的前 10 个时间步长来预测 sin 函数的下一个值。然后,为了将 LSTM 用于此任务,我们使用已经定义的函数来转换数据:

num_steps = 10
x_new, y_new = lstm_data_transform(y, y, num_steps=num_steps)
print ("The new shape of x is", x_new.shape)The new shape of x is (390, 10, 1)

请注意,我们为函数的 x 和 y 参数都传递了 y,因为我们想要创建一个自回归模型。

太好了!现在,我们有了 LSTM 形状,这应该对我们有用。接下来,我们分割数据集,80%用于模型训练,20%用于模型测试。

train_ind = int(0.8 * x.shape[0])
x_train = x_new[:train_ind]
y_train = y_new[:train_ind]
x_test = x_new[train_ind:]
y_test = y_new[train_ind:]

最后,我们定义一个简单的 Keras 模型并训练它!

model = Sequential()
model.add(LSTM(100, activation='tanh', input_shape=(num_steps, 1), 
               return_sequences=False))
model.add(Dense(units=50, activation='relu'))
model.add(Dense(units=1, activation='linear'))
adam = optimizers.Adam(lr=0.0001)
model.compile(optimizer=adam, loss='mse')
model.fit(x_train, y_train, epochs=20)

然后在测试集上进行预测:

test_predict = model.predict(x_test)
plt.style.use('ggplot')
plt.figure(figsize=(20, 7))
plt.plot(y_test, label="True value")
plt.plot(test_predict.ravel(), label="Predicted value")
plt.legend()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。—用于估计正弦波的多对一 LSTM 模型结果

我们得到了一个很好的 sin 函数!这为您提供了一个如何将 1D 数据重塑为 3D 数据以创建多对一架构的 LSTM 模型的示例。 注意,这里我们做了一个自回归模型,把 y 放在“lstm_data_transform”的 x_data 和 y_data 两个输入上。请随意将您的要素数据转换为 x_data,这样它会将 x_data 转换为所需的形式。

2D 示例——通过节流阀的流量

在这个例子中,我们将考虑有 3 个特性和 1 个目标变量的情况,并将使用**多对多和多对一架构。**目标变量是通过阻风门的流量。如果您对该流程一无所知,请不要担心,这只是一个说明性的示例,该概念将完全适用于手头的任何问题。

让我们看看数据帧的开头。在“特征”中,我们有节流器前的压力(P_WHCU)、节流器后的压力(P_WHCD)和节流器开口(0 到 1 之间的值)。因此,我们有 3 个特征和 1 个目标变量,即通过节流器的流量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

资料组

在数据集中,我们有 6570 个点。像以前一样,80%用于培训,20%用于测试。最初,我还使用应用于训练数据的标准缩放器来重新缩放数据,并转换训练和测试数据。

拆分:

train_ind = int(0.8 * x_data.shape[0])
x_train = x_data[:train_ind]
x_test = x_data[train_ind:]
y_train = y_data[:train_ind]
y_test = y_data[train_ind:]

缩放:

# scalers
scaler_x = StandardScaler()
scaler_y = StandardScaler()
# scaling
x_train_sc = scaler_x.fit_transform(x_train)
x_test_sc = scaler_x.transform(x_test)
y_train_sc = scaler_y.fit_transform(y_train)
y_test_sc = scaler_y.transform(y_test)

多对多培训

假设我们想要使用 3 个先前的时间步长特征来预测通过节流器的流量的当前值。首先,我们按如下方式重塑数据:

num_steps = 3
# training set
x_train_shaped = np.reshape(x_train_sc, newshape=(-1, num_steps, 3))
y_train_shaped = np.reshape(y_train_sc, newshape=(-1, num_steps, 3))
assert x_train_shaped.shape[0] == y_train_shaped.shape[0]
# test set
x_test_shaped = np.reshape(x_test_sc, newshape=(-1, num_steps, 3))
y_test_shaped = np.reshape(y_test_sc, newshape=(-1, num_steps, 3))
assert x_test_shaped.shape[0] == y_test_shaped.shape[0]

“newshape”参数的最后一个参数等于 3,因为我们有 3 个特征。

现在,我们编译这个模型。请注意,对于多对多模型,我们需要使用 return _ sequences = True:

model = Sequential()
model.add(LSTM(20, activation='tanh', input_shape=(num_steps, 3), return_sequences=True))
model.add(Dense(units=20, activation='relu'))
model.add(Dense(units=1, activation='linear'))
adam = optimizers.Adam(lr=0.001)
model.compile(optimizer=adam, loss='mse')

拟合模型并根据测试数据进行预测:

model.fit(x_train_shaped, y_train_shaped, epochs=10)
test_predict = model.predict(x_test_shaped)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-节流流量的多对多估计结果

我们看到,预测是好的,除了一些波动行为,这可能是由过拟合引起的,并通过更好的模型训练来减少。现在,让我们考虑相同的数据集,但采用多对一架构。

多对一培训

和以前一样,我们需要重塑我们的数据,但现在我们需要使用预建的函数:

num_steps = 3# training set
(x_train_transformed,
 y_train_transformed) = lstm_data_transform(x_train_sc, y_train_sc, num_steps=num_steps)
assert x_train_transformed.shape[0] == y_train_transformed.shape[0]# test set
(x_test_transformed,
 y_test_transformed) = lstm_data_transform(x_test_sc, y_test_sc, num_steps=num_steps)
assert x_test_transformed.shape[0] == y_test_transformed.shape[0]

之后,数据集就可以进行训练了。我们编译与多对多相同的模型。请注意,对于多对一模型,我们需要使用 return _ sequences = False:

model = Sequential()
model.add(LSTM(20, activation='tanh', input_shape=(num_steps, 3), return_sequences=False))
model.add(Dense(units=20, activation='relu'))
model.add(Dense(units=1, activation='linear'))
adam = optimizers.Adam(lr=0.001)
model.compile(optimizer=adam, loss='mse')

然后,我们拟合模型并对测试集进行预测:

model.fit(x_train_transformed, y_train_transformed, epochs=10)
test_predict = model.predict(x_test_shaped)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-节流流量的多对一估计结果

如我们所见,我们得到了与多对多模型相似的拟合。然而,这是一个简单的数据集,对于许多问题,结果可能不同。

结论

在这篇文章中,我们考虑了如何使用 Keras LSTM 模型进行时间序列回归。我们展示了如何将 1D 和 2D 数据集转换为 3D 张量,以便 LSTM 适用于多对多和多对一架构。在多对多的情况下,我们可以使用 Numpy 功能,而对于多对一,我们需要使用转换函数。此外,对于多对多,Keras 参数 return_sequence 必须等于 True,而对于多对一模型,它必须等于 False。我希望这篇文章对你有用,它将允许你从现在起在研究和日常工作中更多地使用 LSTM 模型!

如何使用 Python 调整图像大小

原文:https://towardsdatascience.com/how-to-resize-images-using-python-8aaba74602ed?source=collection_archive---------13-----------------------

“可扩展”的技术

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上由阿米拉里·米尔哈西米安拍摄的照片

可以肯定地说,每个“电脑人”都曾经需要调整图像的大小。MacOS 的预览版可以做到; Windows PowerToys 也可以。

如果你懂 Photoshop 或者 Gimp,那么你和我……我们不一样。命令行实用程序?好吧,现在我可以理解了。

幸运的是,图像处理和命令行工具是 Python 的两个专长。

本文旨在向您展示三件事:

  1. 什么是图像的基础知识。
  2. 用于处理图像的 Python 库。
  3. 您可以在自己的项目中使用的代码。

我们将要构建的命令行程序可以一次调整一个或多个图像文件的大小。

让我们跳进来吧!

创建图像

对于这个例子,我们将创建我们自己的图像,而不是找到一个真实的图像来操纵。

为什么?因为我不得不把事情复杂化,这就是原因!

事实上,创造我们自己的图像是一个很好的方式来说明图像到底是什么。这个调整大小的程序可以在 Instagram 上运行。

那么,什么是图像呢?在 Python 数据术语中,图像是整数元组列表的列表。

image = list[list[tuple[*int, float]]]

NumPy esque 定义将是一个 shape (h,w,4)的二维数组,其中h是高(上下)的像素数,w是横(从左到右)的像素数。

换句话说,图像是像素(单元)的列表(行)的列表(整个图像)。每个像素是一个由 3 个整数和 1 个可选浮点数组成的序列:红色通道、绿色通道、蓝色通道、alpha(可选的浮点数)。红色、绿色、蓝色通道(RGB)的值从 0 到 255。

从现在开始,为了简单起见,我们将讨论没有 alpha 通道的彩色图像。Alpha 是像素的透明度。图像也可以只有一个通道,其值从 0 到 255。这就是灰度图像,也就是黑白图像。但这是真实的世界…我们用彩色图像工作!

import matplotlib as plt
​
pixel: tuple = (200, 100, 150)
plt.imshow([[list(pixel)]])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从 Python 代码渲染的单个像素。

很性感。

用纯 Python 制作图像

Vanilla Python 完全能够创建图像。为了显示它,我将使用matplotlib库,您可以使用:

pip install matplotlib

创建像素:

创建图像:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是渲染的图像。在幕后,数据看起来是这样的(对于 90x90):

[[[234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [234, 162, 33],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [255, 0, 0],
  [234, 162, 33],
  ...

现在我们有了一个图像,让我们开始调整它的大小!

在普通 Python 中调整大小

心理!我们将而不是用 Python 写一个调整图像大小的算法。

为什么?

TLDR:工作量太大了。

图像处理算法中有太多的东西。有些人把他们的生命奉献给了它。重采样——用缩小图像中的一个像素代替高分辨率图像中的许多像素——本身就是一个巨大的话题。如果你想自己看看,在path/to/site-packages/PIL安装 Pillow 的时候可以查看一下 Pillow 的源代码中的Image.py

还有像抗锯齿和缩小差距这样的优化…这是无止境的。我们将站在巨人的肩膀上,这意味着相信计算机视觉领域的杰出工作,并无耻地调用一行程序来解决我们的问题。

如果您有兴趣了解更多关于处理图像时幕后发生的事情,我鼓励您更多地查看“机器视觉”主题!这绝对是一个蓬勃发展的领域,有比一个人有更多的时间去探索。

变得足够好,有一大群公司愿意为你的计算机视觉专业知识支付高价。自动驾驶、物联网、监控,应有尽有;都是从根本上依赖于处理图片(通常用 Python 或者 C++)。

一个很好的起点是查看 scikit-image

OpenCV

OpenCV 是图像处理的终极目标。OpenCV 是用 C++编写并移植到 Python 的,它运行在你自制的自动驾驶汽车的后备箱中,用于车道检测。

用于interpolation参数的选项是cv2包中提供的标志之一:

INTER_NEAREST – a nearest-neighbor interpolation 
INTER_LINEAR – a bilinear interpolation (used by default) 
INTER_AREA – resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method. 
INTER_CUBIC – a bicubic interpolation over 4×4 pixel neighborhood INTER_LANCZOS4 – a Lanczos interpolation over 8×8 pixel neighborhood

这将返回:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它达到了我们的预期。图像从 900 像素高、900 像素宽变成了 450 X 450(仍然是三个颜色通道)。上面的截图看起来不太好,因为朱庇特实验室的matplotlib着色。

枕头

pillow库在Image类上有一个调整大小的方法。它的论据是:

size: (width, height)
resample: Defaults to BICUBIC. A flag for the resampling algorithm.
box: Defaults to None. A 4-tuple defining a rectangle of the image to work on within parameters (0, 0, width, height).
reducing_gap: Defaults to None. A resampling optimization algorithm to make the output look better.

在这里它是为我们的目的而设的一个函数:

使用 Pillow 的功能与 OpenCV 非常相同。唯一的区别是PIL.Image.Image类有一个属性size来访问图像的(width, height)

结果是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意show方法如何打开操作系统的默认程序来查看图像的文件类型。

创建命令行程序

现在我们有了一个调整图像大小的函数,是时候用一个运行 resize 的界面来完善它了。

调整一个图像的大小是好的。但是,如果我们要经历所有这些麻烦,我们也需要批量处理图像的能力。

我们将要构建的界面将是最简单的:一个命令行工具。

托盘项目是 Flask(Jinja 模板引擎)背后的天才社区,与我们现在相关:点击

pip install click

Click 是一个制作命令行程序的库。这比使用简单的argparse或者在if __name__ == '__main__':语句中加入一些 if-then 逻辑要好得多。因此,我们将使用点击来装饰我们的图像大小。

这是从命令行调整图像大小的完整脚本!

命令行程序从入口点函数main运行。参数由click.option装饰者传递给main:

  • pattern使用一个 Python / RE2 样式的字符串来定位相对于脚本运行目录的一个或多个图像。--pattern="../catpics/*.png"将向上一级并找到catpics文件夹,并返回该文件夹中带有.png图像扩展名的任何文件。
  • scale接受一个浮点数或整数,并将其传递给resize函数。这个脚本很简单,这意味着没有数据验证。如果你添加代码,检查scale是一个介于 5 和 99 之间的数字(合理的缩小参数)。现在,你可以通过-s "chicken nuggets",直到 Pillow 停止程序才会抛出异常。
  • quiet是一个选项参数,用于在程序运行时不希望文本打印为标准输出。

从命令行运行程序:

python resize.py -s 35 -p "./*jpg"

结果:

$ py resize.py -p "checkers.jpg" -s 90
resized image saved to checkers_90_810x810.jpg.

正在检查文件夹:

$ ls -lh checkers*
-rw-r--r-- 1 nicho 197609 362K Aug 15 13:13 checkers.jpg
-rw-r--r-- 1 nicho 197609 231K Aug 15 23:56 checkers_90_810x810.jpg

不错!所以程序缩小了图像,给了它一个描述性的标签,我们可以看到文件大小从大约 362KB 变成了大约 231KB!

为了看到程序一次处理多个文件,我们将再次运行它:

$ py resize.py --pattern="checkers*" --scale=20
resized image saved to checkers_20_180x180.jpg.
resized image saved to checkers_90_810x810_20_162x162.jpg.

和文件系统:

$ ll -h checkers*
-rw-r--r-- 1 nicho 197609 362K Aug 15 13:13 checkers.jpg
-rw-r--r-- 1 nicho 197609 1.8K Aug 16 00:23 checkers_20_180x180.jpg
-rw-r--r-- 1 nicho 197609 231K Aug 15 23:56 checkers_90_810x810.jpg
-rw-r--r-- 1 nicho 197609 1.8K Aug 16 00:23 checkers_90_810x810_20_162x162.jpg

好吧,递归运行这个程序只适用于疯狂的人,但是它表明你可以同时运行任意数量的匹配模式的图像。去吧,自动化!

奖金

点击是一个神奇的工具。它可以包装一个函数,并从一个if __name__ == '__main__':语句“以正常方式”在一个模块中运行。(而实际上,它甚至不需要那个;你可以定义和修饰你想要运行的函数。)但是它真正的亮点是将脚本作为一个包安装。

这是通过 Python 自带的setuptools库完成的。

这是我的setup.py

使用以下命令构建可执行文件/包装包:

*pip install -e .*

现在你可以不使用python命令来调用这个脚本了。另外——作为一个额外的奖励——如果你将新的可执行文件添加到路径中的一个文件夹中,你可以从你计算机上的任何地方调用这个程序,就像resize -p *jpg -s 75

结论

本教程变化很大:首先浏览一些第三方 Python 库进行图像处理。然后使用 Python 从头构建一个图像,以更好地理解图像实际上是什么。然后,选择其中一个选项并构建一个脚本来缩小图像,同时保持它们的比例。最后—吸入——将所有这些都放在一个命令行实用程序中,使用click接受可配置的选项。

再多说几句我就完事了,我保证。

我只想说:我和你一起一口气谈完所有这些话题是有原因的。这个原因不是因为我有多动症。

是因为编程思路需要从想法到执行的闭环。关于如何用 Python 做一些事情的一次性概念是好的…但是归根结底,编程语言是一种工具。它需要帮助我们完成有价值的事情。

当你开始觉得教程“又一件你必须学的东西”压得你喘不过气来时,这意味着是时候放下阅读,开始为一些小项目编码了。一次就能完成的事情,感觉自己有进步。

记住,编写代码可能需要几个小时或几天的时间。然而运行只需要几毫秒。你做的程序不一定要大。你做的任何一件事,只要能节省你的时间或让你产出更多,都有可能让你受益终生!

资源

如何恢复您的 Jupyter 笔记本会话

原文:https://towardsdatascience.com/how-to-restore-your-jupyter-notebook-session-dfeadbd86d65?source=collection_archive---------19-----------------------

重新启动计算机时,不要丢失 Jupyter 笔记本会话中的进度。Dill 包可以保存笔记本会话供以后使用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

尼克·舒利亚欣在 Unsplash 上的照片

H 有没有发生过这样的事情,你重启了电脑,却忘记了你打算以后用的那个重要的 Jupyter 笔记本?“花了这么长时间才完成,现在一切都没了”。

我感觉到你了!在我身上发生过很多次。

好吧,我唯一能给你的建议就是“重启内核,重新运行所有的细胞”。但下次就不一定了。

有一个工具可以让您保存 Jupyter 笔记本会话的状态,并在以后用一个命令恢复它。

这里有几个你可能会感兴趣的链接:

- [Labeling and Data Engineering for Conversational AI and Analytics](https://www.humanfirst.ai/)- [Data Science for Business Leaders](https://imp.i115008.net/c/2402645/880006/11298) [Course]- [Intro to Machine Learning with PyTorch](https://imp.i115008.net/c/2402645/788201/11298) [Course]- [Become a Growth Product Manager](https://imp.i115008.net/c/2402645/803127/11298) [Course]- [Deep Learning (Adaptive Computation and ML series)](https://amzn.to/3ncTG7D) [Ebook]- [Free skill tests for Data Scientists & Machine Learning Engineers](https://aigents.co/skills)

上面的一些链接是附属链接,如果你通过它们进行购买,我会赚取佣金。请记住,我链接课程是因为它们的质量,而不是因为我从你的购买中获得的佣金。

如果你错过了我关于 Jupyter 笔记本的其他帖子:

[## 你还在用 JupyterLab 吗?

数据科学城推出了一款新的高端产品。它有优越的编码辅助,调试和更多…是吗…

towardsdatascience.com](/are-you-still-using-jupyterlab-ce1a4339c0a9) [## JupyterLab 2.0

让我们来一窥 Jupyter 笔记本编辑的未来。有了编码等特性,未来看起来一片光明…

towardsdatascience.com](/jupyterlab-2-0-edd4155ab897) [## 3 个必备的 JupyterLab 2.0 扩展

JupyterLab 刚刚成为一个成熟的 IDE,具有代码辅助、调试和 Git 等功能——欢迎来到未来……

towardsdatascience.com](/3-must-have-jupyterlab-2-0-extensions-41024fe455cc) [## 将 Jupyter 笔记本转变为电子书

将你的 Jupyter 笔记本转换成 PDF、EPUB 和 AWZ3 格式的精美电子书的一些技巧。不要花几个小时…

towardsdatascience.com](/transform-jupyter-notebook-to-an-ebook-ef3a9d32ac4f) [## 如何从 Jupyter 笔记本运行 SQL 查询

SQL IDEs 不能可视化数据。您知道吗,您可以使用…运行并可视化查询结果

towardsdatascience.com](/how-to-run-sql-queries-from-a-jupyter-notebook-aaa18e59e7bc)

遇见迪尔

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Dill 保存和恢复会话

Dill 使您能够保存 Jupyter 笔记本会话的状态,并通过一个命令恢复它。

安装 Dill 就像安装任何其他 Python 包一样简单:

pip install dill

要保存 Jupyter 笔记本会话,只需执行以下命令:

import dill
dill.dump_session('notebook_env.db')

要恢复 Jupyter 笔记本会话:

import dill
dill.load_session('notebook_env.db')

Dill 也使用 python 解释器会话。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Gif 来自 giphy

缺点

Dill 有一些你应该知道的缺点:

  • 这种方法不适用于发电机。
  • 保存的会话不包括生成的图形。所以你需要重新运行代码。
  • 一些用户报告说,恢复会话在另一台计算机上不起作用。

关于莳萝

Dill 将 python 的 pickle 模块扩展到大多数内置 python 类型,用于序列化和反序列化 Python 对象。序列化是将对象转换为字节流的过程,其逆过程是将字节流转换回 python 对象层次结构。

Dill 为用户提供了与 pickle 模块相同的界面,还包括一些额外的特性。除了 pickling python 对象之外,Dill 还提供了在单个命令中保存解释器会话状态的能力。因此,保存解释器会话、关闭解释器、将标记的文件运送到另一台计算机、打开新的解释器、取消标记会话并因此从原始解释器会话的“保存”状态继续是可行的。

Dill 可用于将 python 对象存储到文件中,但主要用途是通过网络以字节流的形式发送 python 对象。Dill 非常灵活,允许序列化任意用户定义的类和函数。因此,Dill 并不旨在防止错误或恶意构造的数据。由用户决定他们提取的数据是否来自可信的来源。

主要特点

莳萝可以腌制以下标准型[1]:

  • none,type,bool,int,long,float,complex,str,unicode,
  • 元组、列表、字典、文件、缓冲区、内置、
  • 新旧风格的课程,
  • 新旧样式类的实例,
  • 集合,冷冻集,数组,函数,异常

莳萝也可以腌制更多的“奇异”标准型[1]:

  • 有收益的函数,嵌套函数,lambdas
  • 单元格,方法,unboundmethod,模块,代码,methodwrapper,
  • dictproxy,methoddescriptor,getsetdescriptor,memberdescriptor,
  • wrapperdescriptor,xrange,slice,
  • 未实现,省略号,退出

莳萝还不能腌制这些标准类型[1]:

  • 框架、生成器、回溯

Dill 还提供了以下功能[1]:

  • 保存和加载 python 解释器会话
  • 从函数和类中保存和提取源代码
  • 交互式诊断酸洗错误

参考文献

  • [1]https://github.com/uqfoundation/dill

我的选择

莳萝无疑是你工具箱中的一个有用的工具。当我需要在另一个操作系统中重启我的计算机时,或者当我预计 Jupyter 内核可能会因为下一个命令而崩溃时,我会使用它。

IMO Dill 不是你应该在每个 Jupyter 笔记本中自动使用的工具。明智地使用它,只在你需要的时候。此外,如果任何底层代码使用了生成器,也要小心,因为还不能 pickle 它。

在你走之前

Twitter 上关注我,在那里我定期发布关于数据科学和机器学习的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

考特尼·海杰Unsplash 拍摄的照片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值