如何使用 Arctic 更有效地查询时间序列数据
加快 Python 时序数据处理脚本的速度
有没有想过在处理大型时间序列数据集时,如何提高数据分析过程的时间效率? 北极 也许就是你要找的。
史蒂文·勒勒姆在 Unsplash 上拍摄的照片
Arctic 是一个为 Python 设计的数据库,目的只有一个:性能。使用 Mongo DB 作为底层数据库,高效存储数据,使用 LZ4 压缩,每秒可以查询上亿行。
除了性能数字之外,它还提供了一些非常有说服力的特性:
什么?商店?
存储引擎 是直接与底层 MongoDB 数据库交互的机制。他们被称为斗牛士。为了使您的查询获得最佳性能,您可以选择 S tores: 中的三个内置参数之一
- 版本存储 默认存储。基于键值和时间序列。它使创建数据快照和检索数据变得容易,而不会损失任何性能。
- tick store 面向列,支持动态字段。适用于高频金融数据或任何持续跳动的数据。不支持快照。
- chunk store 允许以预定义的块大小存储数据。不支持快照。
您也可以将自己的存储实现和插件放入 Arctic,以更好地适应您自己的数据。
“好的,我明白了,非常有说服力的观点,但是我不知道是否值得使用一个新的数据库……”你,读者,还没有被说服。
入门指南
那么,让我们看看使用 Arctic 有多简单,看看我是否能让你,*读者,*更深入地了解使用另一个数据库的想法。这将是一个非常简单的演练,只是为了说明北极的一些核心功能。
安装
首先,你需要安装并运行 MongoDB。你可以在官方 MongoDB 文档页面阅读你的操作系统的说明。
完成后,我们可以使用 pip 安装 Arctic
pip install git+https://github.com/manahl/arctic.git
我们需要安装熊猫库,因为我们将处理[DataFrames](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html)
pip install pandas
编码
首先:让我们将北极导入到我们的空 Python 脚本中
from arctic import Arctic
现在,我们需要将 Arctic 连接到它的底层 MongoDB 实例。您可以将 Arctic 连接到托管在云上或本地网络中的任何 MongoDB 实例。因为我在笔记本电脑上运行 MongoDB,所以我将使用localhost
作为我的实例的地址。
db = Arctic('localhost')
太好了!现在我们需要创建一个库。
Arctic 使用库的概念分离不同的数据。 他们可以是市场、地区、用户等。
在本例中,我将使用一个大约 160MB 的 CSV 文件,其中包含一些财务数据。让我们创建一个财务库来存储它。通过不向initialize_library
方法传递一个lib_type
值,Arctic 将默认这个库使用版本存储存储引擎。对于我们的例子来说,这很好。
db.initialize_library('Finance')
我们需要访问刚刚创建的库,以便向其中写入一些数据。让我们用库名索引我们的db
对象。
finance_library = db['Finance']
在写入数据之前,让我们打开时间序列数据文件。在本例中,我将使用一个名为[finance.csv](https://github.com/tgcandido/time-series-with-arctic/blob/master/finance.csv)
的文件(本例中使用的 CSV 结构的演示文件)。
让我们使用 Pandas 库打开 CSV 文件。首先,我们需要导入库,然后使用[read_csv](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html)
方法将内容读入一个熊猫DataFrame
。
在将string
转换为datetime
之后,我们还会将unix
列设置为DataFrame
的索引。
import pandas as pd
df = pd.read_csv('finance.csv')
df['unix'] = pd.to_datetime(df['unix'])
df.set_index('unix', inplace=True)
好吧!我们准备将数据写入北极。为此,我们需要定义一个符号。
一个符号是一个字符串,我们将使用它在一个库中读取或写入我们的数据。
让我们使用符号Stocks
将我们加载的df
的内容存储到库finance_library
中。
finance_library.write('Stocks', df)
我们可以通过使用方法read
并访问返回对象的data
属性以获得结果DataFrame
来验证数据是否被正确插入。
new_df = finance_library.read('Stocks').data
要执行查找特定时间间隔的查询,我们可以使用read
方法中的date_range
参数。
首先,我们需要从arctic.date
导入DateRange
:
from arctic.date import DateRange
我们可以将一个DateRange
的实例传递给date_range
参数。我们可以通过调用它的构造函数并将开始日期和结束日期作为参数传递来创建它。
custom_range = DateRange('2020-01-01', '2020-01-02')
现在我们可以运行查询:
range_df = finance_library.read('Stocks', date_range=custom_range).data
就是这样!在这个非常简单的例子中,我们通过实现一个处理一些财务数据的 Python 脚本看到了 Arctic 的主要特性。
官方文档真的很好,几乎包含了所有需要的信息,从基础到高级特性。没有必要阅读软件包的源代码,但是如果您有兴趣了解 Arctic 的工作方式以及它是如何实现其高性能的,我鼓励您这样做。
但恐怕我不会说服你,*读者,*除非我给你看一些业绩数字,对吗?
基准
这些数字都是在我的 2.3 GHz 双核 13 英寸 2017 Mac Book Pro 上运行脚本获得的。
请记住,这绝不是北极数据库的完整基准。这是用 Python 的时间库在 Arctic、MongoDB、SQLite 和普通 CSV 文件之间进行的简单比较。
性能比较
下面是运行一个简单的 Pandas read_csv
、一个 PyMongo 查询、 SQLAlchemy 查询(使用 SQLite 数据库)和一个 Arctic read
查询(使用大约 160 MB 的财务数据作为源)的结果。
在引擎返回结果后,PyMongo 和 SQLAlchemy 查询结果被解析成一个DataFrame
,这个时间在基准测试中被考虑。两个数据库都由unix
列索引。
- 熊猫
read_csv
:4.6 秒 - PyMongo 查询:~28 秒
- SQLite 查询:大约 30 秒
- 北极
read
: ~1.45 秒
这些结果来自于一个“get all”类型的查询。让我们尝试添加一个范围参数,看看结果是否成立。
new_df = finance_library.read('Stocks', date_range=DateRange('2020-01-01', '2020-01-02')).data
- 熊猫
read_csv
:4.9 秒 - PyMongo 查询:~1.66 秒
- SQLite 查询:~0.7 秒
- 北极
read
与DateRange
: ~0.12 秒
在使用了DateRange
之后,结果仍然支持 Arctic,这是我们在处理时间序列数据时使用的主要查询类型。
磁盘压缩
同一个 CSV 文件用于播种每个数据库。
这是每个备选方案使用的磁盘空间量:
- 普通 CSV 文件:160.8 MB
- MongoDB 集合:347.31 MB
- SQLite: 297.9 兆字节
- 北极:160.59 兆字节
结论
在处理大型时间序列数据集时使用 Arctic 使我们能够实现显著的速度和压缩改进。凭借其简单的设置和使用,它可以提高生产力和节省一些宝贵的时间。
即使不使用其更高级的功能,如快照或其他存储引擎,我们也可以为使用 Arctic 处理时间序列数据提供有力的支持。
演练和基准测试的代码可以在这里找到。出于许可的原因,它不包含完整的 CSV 文件,但是我鼓励您使用您自己的一些数据来运行,看看您的结果是否与我的相似。
我希望你喜欢阅读这篇文章。
如果你有,可以考虑在 推特 上关注我。
谢谢你的时间。保重,继续编码!
如何快速创建和打开熊猫列表
大蟒
使用 df.explode()解决使用熊猫数据框架时的常见问题
当你需要打开清单时,很快你就会像这个人一样微笑
预处理和“数据争论”占用了大量时间,但这并不总是数据分析项目中最有趣的部分。
当涉及到重新格式化初始数据时,必须解包一个列表。幸运的是,Pandas 提供了许多常见问题的矢量化解决方案,所以我们不必为在数据帧中解包列表而感到太大压力。
在这篇文章中,我们将关注两件事:
- 如何使用
df.explode()
取消数据帧中带有列表值的列嵌套; - 如何使用
Series.str.split()
从一个字符串创建一个列表。
我们将使用这个视频游戏销售数据的修改版本,所以如果你想跟进的话,可以下载 csv 文件。这一次,我在最底部加入了代码来获取每个示例的初始表。我建议先浏览一遍这段代码,然后复制代码以获得输入,然后再尝试示例。
如何在熊猫数据框架中取消嵌套(分解)一列
我们的初始表格如下所示:
目标是分隔“流派”列中的所有值,以便每行只有一个值。就数据库规范化而言,这将是实现“第一范式”的一个步骤,在第一范式中,每一列只包含原子(不可分的)值。
为此,我们需要做的就是使用df.explode()
功能。我们只需要传递一个参数,它是包含列表 like 值的列的名称。
我们的代码是这样的。
df2 = df2.explode('Genre').drop_duplicates()
结果数据帧的子集如下所示:
现在我们有了一个包含每个出版商所有不同类型的表格。我们还通过在数据帧上传递.drop_duplicates()
来确保只有唯一的值。
您会注意到原始数据帧中的索引值是为每一行保留的,这表明df.explode()
函数所做的只是在 iterable 中分离元素,同时保持所有其他行的值不变。如果我们想要新的索引值,我们也可以轻松地通过.reset_index()
。
如何在 Pandas 数据框架中创建(和解包)一个具有列表式值的列
在最后一个问题中,我们使用了一个包含一列列表的数据帧。然而,您经常会处理不完全符合预定义的 Pandas 函数的数据。例如,假设我们有一个类似于上一个问题的表格,如下所示:
你能看出区别吗?
“流派”列中仍有一堆值,但它们不再在 Python 列表中。df.explode()
函数不起作用,因为根据它的文档:
这个例程(df.explode)将分解类似列表的内容,包括列表、元组、序列和 np.ndarray。
我们目前对每个列值都有多个条目,但是它们不是“类似列表”的格式。我们只有一堆很长的字符串。
为了将子字符串组合成一个列表,我们将使用一行简单的代码将字符串转换成一个列表:
df3['Genre'] = df3['Genre'].str.split(',')
这里,我们在“流派”列上使用了Series.str.split()
方法来创建一个列表。顾名思义,这个方法在您指定的分隔符上分割字符串,在我们的例子中是逗号。如果你不向split()
传递任何东西,这个方法将试图在空白上分割一个字符串,但是我们的字符串没有空白。
生成的表格如下所示:
现在,我们有了一个与前一个几乎相同的表,只是这次所有不同的值都在一个列表中。
我们现在要做的就是运行与第一个问题中相同的代码行:
df3 = df3.explode('Genre')
姐姐创作的漫画
我希望你发现这个快速浏览df.melt
对你的熊猫工作有用!如果你需要在你的数据框架中创建或者解压列表,你可以分别使用Series.str.split()
和df.explode()
方法。
正如所承诺的,下面是设置这两个示例的代码:
import pandas as pddf = pd.read_csv('vgsales.csv').dropna()
df['Year'] = df['Year'].astype(int)# problem 1
df2 = df.groupby('Publisher')['Genre'].apply(','.join).reset_index()
df2 = df2.loc[(df2['Publisher']=='Nintendo') | (df2['Publisher']=='Ubisoft') | (df2['Publisher']=='Activision')]
df2['Genre'] = df2['Genre'].str.strip().str.split(',')# problem 2
df3 = df.groupby('Publisher')['Genre'].apply(','.join).reset_index()
df3 = df3.loc[(df3['Publisher']=='Infogrames') | (df3['Publisher']=='Wanadoo')]
你可以看到我使用了.loc[]
函数为我的数据帧设置了一些条件,所以如果你还不熟悉使用它,你可以看看这个:
用向量化解决方案看一下“for 循环”的替代方案。
towardsdatascience.com](/you-dont-always-have-to-loop-through-rows-in-pandas-22a970b347ac)
我在这里也使用了.apply()
,这是一种沿着数据帧的轴执行函数的简单方法。对于逐行编辑数据帧的另一种方法(我建议只在遇到问题没有的矢量化熊猫解决方案时才使用),请查看以下内容:
使用 pd.loc 根据条件更改数据的子集。
towardsdatascience.com](/a-really-simple-way-to-edit-row-by-row-in-a-pandas-dataframe-75d339cbd313)
祝你和熊猫的冒险之旅好运!
如何快速找到最适合直方图的条柱
数据探索是每个数据科学项目中的关键步骤,通常从查看单个变量的分布开始。这就是直方图的亮点。
直方图对于可视化列的分布非常有用,这有助于理解数据的重要方面。例如,通过简单地查看直方图,我们可以立即识别数据中的异常值甚至错误(例如,包含患者年龄的列中的负值)。
当处理直方图时,我们几乎总是最终调整条柱宽度,这是一个关键参数,因为它决定了我们可以从图中提取多少信息和什么类型的信息。
在本文中,我将向您展示如何通过在 Jupyter Notebook 或 JupyterLab 中使用 plotly 和 ipywidgets 创建一个交互式直方图来快速找到您的最佳 bin 宽度。
**注意:**尽管我用 plotly 展示了交互式重新绑定,但是您可以将我所展示的逻辑应用于任何绘图库,比如 seaborn 和 matplotlib。
为了可视化,我将以分钟为单位显示 2013 年从纽约出发的 300,000 多个航班的飞行时间( NYCflights13 数据)。你可以在 GitHub 上找到这篇文章的完整代码。
交互式宁滨直方图
在这张图中,你可以看到最终的结果。如果我们通过一个滑块改变箱的宽度,plotly 图会自动调整。
为了实现这个行为,我们将plotly.graph_objs
(创建 plotly 图)与ipywidgets.Floatslider
结合起来。
这是创建可重组直方图的代码。
让我们一行一行地过一遍。
逐行解释代码
0.功能签名
注意,我们的函数有两个参数:series
一只熊猫。Series 和initial_bin_width
,指定我们希望在绘图中将 a 作为默认值的 bin 宽度。在我们的例子中,它是 10 分钟的广播时间窗口。
1.创建图形
我们生成一个新的FigureWidget
实例。FigureWidget
物体是 plotly 的新“魔法物体”。你可以在 Jupyter 笔记本或 JupyterLab 中显示它,就像任何普通的 plotly 图形一样。但是,这种方法有一些优点:
- FigureWidgets 可以与 ipywidgets 相结合,以创建更强大的结构(事实上,这就是 FigureWidgets 的设计目的)
- 您可以在 Python 中以各种方式操作
FigureWidget
- 您还可以收听一些事件和
- 当事件被触发时,您可以执行更多的 Python 代码
FigureWidget
接收属性data
,该属性指定了我们想要显示的所有轨迹的列表。在我们的例子中,我们只想显示一个直方图。直方图的 x 值来自series
。我们通过将字典传递给xbins
来设置 bin 宽度。当我们在字典中设置size=None
时,plotly 会为我们选择一个 bin 宽度。
2.创建滑块
我们使用ipywidgets
库生成一个FloatSlider
。通过这个滑块,我们稍后将能够操纵我们的直方图。
3.保存对直方图的引用
我们获取对直方图的引用,因为我们想在最后一步操作它。特别是,我们将更改对象的xbins
属性,我们可以通过histogram_object.xbins
访问它。
4.编写并使用回调
我们已经实现的FloatSlider
带有一些魔力。每当它的值改变时(即我们移动滑块),它就触发一个事件。我们可以使用该事件来更新直方图中的区间宽度。从技术上来说,您可以通过调用 bin 滑块上的observe
方法来实现这一点,向它传递您想要调用的函数(在我们的例子中是set_bin_size
,并告诉它何时调用该函数(name="value"
意味着每当滑块的value
改变时我们都会调用该函数)。现在,每当滑块的值改变时,它就会调用set_bin_size
。set_bin_size
通过魔术参数change
访问滑块的值,魔术参数是一个字典,包含关于由bin_slider
触发的事件的数据。例如,change["new"]
包含滑块的新值,但是您也可以使用change["old"]
来访问它以前的值。注意,你不必使用参数名change
。你可以给它取任何你想要的名字。
在回调函数set_bin_size
中,我们可以看到它简单地采用了引用histogram_object
,以便通过覆盖xbins
来更新FigureWidget
的 bin 设置(即改变 bin 宽度)。
当我们把上面的所有部分放在一起时,我们就有了第一个漂亮的交互式直方图的原型。
结论
直方图是开始探索数据集的单个列的一个很好的方法。使用 plotly,我们可以创建强大的交互式可视化,并可以使用 ipywidgets 进一步增强。
在本文中,我向您展示了在 Jupyter Notebook 或 JupyterLab 中使用 plotly 和 ipywidgets 时,如何交互式地快速找到直方图的(主观)最佳条宽。
在 8080 实验室,我们使用我们的 python 工具 bamboolib 中的重新绑定特性。与许多其他交互式功能一起,它帮助我们的用户更快地获得洞察力。
如果您对本文有任何反馈或建设性的批评,或者想讨论如何为直方图添加更多功能,请随时通过 LinkedIn 联系我。
原载于 2020 年 02 月 06 日 https://8080labs.com*。*
如何根据语义相似度对文本内容进行排序
使用 NLP、TF-idf 和 GloVe 查找 Python 中的相关内容并进行排名
搜索是查找内容的标准工具——无论是在线还是设备上的——但是如果你想更深入地根据单词的含义来查找内容呢?为自然语言处理开发的工具会有所帮助。
帕特里克·托马索在 Unsplash 上拍摄的照片
内容
在本文中,我们将介绍两种计算文本相似度的方法:
- 术语频率-逆文档频率(TF-idf) : 这查看在两个文本中出现的单词,并根据它们出现的频率对它们进行评分。如果你希望两个文本中出现相同的单词,这是一个有用的工具,但是有些单词比其他的更重要。
- 语义相似度:根据单词的相似程度来评分,即使它们并不完全匹配。它借用了自然语言处理(NLP)的技术,比如单词嵌入。如果文本之间的单词重叠是有限的,这是很有用的,例如,如果您需要将’水果和蔬菜’与’西红柿’联系起来。
对于第一部分,我们将单独使用scikit-learn
中的 TF-idf 实现,因为它非常简单,只需要几行代码。对于语义相似性,我们将使用来自gensim
的一些函数(包括它的 TF-idf 实现)和来自手套算法的预训练单词向量。此外,我们还需要一些来自nltk
的工具。这些软件包可以使用 pip 安装:
pip install scikit-learn~=0.22
pip install gensim~=3.8
pip install nltk~=3.4
或者,您可以从 GitHub 存储库中获取示例代码,并从需求文件中安装:
git clone [https://github.com/4OH4/doc-similarity](https://github.com/4OH4/doc-similarity)cd doc-similaritypip install -r requirements.txt
使用 Python - 4OH4/doc-similarity 中的语义相似度对文档进行排序
github.com](https://github.com/4OH4/doc-similarity)
快速入门
- 运行
examples.ipynb,
中的演示代码 - 使用
tfidf.rank_documents(search_terms: str, documents: list)
功能根据重叠内容对文档进行评分, - 使用
docsim.DocSim()
类对使用doc2vec
和GloVe
单词嵌入模型的文档进行相似性评分。
术语
文档 :字符串形式的一段文本。这可能只是几句话,也可能是一整部小说。
文集 :文件的集合。
术语 :文档中的一个词。
1.TF-idf
术语频率-逆文档频率(或 TF-idf)是一种基于文档共享单词的重要性对文档相似性进行评分的既定技术。非常高层次的总结:
- 如果某个术语(单词)在文档中频繁出现,则该术语在该文档中可能很重要。
- 但是,如果一个术语在许多文档中频繁出现,则该术语通常可能不太重要。
TF-idf 分数根据一个单词出现的频率来估计这两种启发式方法之间的权衡。这里有更详细的总结。
我们可以用几行代码从scikit-learn
中创建并装配一个 TF-idf 矢量器模型:
在这里,我们创建模型并使用文本语料库进行“拟合”。TfidfVectorizer
使用其默认的记号赋予器处理预处理——这将字符串转换成单个单词“记号”的列表。它产生包含频率项的文档向量的稀疏矩阵。
然后,我们用文档的第一个向量(包含搜索项)的点积(线性核)来确定相似性。我们必须忽略第一个相似性结果([1:]
),因为这是比较搜索词本身。
这对于一个简单的例子来说很有效,但是在现实世界中可能会因为一些原因而失败。
首先,在匹配过程中使用诸如[‘and ‘,’ on ‘,’ the ‘,’ are’]这样的词没有多大意义,因为这些停用词不包含上下文信息。在确定相似性之前,这些单词应该被剔除。
其次,我们希望“水果”和“水果”被认为是相关的,尽管上面的模型只能找到精确的匹配。解决这个问题的一个方法是将每个单词简化为最简单的 引理 — 为此我们需要一个引理器。
在这种情况下,第二个文档中的“tomatos”被 lemmatizer (tokenizer)简化为“tomato ”,然后与搜索词中的相同单词进行匹配。
从概念上讲,每个文档都是高维空间中的一个点,其中维度的数量等于文本语料库中唯一词的数量。这个空间通常是非常空的,因为文档包含各种各样的单词,所以点(文档)之间的距离很大。在这种情况下,点之间的角度作为相似性的度量比距离相关的度量(如欧几里德距离)更有意义。
这里的相似度被称为余弦相似度。TfidfVectorizer
的输出(默认情况下)是 L2 归一化的,因此两个向量的点积是向量所表示的点之间角度的余弦。
摘要:TF-idf
- 当文档很大和/或有很多重叠时,它速度很快,效果很好。
- 它寻找精确的匹配,所以至少你应该使用一个 lemmatizer 来处理复数。
- 当比较短的文档和有限的术语种类时——比如搜索查询——有一个风险,就是在没有精确的单词匹配时,你会错过语义关系。
2.语义相似度
一种更高级的方法是根据单词的相似程度来比较文档。例如,“苹果”和“橙子”可能比“苹果”和“木星”更相似。大规模判断单词相似性是困难的——一种广泛使用的方法是分析大量文本,将经常一起出现的单词排序为更相似。
这是单词嵌入模型 GloVe 的基础:它将单词映射到数字向量——多维空间中的点,以便经常一起出现的单词在空间上彼此靠近。这是一种无监督学习算法,由斯坦福大学于 2014 年开发。
在前面的例子中,我们使用了来自nltk
包的WordNetLemmatizer
对我们的数据进行词干化和标记化(转换成单个单词串的list
)。这里我们在gensim
中做了类似的预处理,也删除了任何可能存在的 HTML 标签,比如我们从网上抓取了数据:
然后,我们创建一个相似性矩阵,其中包含每对单词之间的相似性,使用术语频率进行加权:
最后,我们计算查询和每个文档之间的软余弦相似度。与常规余弦相似度不同(对于没有重叠项的向量,它将返回零),软余弦相似度也考虑单词相似度。
代码库中的笔记本中有完整的示例。还有一个自包含的DocSim
类,可以作为模块导入,用于运行语义相似性查询,而无需额外的代码:
from docsim import DocSimdocsim = DocSim(verbose=True)similarities = docsim.similarity_query(query_string, documents)
GloVe word 嵌入模型可能非常大——在我的机器上,从磁盘加载大约需要 40 秒。然而,一旦它被加载,随后的操作会更快。该类的多线程版本在后台加载模型,以避免长时间锁定主线程。它以类似的方式使用,尽管如果模型仍在加载时会引发一个异常,所以应该首先检查model_ready
属性的状态。
from docsim import DocSim_threadeddocsim = DocSim_threaded(verbose=True)similarities = docsim.similarity_query(query_string, documents)
概述:使用 GloVe 的语义相似性
- 它更加灵活,因为它不依赖于寻找精确的匹配。
- 涉及的计算要多得多,所以可能会慢一些,而且单词嵌入模型可能会相当大,需要一段时间来准备第一次使用。这很容易扩展,但是运行单个查询很慢。
- 大多数单词与其他单词有某种程度的相似性,因此几乎所有文档都会与其他文档有某种非零的相似性。语义相似性有利于按顺序排列内容,而不是对文档是否与特定主题相关做出具体判断。
我们已经研究了两种比较文本内容相似性的方法,例如可能用于搜索查询或内容推荐系统的方法。第一个(TF-idf)基于共有词的出现频率对文档关系进行评分。当文档很大和/或有很多重叠的术语时,这种方法速度很快,效果很好。第二种技术寻找表达相似概念的共有词,但不要求完全匹配:例如,它将“水果和蔬菜”与“番茄”联系起来。这种方法速度较慢,有时会产生不太清晰的结果,但对于较短的搜索查询或单词重叠度较低的文档很有用。
我在现实世界的应用程序中检查了这两种方法:我需要知道某个<collection of documents>
是否与一小部分<search terms>
相关。最初,我认为我需要使用语义相似性匹配——搜索词来自不受控制的用户输入,因此可能存在一些非常微妙的关系。文档排序是一个有用的功能,但总体目标是应用一个决策阈值,以便生成一个二进制的是/否结果。
对于那个特定的应用,我发现单独使用 TF-idf 就足够了。文档集合通常足够大,以至于在确实应该找到匹配的情况下,与搜索词有足够的重叠。以色列国防军工作队对正面和负面案例进行了更明确的区分,尽管它确实遗漏了一些也是相关的文件。
值得采取数据驱动的方法,看看什么最适合你的应用程序。
你有什么想法?你在工作中使用过这些工具吗,或者你喜欢不同的方法?在下面留下你的评论吧!
鲁珀特·托马斯 是一名技术顾问,专门研究机器学习、机器视觉和数据驱动产品。@ Rupert Thomas
参考
如何高效地从 TensorFlow 2.0 中读取 BigQuery 数据
使用 tensor flow _ io . big query . bigqueryclient 创建 tf.data.dataset
TensorFlow 的 BigQueryClient 使用存储 API 直接从 BigQuery 存储中高效地读取数据(即,无需发出 BigQuery 查询)。在本文中,我将向您展示如何在 Keras/TensorFlow 2.0 模型中使用该类来创建 tf.data 数据集。你可以在 GitHub 上跟随这个笔记本。
使用 BigQuery 作为数据湖:将数据直接从 BigQuery 读入 TensorFlow
作为示例,我将使用信用卡交易的数据集。这些交易中约有 0.17%是欺诈性的,挑战在于在这个非常非常不平衡的数据集上训练一个分类模型。因此,在这个过程中,您还将学习一些如何处理不平衡数据的技巧。
基准
开发机器学习模型的最佳实践是拥有一个简单的基准。在这种情况下,我将使用 BigQuery ML 开发基准模型:
CREATE OR REPLACE MODEL advdata.ulb_fraud_detection
TRANSFORM(
* EXCEPT(Amount),
SAFE.LOG(Amount) AS log_amount
)
OPTIONS(
INPUT_LABEL_COLS=['class'],
AUTO_CLASS_WEIGHTS = TRUE,
DATA_SPLIT_METHOD='seq',
DATA_SPLIT_COL='Time',
MODEL_TYPE='logistic_reg'
) ASSELECT
*
FROM `bigquery-public-data.ml_datasets.ulb_fraud_detection`
请注意,我在这个超级简单的模型中做了几件事情:
- 我正在训练一个逻辑回归模型(或线性分类器)——这是这个问题最简单的可能 ML 模型,可以作为对更复杂模型的检查。
- 我使用时间列划分数据,因此前 80%的事务是训练数据集,后 20%是评估数据集。这样,我们就不会在欺诈活动存在时间依赖性的情况下泄露信息。
- 我要求 BigQuery ML 根据它们在训练数据集中的出现频率自动对这些类进行加权。
- 我正在使用 log 函数转换范围很大的 Amount 列,这样它也是一个相对较小的数字。我使用 BigQuery ML 的 TRANSFORM 子句来实现这一点。
- 因为在某些情况下金额为零,所以我用 SAFE。记录以避免数字错误。
这给了我一个 AUC(曲线下面积)为 0.9863 的模型,说明了 BigQuery ML 有多么强大。让我们看看我们是否可以用 Keras 编写的更复杂的机器学习模型来击败这一点。
创建 tf.data.dataset
为了将 BigQuery 数据读入 Keras,我将使用 BigQueryClient 。下面是创建数据集的代码:
import tensorflow as tf
from tensorflow.python.framework import dtypes
from tensorflow_io.bigquery import BigQueryClient
from tensorflow_io.bigquery import BigQueryReadSessiondef features_and_labels(features):
label = features.pop('Class') # this is what we will train for
return features, labeldef read_dataset(client, row_restriction, batch_size=2048):
GCP_PROJECT_ID='your_project_name' # CHANGE
COL_NAMES = ['Time', 'Amount', 'Class'] + ['V{}'.format(i) for i in range(1,29)]
COL_TYPES = [dtypes.float64, dtypes.float64, dtypes.int64] + [dtypes.float64 for i in range(1,29)]
DATASET_GCP_PROJECT_ID, DATASET_ID, TABLE_ID, = 'bigquery-public-data.ml_datasets.ulb_fraud_detection'.split('.')
bqsession = client.read_session(
"projects/" + GCP_PROJECT_ID,
DATASET_GCP_PROJECT_ID, TABLE_ID, DATASET_ID,
COL_NAMES, COL_TYPES,
requested_streams=2,
row_restriction=row_restriction)
dataset = bqsession.parallel_read_rows()
return dataset.prefetch(1).map(features_and_labels).shuffle(batch_size*10).batch(batch_size)client = BigQueryClient()
train_df = read_dataset(client, 'Time <= 144803', 2048)
eval_df = read_dataset(client, 'Time > 144803', 2048)
本质上,我们使用 client.read_session()创建一个会话,传入要读取的表、要读取的表的列,以及对我们关心的行的简单限制。这些数据被并行读入 tf.data.dataset,我用它来创建一个训练数据集和一个评估数据集。
请注意,上面的代码使用了一些 tf.data.dataset 最佳实践,如预取、混排和批处理。
创建 Keras 模型输入层
创建 Keras 模型来读取结构化数据涉及到特性列。要了解更多,请参阅我的文章,关于在 Keras 中创建宽深模型和保持 T2 转换代码与输入分离。因此,不重复我自己,下面是创建 Keras 的输入层的代码:
# create inputs, and pass them into appropriate types of feature columns (here, everything is numeric)
inputs = {
'V{}'.format(i) : tf.keras.layers.Input(name='V{}'.format(i), shape=(), dtype='float64') for i in range(1, 29)
}
inputs['Amount'] = tf.keras.layers.Input(name='Amount', shape=(), dtype='float64')
input_fc = [tf.feature_column.numeric_column(colname) for colname in inputs.keys()]# transformations. only the Amount is transformed
transformed = inputs.copy()
transformed['Amount'] = tf.keras.layers.Lambda(
lambda x: tf.math.log(tf.math.maximum(x, 0.01)), name='log_amount')(inputs['Amount'])
input_layer = tf.keras.layers.DenseFeatures(input_fc, name='inputs')(transformed)
处理阶级不平衡
处理 Keras 模型中的类不平衡包括两个步骤:
- 指定对数输出图层的初始偏差(正/负)
- 以总权重等于训练样本数量的方式,对不频繁类进行比频繁类大得多的加权
我们可以使用 BigQuery 计算必要的值:
WITH counts AS (
SELECT
APPROX_QUANTILES(Time, 5)[OFFSET(4)] AS train_cutoff
, COUNTIF(CLASS > 0) AS pos
, COUNTIF(CLASS = 0) AS neg
FROM `bigquery-public-data`.ml_datasets.ulb_fraud_detection
)SELECT
train_cutoff
, SAFE.LOG(SAFE_DIVIDE(pos,neg)) AS output_bias
, 0.5*SAFE_DIVIDE(pos + neg, pos) AS weight_pos
, 0.5*SAFE_DIVIDE(pos + neg, neg) AS weight_neg
FROM counts
这给了我以下数字:Keras 模型的输出偏差需要设置为-6.36,类权重需要为 289.4 和 0.5。
创建 Keras 模型
然后,我们可以创建一个 Keras 模型,其中包含两个隐藏的全连接层和一个丢弃层(以限制过拟合),并注意为输出层提供初始偏差,为损失函数提供类权重:
# Deep learning model
d1 = tf.keras.layers.Dense(16, activation='relu', name='d1')(input_layer)
d2 = tf.keras.layers.Dropout(0.25, name='d2')(d1)
d3 = tf.keras.layers.Dense(16, activation='relu', name='d3')(d2)
output = tf.keras.layers.Dense(1, activation='sigmoid', name='d4', bias_initializer=tf.keras.initializers.Constant())(d3)model = tf.keras.Model(inputs, output)
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=metrics)class_weight = {0: 0.5, 1: 289.4}
history = model.fit(train_df, validation_data=eval_df, epochs=20, class_weight=class_weight)
结果呢?经过 20 次迭代,我得到了:
val_accuracy: 0.9718 - val_precision: 0.0401 - val_recall: 0.8831 - val_roc_auc: 0.9865
这比标准的逻辑回归模型要好,但只是勉强好。我们将需要进一步超参数调整节点数、掉线等。深度学习模型比这做得更好。
将张量流模型加载到 BigQuery 中
可以将训练好的 TensorFlow 模型加载到 BigQuery 中,用它来做推理。要加载模型,请从 Keras 调用:
model.save('gs://{}/bqexample/export'.format(BUCKET))
然后,在 BigQuery 中,执行以下操作:
CREATE OR REPLACE MODEL advdata.keras_fraud_detection
OPTIONS(model_type='tensorflow',
model_path='gs://BUCKETNAME/bqexample/export/*')
您可以使用此模型进行预测,就好像它是一个原生的 BigQuery ML 逻辑回归模型一样:
SELECT d4, Class
FROM ML.PREDICT( MODEL advdata.keras_fraud_detection,
(SELECT * FROM `bigquery-public-data.ml_datasets.ulb_fraud_detection` WHERE Time = 85285.0)
)
上面将其称为 d4 的原因是我的 Keras 输出节点被称为 d4。
摘要
在本文中,您看到了如何:
- 直接从 BigQuery 读入 TensorFlow 2.0/Keras 模型
- 如何将训练好的模型加载到 BigQuery 中
在这个过程中,您还看到了如何在高度不平衡的数据上训练 BigQuery ML 模型和 Keras 模型。
后续步骤:
- 本文中的代码在 GitHub 上的一个笔记本里。在 AI 平台笔记本上试用一下。
- 关于 BigQuery 的更多信息,请阅读由 O’Reilly Media 出版的《BigQuery:权威指南》一书:
注意:根据贵公司的定价方案,使用超过一定限制的 存储 API 可能会产生 BigQuery 费用 。在我写这篇文章的时候,采用统一费率计划的客户如果每月的读取量超过 300 TB,就会开始产生这些费用。
如何使用 Pandas 将 CSV 文件读入 Python
在本帖中,我们将讨论如何将 CSV 文件导入 Python。
由 Unsplash 上的absolute vision拍摄的照片
简短回答
最简单的方法是:
import pandas as pddf = pd.read_csv ('file_name.csv')
print(df)
如果您想要导入列的子集,只需添加usecols=['column_name']
;
pd.read_csv('file_name.csv', usecols= ['column_name1','column_name2'])
如果想用另一个分隔符,只需添加sep='\t'
;默认分隔符是','
。
pd.read_csv('file_name.csv', sep='\t')
概述熊猫数据框架
Pandas DataFrames 是一个类似 excel 的数据结构,带有标记轴(行和列)。下面是一个熊猫数据帧的例子,我们将把它作为下面的例子:
生成数据帧的代码:
将 CSV 文件导入数据帧
Pandas read_csv()
函数将 CSV 文件导入到 DataFrame 格式。
以下是一些选项:
**文件路径或缓冲区:**这是文件名或文件路径
df.read_csv('file_name.csv’) # relative position
df.read_csv('C:/Users/abc/Desktop/file_name.csv')
header :这允许您指定哪一行将被用作数据框的列名。应为 int 值或 int 值列表。
默认值是header=0
,这意味着 CSV 文件的第一行将被视为列名。
如果您的文件没有标题,只需设置header=None
。
df.read_csv('file_name.csv’, header=None) # no header
没有标题的输出:
sep :为 CSV 输入指定一个自定义分隔符,默认为逗号。
pd.read_csv('file_name.csv',sep='\t') # Use Tab to separate
index_col: 这允许您设置哪些列将用作数据帧的索引。默认值是 None,pandas 将从 0 开始添加一个新列来指定索引列。
它可以设置为列名或列索引,将用作索引列。
pd.read_csv('file_name.csv',index_col='Name') # Use 'Name' column as index
nrows: 仅从文件中读取第一行的数量。需要一个 int 值。
使用列:指定将哪些列导入数据框架。它可以是 int 值或列名的列表。
pd.read_csv('file_name.csv',usecols=[1,2,3]) # Only reads col1, col2, col3\. col0 will be ignored.
pd.read_csv('file_name.csv',usecols=['Name']) # Only reads 'Name' column. Other columns will be ignored.
转换器:通过定义的函数帮助转换列中的值。
na_values :缺省值为 NaN。如果您希望其他字符串被视为 NaN,请使用此选项。预期的输入是字符串列表。
pd.read_csv('file_name.csv',na_values=['a','b']) # a and b values will be treated as NaN after importing into dataframe.
如何和熊猫“读 _csv”
使用 read_csv 作为通用工具
数据科学项目中最耗时的部分是数据清理和准备。Pandas 是一个非常强大和通用的 Python 数据分析库,可以加速项目的预处理步骤。我们通常倾向于使用带有默认参数的函数,这使得我们无法充分利用函数的优势。
米卡·鲍梅斯特在 Unsplash 上的照片
熊猫最广泛使用的功能之一是 read_csv ,它读取逗号分隔值(csv)文件并创建一个数据帧。在这篇文章中,我将重点介绍 read_csv 函数的许多不同参数以及如何有效地使用它们。
Pandas 的基本数据结构是 DataFrame ,它以表格的形式表示数据,带有标记的行和列。
一如既往,我们从进口熊猫开始。
import numpy as np
import pandas as pd
我创建了一个示例数据框架,旨在显示参数的效果和有用性,因此这些值可能没有意义。
让我们从使用不带可选参数的 read_csv 开始:
df = pd.read_csv("SampleDataset.csv")
df.head()
唯一需要的参数是文件路径。我们需要告诉熊猫文件在哪里。如果 csv 文件在同一个工作目录或文件夹中,您可以只写文件名。如果没有,我们可以如下指定位置:
df = pd.read_csv(r"C:\Users\soner\Downloads\SampleDataset.csv")
index_col
默认情况下,从 0 开始的整数索引被分配给数据帧。我们可以显式定义一个列作为索引。例如,我们可能希望使用“ID”列作为索引:
df = pd.read_csv("SampleDataset.csv", index_col='ID')
df.head()
数据类型
有浮点 ID 号有点奇怪。我觉得没有哪家公司是这样分配身份证号的。我们可以使用 dtype 参数指定 read_csv 函数中任何列的数据类型:
df = pd.read_csv("SampleDataset.csv", index_col='ID',
dtype={'ID':np.int32})df.head()
用途
在某些情况下,根据我们对日期的计划,我们可能不需要所有的特性(列)。我们可以在读取所有数据后删除不必要的列。然而,更好的选择是只读取我们需要的列,这可以通过 usecols 参数轻松完成:
cols = ["ID","Deparment","Salary","StartDate","Location"]
df = pd.read_csv("SampleDataset.csv", usecols=cols)df.head()
我们也可以使用列的索引作为参数来使用 cols 参数。以下代码可以很好地完成这项工作:
df = pd.read_csv("SampleDataset.csv", usecols=[2,3,4,5,6])
解析日期
让我们检查列的数据类型:
df = pd.read_csv("SampleDataset.csv")df.dtypes
Unnamed: 0 int64
RowNumber int64
ID float64
Deparment object
Salary int64
StartDate object
Location object
dtype: object
StartDate 列的数据类型是 object,但是我们知道该列包含日期,所以我们可以通过使用 parse_Dates 参数将值读取为 date。
df = pd.read_csv("SampleDataset.csv", parse_dates=[5])df.dtypes
Unnamed: 0 int64
RowNumber int64
ID float64
Deparment object
Salary int64
StartDate datetime64[ns]
Location object
dtype: object
日期现在有了更合适的格式。
na_values
缺失值有时不是熊猫可以检测为“缺失”的格式。例如,在位置列中,'?'是一个缺失值,但是 read_csv 函数无法知道这一点,除非我们指定它。我们可以使用 na_values 来指示要被识别为“缺失”的附加值:
df = pd.read_csv("SampleDataset.csv", na_values=['?'])df.head()
船头和船尾
如果我们有一个非常大的数据帧,并且只想读取其中的一部分,我们可以使用 nrows 参数,并指出我们想读取多少行并放入数据帧:
df = pd.read_csv("SampleDataset.csv")
df.shape
(30,7)df = pd.read_csv("SampleDataset.csv", nrows=10)
df.shape
(10,7)
在某些情况下,我们可能希望跳过文件开头的一些行。我们可以把要跳过的行数传递给skip prowsparameter,或者传递一个带有整数的列表,指示要跳过的行:
- skiprows=5:跳过前 5 行
- skiprows=[1,3]:跳过第一行和第三行
原始数据帧:
df = pd.read_csv("SampleDataset.csv", skiprows=3)
df.head()
跳过前三行,数据帧从第四行开始。然而,这里有一个问题。第 4 行用作列名。
表头
我们可以使用头参数来解决这个问题。在大多数情况下,csv 文件中的第一行包括列名,并推断为标题。因此,如果没有指定列名,csv 文件的默认行为是采用header=0
,并且从文件的第一行推断列名。
如果header=None
,列名被指定为整数索引,文件的第一行被读取为数据帧的第一行:
df = pd.read_csv("SampleDataset.csv", header=None)
df.head()
所以我们可以设置header=None
并使用 skiprows,但是记住第一行包括列名。如果skiprows=3
,数据框的第一行变成有ID=127
的人。
df = pd.read_csv("SampleDataset.csv", header=None, skiprows=3)
df.head()
姓名
如果我们想改变列名或者文件中没有一行包含列名,我们可以使用 names 参数:
cols=["Drop1","Drop2","Employee_ID","Department","Salary", "StartDate"]df = pd.read_csv("SampleDataset.csv", names=cols, skiprows=1)
df.head()
read_csv 函数还有很多附加参数。我试着解释我常用的参数,但是你可以在这里查看所有的参数列表。
感谢您的阅读。如果您有任何反馈,请告诉我。
如何从亚马逊 SageMaker 上读取 S3 的数据文件
将您的数据科学工作流保存在云中
亚马逊 SageMaker 是由亚马逊网络服务公司(AWS)提供的强大的云托管 Jupyter 笔记本服务。它用于创建、训练和部署机器学习模型,但它也非常适合进行探索性数据分析和原型制作。
虽然它可能不像一些替代品那样对初学者友好,如 Google CoLab 或 Kaggle Kernels ,但有一些很好的理由让你想在 Amazon SageMaker 内从事数据科学工作。
我们来讨论几个。
托管在 S3 的私有数据
机器学习模型必须在数据上训练。如果您正在处理私有数据,那么在访问这些数据进行模型训练时必须特别小心。将整个数据集下载到您的笔记本电脑上可能违反您公司的政策,或者可能是轻率的。想象一下,您的笔记本电脑丢失或被盗,而您知道其中包含敏感数据。顺便提一下,这是你应该总是使用磁盘加密的另一个原因。
云中托管的数据也可能太大,不适合放在你个人电脑的磁盘上,所以更简单的做法是将数据托管在云中,直接访问。
计算资源
在云中工作意味着您可以访问强大的计算实例。AWS 或您首选的云服务提供商通常会允许您选择和配置您的计算实例。也许你需要高 CPU 或高内存——比你的个人电脑所能提供的更多。或者您可能需要在 GPU 上训练您的模型。云提供商提供了许多不同类型的实例。
模型部署
如何直接从 SageMaker 部署 ML 模型是另一篇文章的主题,但是 AWS 为您提供了这个选项。您不需要构建复杂的部署架构。SageMaker 将剥离一个托管计算实例,该实例在一个用于执行推理任务的 API 后面托管一个经过训练的 ML 模型的 Dockerized 版本。
由考特尼·摩尔在 Unsplash 上拍摄的照片
将数据加载到 SageMaker 笔记本中
现在让我们进入这篇文章的主题。我将向您展示如何使用 Python 加载保存为文件的数据到 S3 桶中。示例数据是我想加载到我的 SageMaker 笔记本中的 Python 字典。
加载其他数据类型(如 CSV 或 JSON)的过程类似,但可能需要额外的库。
第一步:知道你把文件放在哪里
你需要知道 S3 桶的名字。文件在 S3 桶中被表示为“键”,但是从语义上来说,我发现仅仅从文件和文件夹的角度来考虑更容易。
让我们定义文件的位置:
bucket = 'my-bucket'
subfolder = ''
步骤 2:获得从 S3 桶中读取的权限
SageMaker 和 S3 是 AWS 提供的独立服务,一个服务要对另一个服务执行操作,需要设置适当的权限。谢天谢地,预计 SageMaker 用户将从 S3 读取文件,所以标准权限是没问题的。
尽管如此,您仍然需要导入必要的执行角色,这并不难。
from sagemaker import get_execution_role
role = get_execution_role()
步骤 3:使用 boto3 创建一个连接
Python 库旨在帮助用户以编程方式在 AWS 上执行操作。它将有助于连接 S3 桶的 SageMaker 笔记本。
下面的代码列出了 S3 存储桶上特定子文件夹中包含的所有文件。这对于检查存在什么文件很有用。
如果要遍历许多文件,可以修改这段代码,用 Python 创建一个列表对象。
第 4 步:直接从 S3 存储桶加载经过酸洗的数据
Python 中的pickle
库对于将 Python 数据结构保存到文件中很有用,以便以后可以加载它们。
在下面的例子中,我想加载一个 Python 字典,并将其赋给data
变量。
这需要使用boto3
来获得我想要加载的 S3 上的特定文件对象(pickle)。注意例子中的boto3
客户端如何返回包含数据流的响应。我们必须用pickle
库将数据流读入data
对象。
与使用pickle
加载本地文件相比,这种行为有点不同。
因为这是我总是忘记如何做好的事情,所以我将这些步骤编入了本教程,以便其他人可以受益。
替代方法:下载文件
有时,您可能希望以编程方式从 S3 下载文件。也许您想将文件下载到本地机器或连接到 SageMaker 实例的存储器中。
为此,代码略有不同:
结论
在这篇文章中,我主要关注 Amazon SageMaker,但是如果你在本地机器上正确安装了boto3
SDK,你也可以从 S3 那里读取或下载文件。由于我自己的大部分数据科学工作都是通过 SageMaker 完成的,您需要记住设置正确的访问权限,所以我想为其他人(以及我未来的自己)提供一个资源。
显然 SageMaker 不是镇上唯一的游戏。如今有各种不同的云托管数据科学笔记本环境可供选择,与五年前(2015 年)我完成博士学位时相比,这是一个巨大的飞跃
我没有提到的一个考虑是成本:SageMaker 不是免费的,而是按使用量计费的。完成后,记得关闭笔记本实例。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。一个月 5 美元,这样你就可以接触到我所有的作品以及成千上万其他作家的作品。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
[## 通过我的推荐链接加入 Medium 米哈伊尔·克拉森
阅读米哈伊尔·克拉森(以及媒体上成千上万的其他作家)的每一个故事。您的会员费直接支持…
mikhailklassen.medium.com](https://mikhailklassen.medium.com/membership)
如何使用 AWS S3/Lambda 在 bubble.io 中读取 Excel/CSV 文件
入门
代码少的数据 ETL 管道;)
嗨大家好,今天我将在 bubble.io 中演示一个非常简单的方法来实现从 Excel/csv 文件到 SQL 数据库的数据提取。
此工作流使用 AWS S3/Lambda 函数作为连接桥梁,与手动输入相比,它既节省时间,如果您使用 AWS 自由层服务,也节省成本。
唯一的成本是在 bubble 中安装一个名为 AWS 文件上传器的插件(35 美元),仅此而已。很神奇,对吧?
现在让我们深入研究一下,看看它是如何工作的。
第一步:确认您输入的数据,并据此建立数据库
这其实是一个蛋和鸡的问题。通常,当您发现 Excel/Access 不再能满足您快速管理和检索数据的目的时,就会发生这种情况,这时您就开始构建关系型(和/或)非关系型数据库。
迪伦·吉利斯在 Unsplash 上的照片
例如,作为一名负责全球或地区市场的高级项目经理,使用 Excel 作为分发和提取数据的标准模板将非常有用,特别是当您的项目经理遍布世界各地,并且您希望集中项目信息并从整体角度监控进度时。
第二步:创建你的泡泡页面来反映项目信息
测试用项目数据库
假设我想更新这个项目信息。
演示项目信息的气泡页
我知道这个页面看起来很丑,但它只是所列信息的一个非常基本的演示。您可以在此基础上开发许多变体。
这里有一些额外的注释:
- 允许用户下载 excel 模板,确保所有用户将输入统一的数据,并易于更新;
- 您可以为文件添加前缀,这样我们就不会被各种不同的文件名所困扰,也可以从文件名中提取关键信息作为关键标识符;
- 我建议选择 uuid_filename 作为文件结构,因为这样更容易看到每个上传的文件。
文件上传功能调整
第三步:部署 bubble 插件——AWS 工作流程
AWS 文件上传插件👉S3 水桶👉希腊字母的第 11 个👉SQL 数据库
S3 桶和 Lambda 之间的微妙联系是通过触发器实现的,所以一旦任何东西上传到 S3 桶,就会有一个事件生成,你的代码将从这里开始。
新 Excel 文件信息
已将文件上传到 uuid 为的 S3 桶中
在我的方法中,我使用两层来提取数据。
第一层,“文件上传”S3 存储桶触发“提取功能”Lambda 功能,该功能在“csv 接收器”S3 存储桶中输出新的 CSV 文件。
为加载生成的 CSV
λ接口
第二层,“csv 接收器”S3 存储桶触发“CSV 到 RDS”Lambda 函数,该函数从 CSV 中提取数据并将其输入到 SQL 数据库中。
这背后的原因:
- 这个中间步骤允许用户检查是否有不规则的数据;
- 如果它是非常大的 excel 文件,它转移了 1 lambda 函数的计算压力;
- 如果需要,你可以在第一个 lambda 函数中写一些简单的代码来创建一个文件上传日志,跟踪所有用户的上传历史;
成功上传文件并刷新页面后,项目信息就成功更新了!
更新的项目信息页面
实际上,AWS 生态系统中有许多有趣的部分,围绕 Lambda 也有许多有用的工具,但由于文章的长度,我将只提到几个:
- 如果你正在使用 pandas 或 pymysql 库,在 Lambda 中部署一个层;
- CloudWatch 非常非常有用,可以记录你所有的 lambda 活动;
- 如果你在 bubble 中使用 AWS 文件上传插件,请确保调整你的 S3 存储桶权限策略,否则它将无法工作。
参考:https://docs.zeroqode.com/plugins/aws-file-uploader-plugin
感谢大家阅读这篇文章,我将继续在 bubble.io 中分享有用的工具和技巧。
如何阅读科技论文
使用三重方法提高效率
放在我自己桌子上的高亮纸
本文的目标
这篇文章应该作为如何阅读科学论文的粗略指南,因为这种技能在大学里很少教授,可能会导致巨大的挫折。大多数时候,人们认为学生们已经知道了一些阅读研究论文的方法,但是我不得不承认,一开始我一个也不知道。
当我不得不阅读我的第一篇论文时,我只是开始从头到尾阅读它们。像一本书。我看了每一个表格,数字和数学方程式,试图从中找出意义。我想了解这一切,不要错过任何一条信息!恰好对此有一个合适的术语:对错过的恐惧(FOMO) 。但当我在几个小时的沮丧和背景阅读后来到结尾时,我意识到这篇论文并没有我一开始想象的那么有帮助。我已经忘记了大局或者从一开始就没有大局。我迷失在细节中。这不是一种非常有效的阅读论文的方式,尤其是当你在做一项文献调查或者一天之内必须阅读多篇论文的时候。
但是似乎有一个更好的方法来解决这个问题:三关方法。
本文的其余部分结构如下,并更详细地解释了每个主题:
- **三关做法(**TL; ) 博士
- 第一关:鸟瞰图
- 第二关:抓住内容
- 第三关:虚拟重新实现论文
- 做文献调查
- 可选扩展
- 小盒子
- 荧光笔
- 思维导图
- 番茄时段
- 费曼技术与橡皮鸭调试
- 帕金森定律和帕累托原理
三步法(TL;博士)
在 Srinivasan Keshav 的“如何阅读论文”[1]中,他描述了作为过滤系统的三遍方法。这是一种阅读论文的迭代和增量方式。这种演绎方法从总体概述到具体细节,而每一步都比前一步花费更多的时间,并在每次迭代中给你更深刻的见解。
- **第一关:**在这里你得到了论文的鸟瞰图或“大图”。这一步通常需要 5 到 10 分钟。你浏览论文的结构,忽略任何细节,比如数学方程,但是你应该完整地阅读摘要、标题、引言和结论。这一步作为第一步检查论文是否值得一读。通过遵循这种方法,你已经可以丢弃那些没有帮助的论文,例如文献综述。
- **第二遍:**这里你试着通过整体阅读来理解论文的内容。这一步可能需要 1 个小时。你仍然可以忽略像数学方程式这样的细节,但是试着在空白处做些笔记,写下要点。试着用你自己的话重新表述要点。
- **第三关:**在继续这一步之前,你必须非常确定这篇论文值得你花时间,因为作为初学者,它可能需要长达 5 个小时。更有经验的读者可能能在 1 小时内完成这一步。现在是时候阅读完整的论文及其所有的数学公式和细节了。尝试虚拟地重新实现论文或使用任何你喜欢的工具来重现结果。如果你是一个评论者,那么你可能需要采取这个步骤来给出详细的反馈。
我想指出的是,这篇文章不是关于论文的评论。然而,如果你正在寻找任何复习指南,那么看看这篇文章末尾的参考文献[2]、[3]、[4]。
以下部分更详细地描述了三步法的每个步骤。
第一关:鸟瞰
"第一遍是快速浏览,获得论文的鸟瞰图."斯里尼瓦桑·凯沙夫
第一遍的目标是了解论文的全貌,并且不应该超过 10 分钟。你不必深入细节,甚至不必通读整篇论文。
浏览一下这篇论文,看看它是如何组织的。查看小节和子小节,但忽略它们的内容。当你阅读章节和小节时,你的大脑已经为即将到来的内容做好了准备,你的大脑中可能已经出现了一些模糊的问题。这将使你更容易发现重要或有趣的段落,如果你决定继续下去。
尽管您只是浏览了一下结构,但您应该完整地阅读以下部分:
- 摘要
- 标题
- 简介
- 结论
这些部分会给你足够的信息,让你知道这篇论文是关于什么的,是否值得进一步阅读。在阅读这些章节的时候,你也可以看一下参考文献,看看有些东西你是否熟悉,或者有些东西你以前读过的其他论文中已经提到过。
在第一遍结束时,你应该能够回答 Keshav[1]所说的所谓的“5c ”:
- **类别:**类别描述了论文的类型。这篇论文是关于原型的吗?关于一种新的优化方法?是文献调查吗?
- **上下文:**上下文使这篇论文成为其他论文的视角。还有哪些论文与此有关?你能把它和别的东西联系起来吗?你也可以把上下文看作一棵语义树,在这里你可以给文章赋予特定的重要性。是重要的枝干还是无趣的叶子?也许你在这个领域没有任何先验知识,因此你仍然需要从头开始构建你的语义树。这在开始时可能会让人失去动力,但这是正常的。
- **正确性:**正确性,顾名思义,是一种有效性度量。假设成立吗?大多数情况下,第一遍不会给你足够的信息来肯定地回答这个问题,但你可能有一种预感,这在开始时就足够了。
- 大多数论文都在引言部分列出了他们的来稿。这些贡献有意义吗?它们有用吗?它们解决了哪些问题?这些投稿新颖吗?
- 根据您刚刚阅读的部分,您认为这篇论文写得好吗?你发现语法错误了吗?有错别字吗?
这一步应该作为快速的第一步过滤。当你读完第一遍时,你可以决定继续读第二遍,或者你决定不继续读,因为:
- 你缺乏背景信息
- 你对这个话题了解不够
- 这篇论文你不感兴趣或者对你没有好处
- 这篇论文写得很差
- 作者做了错误的假设
如果那篇论文不属于你的专业领域,但在以后可能会变得与你相关,那么这第一遍就足够了,你可能不必继续阅读。如果不是这样,那么你可以继续第二遍。
第二关:抓内容
"有时,即使在第二遍结束时,你也不会理解一篇论文."斯里尼瓦桑·凯沙夫
第二遍可以持续 1 个小时,在这里你应该阅读完整的论文。忽略诸如证明或方程式等细节,因为大多数时候你不需要那些特定的知识,而且这会耗费你宝贵的时间。在纸边上做些笔记,写下要点。用自己的话在空白处写下小的总结或关键点是一个很好的方法,可以看出你是否真正理解了你刚刚读过的内容;你会记得更久。
查看论文中任何类型的插图,如表格和数字,看看你是否能发现任何错误或差异。插图有意义吗?它们传达了什么样的信息?轴的标签是否正确?图表有适当的标题吗?像这样草率的工作已经是一篇整体写得很差的论文的一个强有力的指标。
你可以标记相关的未读参考文献,以便进一步阅读,这是了解更多背景知识的好方法。建立你的语义树,看看哪些论文是重要的分支,哪些是不重要的叶子。
在第二遍结束时,你可能仍然不理解你刚刚读到的内容。这可能是由许多原因造成的。也许这不是你的专业领域,或者你缺乏背景信息。不要感到气馁,因为这种事情经常发生;甚至对教授也是如此…有人告诉我。
请记住,研究小组经常花几个月甚至几年来进行他们的研究。现在,他们必须将他们的结果和知识压缩到一篇论文中,该论文可能必须满足某些要求才能被会议接受,例如,一定的页数。如果你这样想的话,当你在一个小时内没有理解所有事情的时候,你就不会那么沮丧了。
当你写下你不理解的东西时,有时会有帮助。然后你就有了一个很好的起点,可以通过阅读一些背景知识来填补知识空白。
您现在有不同的选择:
- 你可以停止继续读下去,因为出于几个原因,这篇论文对你没有好处
- 读完一些背景材料后,把纸放在一边继续读
- 继续第三遍
第三关:虚拟地重新实现论文
“这一关需要非常注意细节。你应该识别并质疑每一句话中的每一个假设。”斯里尼瓦桑·凯沙夫
如果你是初学者,这一关可能需要 4 到 5 个小时。这是大量的工作,你应该仔细考虑这一步是否值得你花时间。另一方面,如果你已经是一个有经验的读者,那么这一步可能只需要你 1 个小时。如果您是指定的审阅者,或者您已经确定您必须理解论文的所有细节,则这一步是强制性的。
通读这篇论文,质疑每一个细节。现在是时候进入本质的数学方程,并试图理解发生了什么。做与作者相同的假设,从零开始重新创作作品。你可以在头脑中重新执行这些步骤,或者使用任何你认为合适的工具。用一张纸画出不同步骤的流程图,或者使用伪代码。这真的取决于你。大部分时间我都在阅读与人工智能和计算机科学相关的论文,因此用原始 Python 或使用 Jupyter 笔记本重新实现东西是有意义的。这真的取决于你的领域。
在这一轮结束时,你应该是一个专家,知道论文的优点和缺点。你可以对遗漏的引用和潜在的问题进行陈述。你可以重建结构,用简单的语言向某人解释这篇论文的内容。
通过教别人来学习的概念被称为“费曼技巧”,是发现你理解中的任何差距的好方法。
他们称费曼为“伟大的解释者”
medium.com](https://medium.com/taking-note/learning-from-the-feynman-technique-5373014ad230)
做一个文献调查
做一个文献调查和阅读一篇论文有点不同,但是你仍然可以应用三重方法。
第一关
第一遍你必须收集可能有用的文件。你可以使用像谷歌学术这样的搜索引擎,输入关键词找到 3 到 5 篇最近的论文。我通常做的是创建一个简单的论文列表,按照论文的主题、出版年份和引用次数进行分类。引用次数通常是一篇论文重要的良好指标。仅仅在谷歌中输入你的关键词也能得到令人惊讶的好结果。
当你准备好你的初始论文小集合时,你可以继续对它们进行通常的第一遍,以获得大的图片。你也可以浏览参考文献,看看这些论文是否有共同的引用。常见的引用是包含在你的调查中的很好的候选。
第二遍
当你确定了常见的引用和重复的作者,你可以访问他们的网站,看看你是否能发现任何最近的工作。也下载常用的论文,并再次应用单篇论文的三关方法。
第三遍
在这里,你可以尝试访问顶级会议或期刊的网站,浏览最近的会议记录。尝试确定相关的高质量工作,并再次应用单个论文的三关方法。
可选扩展
请记住,这些可选步骤可能会给三重方法增加一些时间。如果你只是浏览文件,看看它们是否有帮助,它们可能对你没有好处。另一方面,如果你已经知道你必须完整地阅读和理解这篇论文,并且没有其他的方法,那么这些步骤也可以帮助你。这些是我的个人实践,我一直在努力改进它们。
小盒子
当你看下面的图片时,你可以看到我用盒子围住了数学方程式、数字和表格。我喜欢用明显分开的盒子来看论文,并把正文和其他部分分开。我通常在第一遍浏览时这样做。这有助于我量化在数学方程式方面的细节,我可以期待在以后,它只是看起来更愉快。
数学方程式周围的小盒子
荧光笔
荧光笔是一个很好的工具来标记你论文中的部分,并赋予它们独特的意义。你可以试着想出自己的高亮系统或者使用现有的系统。试着赋予每一种颜色与众不同的意义,并坚持下去。
一套漂亮的荧光笔
在第二遍时,我通常用黄色表示有趣或重要的句子。橙色代表引用,绿色代表定义或标语。然而,请随意使用您喜欢的任何系统。请记住,高亮显示并不能代替记笔记!在第二遍的时候,你可以在空白处做笔记,画一些小图表以便更好地理解,并结合使用荧光笔。
每种颜色都有独特的含义
论文末尾有趣或重要的参考文献获得和之前一样的颜色。
用橙色标记参考文献
思维导图
如果你更注重视觉,想更好地了解论文,思维导图可能是一个合适的选择。创建思维导图没有严格的规则,我只是从中间的论文标题开始。大箭头指向主要部分的标题,并用橙色突出显示。这些是大树枝。第一级子部分用绿色突出显示。其他任何东西都不会突出显示。随意想出自己的系统。
这一步通常会在第一遍的基础上增加 25 分钟,我仍然不确定这是否值得。另一方面,如果你继续第二遍,想写下一个重要的笔记,你可以直接把它放到思维导图的相应节点上。这可能有助于你更直观地了解全局。过了一段时间后,这也可能是一种更快的方法来刷新你对一篇论文的记忆。
番茄时段
如果你缺乏动力,番茄工作法是一个很好的工具。有时候,并不是你不知道如何阅读一篇论文,而是你被它吓倒,甚至缺乏开始阅读的动力。拖延症开始了,你错过了一个重要的回顾截止日期。
拿一个计时器,设置为 25 分钟。不要期待任何结果。设置为 25 分钟就可以开始了。排除一切干扰,遵循三步走的方法,直到 25 分钟结束。你可能无法完成整个三关方法,但在 25 分钟结束时,你可能会惊讶于你所取得的成就。你现在知道这篇论文是关于什么的了,你可能不那么害怕了。你可能会觉得你可以把计时器再调 25 分钟。
通过使用这种计时方法,你可以获得动力,并可以更容易地遵循三步法。好的一面是:你可以将番茄工作法应用于任何任务。
费曼技术与橡皮鸭调试
如前所述,费曼学习技巧是发现你理解中的差距的一个很好的工具。一般步骤是:
- 选择一个你想学习的概念,并把它的名字写在一张纸的顶端。
- 假装你正在向一个事先没有相关知识的人教授这个概念。尽量使用简单的语言,不要简单地背诵。用你自己的话!
- 回顾你的解释。尽管使用了简单的语言,Was 是准确的吗?找出你解释中的弱点,并写在纸上。回到你的学习材料,看看你是否能阐明这些观点。
- 如果你在解释中使用了大量的专业术语或复杂的语言,简化你的解释。
如果你想应用费曼技巧,但手头没有第二步的小兄弟,那么橡胶鸭可能适合你。
橡皮鸭调试背后的想法源于软件工程,最早出现在安德鲁·亨特和戴维·托马斯的《实用程序员》一书中。在这本书里,一个程序员带着一只橡皮鸭,一行一行地向这只鸭子解释代码,以找出任何错误。您也可以为此使用任何其他对象。你有一只猫吗?我敢肯定她一直想知道汉密尔顿蒙特卡罗抽样[6]是如何工作的。
向对你的话题一无所知的人解释
帕金森定律和帕累托原理
下面的两种方法不仅仅局限于阅读论文的任务,也适用于其他任何任务。如果你把这两者结合起来,你就有了一个限定时间的方法,例如,为整篇论文计划 10 次番茄大战,然后停止。你也可以试着给自己一个完全不切实际的时间框架来阅读一篇论文,然后检查你的进展。
帕金森定律 陈述如下:
“工作扩大以填补完成工作的时间”——西里尔·诺斯古德·帕金森
如果你计划花 10 个小时读一篇论文,做笔记,写总结等等,那么它可能会花掉你 10 个小时。
另一方面, 帕累托法则 (也称为 80/20 法则)指出:
“对许多事件来说,大约 80%的结果来自 20%的原因.”—维尔弗雷多·帕累托
这意味着你可能要花 20%的精力和时间去理解 80%的文章。这种 80/20 的比例不是固定的,而是一个粗略的估计。也可能是 70/30 左右。
你有没有在临近最后期限的时候,只剩下 30 分钟去做一件你认为要花几个小时的事情?然后你意识到你实际上做得相当好?帕金森定律迫使你进入 30 分钟的时间框架,帕累托原则确保你只做对最终结果贡献最大的任务。通过给自己设定不切实际的、紧迫的截止日期来模拟这种情况。
我希望你喜欢这篇文章,并且我能在你的学术旅程中帮助你。
你对我的文章或项目的更新感兴趣吗?我的简讯每月一次让你了解最新动态!
如果你想支持我,并且你还不是一个中等会员,那么请考虑通过我的推荐页注册:https://medium.com/@christoph.schmidl/membership
参考
[1]s .凯沙夫(斯里尼瓦桑)(2007 年)。如何阅读一篇论文? ACM SIGCOMM 计算机通信评论, 37 (3),83–84。(网址:https://dl.acm.org/doi/pdf/10.1145/1273445.1273458)
[2] Cormode,G. (2009 年)。如何不评论一篇论文:敌对评论者的工具和技术? ACM SIGMOD 记录, 37 (4),100–104。(网址:https://dl.acm.org/doi/pdf/10.1145/1519103.1519122)
[3]迈耶,A. (1992 年)。如何审阅一篇技术论文?能源与建筑, 19 (1),75–78。(网址:https://eta-intranet . LBL . gov/sites/default/files/how-to-review-a-technical-paper _ 0 . pdf)
[4]t .罗斯科(2007 年)。为系统会议写评论。(网址:https://www . cl . cam . AC . uk/teaching/1011/R01/review-writing . pdf)
[5]西里洛,F. (2018)。番茄工作法:改变生活的时间管理系统。兰登书屋。
[6]贝当古,M. (2017)。哈密尔顿蒙特卡罗的概念介绍。 arXiv 预印本 arXiv:1701.02434 。(网址:https://arxiv.org/pdf/1701.02434.pdf)
corona 疫情期间如何用 Python 实现地理可视化
基于 GeoPandas 和 Matplotlib 的德国 web 刮削日冕信息的地理演示
乔纳森·博尔巴在 Unsplash 上的照片
在这个故事中,我尝试分享我用 Python 包(Geopandas,pandas,Matplotlib)制作 Corona 信息地理可视化的经验。电晕信息是通过网刮提取的。由于 web 抓取并不是我们的目标,而是一种获取信息的方法,所以我不会花太多时间解释这部分代码🌚。
编者注:towardsdatascience.com是一家以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
社会背景:科罗纳·疫情
截至 2020 年 3 月 13 日,当新增病例数超过中国时,世界卫生组织(世卫组织)开始认为欧洲是新冠肺炎疫情的活跃中心。[1][2]
我的父母住在中国,他们目睹了疫情的严重性。在一月、二月和三月,他们不得不总是呆在家里😷。只有我的父亲一周两次去市场购买全家基本生活所需的必需品。后来在欧洲,许多城市也实行了封锁。我住在德国,那些日子太可怕了。在欧洲爆发之前,我呆在家里,做了一个在德国感染的电晕的地理可视化,以消磨时间😇。https://github.com/Kopfgeldjaeger/Covid-19_in_germany,于 2020 年 2 月 28 日提交。
存储库的简介图像
代码上传至今已经半年多了。现在在欧洲,第二波疫情来势凶猛。我将为抗击疫情贡献自己的力量,用可视化的日冕信息警告人们。
在疫情开始的时候,关于日冕的视觉信息很少。我和朋友每天打开网站了解德国 16 个州有多少感染者。后来,更多的 Dash 网站被建立来可视化相同的信息,甚至 YouTube 上的 Corona 实时计数器。
今天我打开了每天早些时候访问的 www.coronavirus.jetzt/karten/deutschland/网站。那里关于日冕的信息是以表格的形式呈现的。
网站截图
它非常清晰和简洁,然而,对于不熟悉德国地理的人来说,很难想象每个州的日冕分布情况。因此,我们需要添加这些地理信息,最好是在图像中与日冕信息一起展示。
抓取日冕信息的 HTML 表格
我们目标的第一步是收集所有 16 个州的感染和死亡人数。这部分作品与另一个媒介故事非常相似[3]。
下面的代码可以让我们得到信息,我把这些信息保存在一个 Numpy 数组中,这个数组很容易转换成 Pandas 数据帧。
import requestsimport lxml.html as lhimport pandas as pd
import numpy as npurl='https://www.coronavirus.jetzt/karten/deutschland/'page = requests.get(url)doc = lh.fromstring(page.content)tr_elements = doc.xpath('//tr')tr_elements = doc.xpath('//tr')col=[]#For each row of 16 states, store the name of state, the numbers of infected and deathsfor n in range(1,len(tr_elements)):row =[]for t in tr_elements[n]:name=t.text_content()row.append((name))col.append(row)col= np.array(col)df = pd.DataFrame({'Bundesland': col[:,0], 'Infizierte': col[:,1], 'Todesfälle':col[:,2]})df
数据帧(df)看起来像:
查找德国的 shapefile
最后一步完成后,我们寻找德国 16 个州的地理信息🌴🌋🌅。使用地理定位数据时,地图是交流和比较信息的一种很好的方式。如何用 Python 展示地图?答案是 GeoPandas[4],这是一个开源项目,旨在简化 python 中地理空间数据的处理。它结合了 pandas 和 shapely 的功能,在 pandas 中提供地理空间操作,并为 shapely 提供多种几何图形的高级接口。
为了生成德国地图,我们需要下载必要的形状文件。这个链接并不是唯一为我们提供德国形状文件的链接。不同类别的 shapefiles 有很多种。我在这里使用的是解压 zip 文件后的文件名“geomap/vg2500_bld.shp”。
import matplotlib.pyplot as pltimport geopandas as gpdfp = "geomap/vg2500_bld.shp"map_df = gpd.read_file(fp)map_df.head()map_df.plot()
map_df 展示了德国各州的地图。
map_df.plot()的结果
我们可以用 plt.annotate 添加州名,代码如下
fig, ax = plt.subplots(1, figsize=(10, 6))map_df['coords'] = map_df['geometry'].apply(lambda x: x.representative_point().coords[:])map_df['coords'] = [coords[0] for coords in map_df['coords']]map_df.plot(ax=ax)for idx, row in map_df.iterrows():plt.annotate(s=row['GEN'], xy=row['coords'],horizontalalignment='center')plt.xticks(np.array([]))plt.yticks(np.array([]))
带注释的德国各州地图
最后一个插图有一个小问题,柏林和勃兰登堡的注释相互重叠。我们可以对 if 句使用一个技巧:
fig, ax = plt.subplots(1, figsize=(10, 6))map_df.plot(ax=ax)for idx, row in map_df.iterrows():if idx ==10: # the idx of Brandenburg is 10plt.annotate(s=row['GEN'], xy=row['coords'],verticalalignment='top',horizontalalignment='right')continueplt.annotate(s=row['GEN'], xy=row['coords'],verticalalignment='bottom',\horizontalalignment='center')plt.xticks(np.array([]))plt.yticks(np.array([]))
如果注释是针对勃兰登堡或柏林的,我们可以设置它们注释的属性,以使它们不再重叠。
带有清晰注释的德国各州地图
由于柏林在地理上被勃兰登堡包围,两个州的注释很容易重叠,所以我们后来又使用了这个技巧。
信息融合
我们既有日冕信息,也有德国各州的地理信息。然而,这两条信息并不相关。我们需要将它们合并成一个表格或数据帧。和熊猫一起🐼连接方法[5],我们可以将它们合并成一个数据帧。
merged = map_df.set_index('GEN').join(df.set_index('Bundesland'))merged
结合日冕信息和地理信息的合并数据帧
用 Matplotlib 从数据帧到插图
令人兴奋的是,我们已经到了用 Matplotlib 绘制结果的最后一步。
Matplotlib 是 Python 编程语言及其数字数学扩展 NumPy 的绘图库。它提供了一个面向对象的 API,使用 Tkinter、wxPython、Qt 或 GTK+等通用 GUI 工具包将绘图嵌入到应用程序中。还有一个基于状态机(如 OpenGL)的过程化“pylab”接口,设计得与 MATLAB 非常相似,尽管不鼓励使用它。[6]“维基百科”
在我的最后一个故事中,也是关于如何利用 Matplotlib 对研究结果进行可视化[7]。
在这里,我们继续利用 Matpotlib 实现地理可视化,因为数据帧包含所需的地理信息。
column = df['Infizierte']max_Infizierte = column.max()column = df['Todesfälle']max_Todesfälle = column.max()vmin, vmax = 0, max_Infiziertemerged['coords'] = merged['geometry'].apply(lambda x: x.representative_point().coords[:])merged['coords'] = [coords[0] for coords in merged['coords']]# create figure and axes for Matplotlibfig, ax = plt.subplots(1, figsize=(10, 6))merged.plot(column='Infizierte', cmap='YlOrRd', linewidth=0.8, ax=ax, edgecolor='0.8')for idx, row in merged.iterrows():if idx =='Berlin':plt.annotate(s=row['Infizierte'], xy=row['coords'],horizontalalignment='right',fontsize=8)continueplt.annotate(s=row['Infizierte'], xy=row['coords'],horizontalalignment='center',fontsize=8)# remove the axisax.axis('off')# add a titleax.set_title('Coronavirus infected in Germany (18.09.2020)', \fontdict={'fontsize': '18','fontweight' : '3'})ax.annotate('Source: [https://www.coronavirus.jetzt/karten/deutschland/',](https://www.coronavirus.jetzt/karten/deutschland/',)xy=(0.2, .06), xycoords='figure fraction',horizontalalignment='left', verticalalignment='top',fontsize=10, color='#555555')sm = plt.cm.ScalarMappable(cmap='YlOrRd', norm=plt.Normalize(vmin=vmin, vmax=vmax))sm._A = []cbar = fig.colorbar(sm)fig.savefig('testmap_1.png', dpi=300)
使用上面的代码,我们得到如下的图
截至 2020 年 9 月 18 日,德国最新的冠状病毒感染病例数
同样,我们也可以绘制德国直到今天 2020 年 9 月 18 日的死亡人数。为了使这个图与上一个不同,我从 Matplotlib [8]中选择了另一个调色板。
截至 2020 年 9 月 18 日,德国最新的电晕死亡病例数
正如上图“带有清晰注释的德国各州地图”所示,我们使用相同的技巧为柏林和勃兰登堡的数字设置了不同的注释格式,这样两个注释几乎不会重叠。
结论
在这个故事中,我介绍了一种制作地理可视化的方法,通过解释我的工作流程,使德国地图中显示的日冕情况直观且易于访问,从 web 抓取开始,到日冕地图的插图结束。所有相关文件上传到 GitHub:https://GitHub . com/Kopfgeldjaeger/Medium _ blogs _ code/tree/master/1 _ corona _ visualization。
最后但同样重要的是,我希望疫情将很快结束。我们可以一起在未来取得进步🚀,不管你现在住在哪里,也不管外面的日冕情况有多糟糕。
参考
[1]弗雷德里克斯 2020 年 3 月 13 日)。“世卫组织称欧洲是疫情冠状病毒的新中心”。纽约邮报纽约邮报T5。2020 年 5 月 9 日检索。
[2] “世卫组织宣布南美为新新冠肺炎震中”。巴西报道。2020 年 5 月 22 日。检索于 2020 年 6 月 1 日。
[3]https://towards data science . com/we b-scraping-html-tables-with-python-C9 Baba 21059
https://pandas.pydata.org/docs/
[7]https://towards data science . com/how-to-draw-a-bar-graph-for-your-science-paper-a 6 C2 a5 E6 BCD 7
[8]https://matplotlib . org/3 . 1 . 1/tutorials/colors/colormaps . html
如何识别和纠正谷歌分析报告中的(未设置)值
来源:沉积照片
了解更多关于 Google Analytics 报告中(未设置)值出现的信息,为什么它会出现在那里,以及如何减少该值的百分比。
你有没有注意到谷歌分析中的(未设置)值而不是实际值?这意味着 Google Analytics 没有关于这个值的数据,或者无法解读它。很明显,这个值对你这个营销专员来说是个美中不足的地方。你有数字,但不清楚它们是什么意思,以及如何在你的数据分析中使用它们。
此外,在一些标准的谷歌分析报告中获得一个(未设置的)值是一个警告信号,表明在你的网站上如何实现分析存在一些错误。在第一次设置分析和添加新功能时,观察这个值尤其重要。
观众报告
地理和移动报告组
Google Analytics 借助 cookies、移动设备的广告标识符等自动获取这些报告的数据。
您可以在地理语言报告中找到(未设置的)值。例如,在这张截屏显示的报告中,Google Analytics 无法为 1.24%的网站访问者定义语言:
图片由作者提供
地理位置报告几乎相同:
图片由作者提供
移动设备报告显示,3%的访问者的设备无法识别:
图片由作者提供
一般来说,你至少可以找到 1%访问者的(未设置)值。显示该值有各种原因,遗憾的是,我们无法影响这些原因:
- 删除的 cookies
- 用户浏览器中禁用了 JavaScript
- 无法检测到基于 IP 的位置或根本无法检测到 IP
- 各种拦截器(广告拦截器等。)
技术报告小组
这里有一个更有趣的报告:技术——浏览器和操作系统。每次访问者进入网站,他们的浏览器都会发送一个带有用户代理的字符串。该字符串包含有关发送请求的平台的信息,包括浏览器上的数据。GA 接收该信息,但有时它可能不可用。结果,我们在报告中得到(未设置的)值:
图片由作者提供
如果报表中具有(未设置)值的独立访问者的数量少于 5%,这被认为是正常的。如果更高,你应该检查你的流量——可能是由垃圾邮件引起的。在测量协议的帮助下,他们可以向 GA 发送任何虚假点击。要做到这一点,让他们知道你的谷歌分析资源 ID 就足够了。
有两种垃圾邮件。第一个访问你的网站,并产生虚假的流量。这些机器人可以发送带有虚假推荐人标题的 HTTP 请求来掩盖自己。为此,他们甚至可以在 URL 中使用另一个流行网站的名称。由于它们执行 JavaScript 代码的能力,它们不会被 GA 过滤为机器人。
第二种垃圾邮件发送虚假点击,而不访问您的网站。这种垃圾邮件被称为幽灵垃圾邮件。
在我们的例子中,高达 14.95%的访问者的浏览器是未定义的。事务的平均会话持续时间接近两分钟。为了调查这种可疑情况,我们需要更深入地打开采集-所有流量-转介:
图片由作者提供
注意跳出率接近 0%或 100%且会话数超过 10 的推荐源。正如我们所看到的,这发生在字符串 5 到 9 中。最有可能的是,这些是机器人。垃圾邮件的第二个迹象是奇怪的来源名称:在我们的例子中,有我爱你和我想约你。
另外,注意字符串 8 中源名称为 spamcampaign22 的访问者。他们进行了 264 次交易——每位访问者一次。如果我们没有手动将这些数据放入属性中,我们对垃圾邮件流量的猜测将被证明是正确的。
让我们添加主机名作为第二个维度:
图片由作者提供
在这种情况下,我们的网站叫做 example。com。确切地说,该主机需要出现在报告的所有行中。但是所有可疑的推荐源都有值(未设置),正如我们所看到的。我们终于找到了幽灵垃圾邮件!事实上,脸书和 Instagram 的流量也有一部分是垃圾邮件。这意味着这些机器人向 GA 发送虚假的点击,并试图使它们尽可能真实。
如果所有的机器人都发送一个主机值,就像报告中的字符串 9(hostnameghost),我们永远不会知道我们有过多的访客和推荐数据。
在这些报告中忽略(未设置)会导致销售数据损坏,向物业发送虚假交易。因此,您会看到不在您的规格中的事件和您从未销售过的产品视图。
如果您已经设置了从 Google Analytics 到 Google BigQuery 的数据导出(无论是 standard 还是在 OWOX BI Pipeline 的帮助下),这些虚假数据将增加数据库表的权重。因此,数据处理将会变慢,你将会在数据存储和处理上花费更多。
如何摆脱垃圾邮件流量
首先,您必须排除 bot 流量。打开查看设置,在僵尸过滤设置中勾选排除已知僵尸和蜘蛛的所有点击。这将排除可见的机器人。
图片由作者提供
您也可以将过滤器应用于推荐来源。为此,打开视图设置,转到滤镜子菜单,然后点击添加滤镜。现在将过滤器的类型设置为自定义,选中排除,并选择推荐过滤器字段。在过滤器模板字符串中,输入您要从报告中排除其通信的可疑来源的名称。
图片由作者提供
这种方法的局限性在于,我们无法利用真实网站的网址来排除来自僵尸程序的流量。否则,来自这些网站的合法流量也会被过滤掉。在这种情况下,我们可以尝试基于 IP 地址而不是来源进行阻止。如果你已经在 OWOX BI Pipeline 中建立了一个流,那么获取僵尸工具的 IP 地址就不是什么大事了。但是如果机器人不访问网站,IP 屏蔽就没有效果。
要消除幽灵垃圾邮件,请尝试使用主机名过滤器。设置过滤器时,选择包含,然后在过滤器模式字段中输入您网站的域名。
图片由作者提供
流量来源报告
流量来源报告帮助您评估广告来源和渠道的效率。在这些报告的帮助下,您可以比较您发起的每个活动的费用和收入,分析流量质量,并最终增加销售额。如果您看到这些报告中的值(未设置),您将无法确定在哪些方面投入更多,哪些活动需要更改,以及哪些活动应该简单地结束。
如果您没有值得信赖的数据,您就无法正确设置重新定位或为某个活动选择受众群体。显然,不可信的数据也不能应用于队列分析、收入预测、客户终身价值(CLV) 等增强型报告。
所有交通报告
让我们看一些所有交通报告的例子。让我们假设你想知道哪个频道给你最新的访问者。为了找到答案,让我们打开“所有流量-通道”报告,选择通道作为主要参数。我们可以在字符串 4 中观察到 7.19%的新访问者的值(未设置)。但是在这个字符串中隐藏了哪个或哪些特定的频道还不清楚。
图片由作者提供
在“所有流量—源/媒体”报告中,您可以看到类似的情况:
图片由作者提供
为什么此报告显示(未设置的)值?因为部分渠道和来源是由 Google Analytics 自动识别的:有机搜索、其他网站的推荐、直接流量。但是对于一个特殊的活动或广告服务,你需要 UTM 标签。如果 UTM 标签包含错误,就会出现问题。
UTM 标签是包含在来自广告消息的 URL 中的代码片段,并且引导到目标网站。这里有一个例子:
site.com/?UTM _ source = Google & UTM _ medium = CPC & UTM _ campaign = test
有三个必需的 UTM 参数:utm_source、utm_medium 和 utm_campaign。还有两个可选参数:utm_term 和 utm_content。这些参数的值可以是 google、cpc 或 test。
UTM 代码中导致(未设置)报告的典型错误包括:
- 拼错的参数。
- 参数和它的值之间没有等号。
- 没有用&符号分隔的参数和值对。
- 其他语法错误:重复的问号等。
为了避免这些错误,使用专门的 URL 创建工具来帮助自己标记 URL。
活动报告
在“营销活动-有机关键词”报告中,您可能会看到两种有趣的值:(未设置)和(未提供)。
图片由作者提供
(未设置)值意味着 Google Analytics 无法准确定义哪个请求为您的网站带来了有机流量。这可能是由以下原因造成的:
- 一些不发送关键字信息的旧搜索系统
- 忘记在使用手动 UTM 标签的活动中设置关键字
- 来自电子邮件中链接的流量
(未提供)值通常在此报告中更常见。这意味着访问者在 HTTPS 协议的帮助下进行了搜索。根据其隐私政策,谷歌必须在这种情况下隐藏关键词信息。
谷歌广告报告
这组报告将帮助你估计你的广告在谷歌展示网络中的效率,并让你知道用户点击广告后做了什么。(未设置)值帮助您优化付费流量的费用。
图片由作者提供
Google Ads 报告中的一个(未设置的)值可能是由几个原因造成的
1.不恰当的谷歌广告和谷歌分析的整合(检查你是否在 GA 中连接了正确的视图和广告中的账户。)
2.谷歌广告中的自动标记被关闭。当访问者点击广告消息时,谷歌点击标识符(GCI)值被添加到目标 URL。它保存在该网站域名的一个新的谷歌分析 cookies 文件中。由于这一功能,我们可以在谷歌分析报告中看到谷歌广告的活动和费用数据。
如果您的网站没有自动标记功能,您可以手动标记链接。为此,打开 Google Analytics 资源首选项并选中复选框允许手动标记。
图片由作者提供
3.同时使用手动和自动标记。
4.无效点击。Google Ads 认为这些是机器人或恶意软件的点击,或者是对广告商没有任何用处的两次或三次点击。你可以在谷歌帮助中心找到更多关于无效点击以及如何处理的信息。
5.重定向。当从一个 URL 重定向到另一个 URL 时, gclid 可能会丢失。结果,你会看到(未设置)。你可以用 Chrome 开发者工具检测并修复这个问题。在谷歌分析帮助中心阅读更多信息。
6.语法和 gclid 问题,例如,如果 URL 中的寄存器发生变化,或者如果您的网站因为超过 100 个符号而删除了 gclid 参数,或者如果您的网站设置禁止处理正确处理标签所需的请求。您可以通过确保所有脚本正确运行来解决此问题。
行为报告
您可以在“网站内容-登录页面”报告中找到(未设置的)值:
图片由作者提供
此报告显示访问者从哪些页面开始他们的会话。登录页面是新访问者熟悉您的网站的页面。在这里检查跳出率。跳出率是在页面上没有采取任何行动就离开的访问者的百分比。
如果你在这个报告中没有数据,你就无法在高跳出率的基础上定义哪些页面需要改进。
如果选择主要维度“内容分组”,您还可以在“网站内容报告”组的其他报告中找到(未设置的)值。
图片由作者提供
您可以将网站内容分成逻辑组,如品牌或产品类别,以便在一份报告中比较它们的效率。很明显,如果你发现(未设置)代替了类别名,那么比较将毫无意义。
在行为报告中,如果出现以下情况,可能会出现(未设置)
1.会议在午夜或访客等待 30 分钟后结束。这 30 分钟后完成的任何操作都将被标记为新会话。如果此操作是会话中的唯一点击,则登录页面值将为(未设置)。
2.会话没有页面或屏幕视图。如果您设置了删除特定页面视图的过滤器,或者屏幕和页面跟踪设置错误,则可能会发生这种情况。查看谷歌帮助中心来解决这个问题。
3.您一次使用了两个跟踪代码(分析。js 和 gtag。js),在这种情况下,一个会话框架中的点击将不会被链接。你应该使用一个代码。谷歌分析推荐使用 gtag。js 。
4.如果(未设置)值出现在内容分组报告中,可能是由于页面标题中的
转换报告
这些报告有助于跟踪你的网站上的转换,并分析买家的行为。它们包含关于交易、产品、目标等的有用信息。
电子商务报告小组
电子商务-产品列表性能报告中的(未设置)值使得无法知道哪些产品最受欢迎,哪些列表应该移动或需要改进,以及哪些应该从该网站部分删除。
图片由作者提供
没有准确的数据,你就无法针对受众群体开展个性化的营销活动。例如,您不能在产品卡上设置折扣块,然后将那些在购买其他产品时查看折扣的访问者分隔开来,并向他们发送包含特殊优惠的电子邮件。
让我们仔细看看另一个例子。假设您在网站上发起了一项内部推广活动,创建了一个对您的活动感兴趣的细分市场,发送带有个人优惠的电子邮件。如果你打开电子商务——营销——内部推广,看到(未设置)值,这意味着你的部分受众没有定义。因此,你失去了将这些用户添加到你的细分市场并赚取更多的机会。
图片由作者提供
通常,(未设置的)值出现在电子商务报告中,因为增强型电子商务跟踪代码缺少单独参数的值:产品列表、订单优惠券、产品优惠券等。要解决这个问题,您需要正确地设置增强的电子商务,以便将必要参数的值和点击发送到 Google Analytics。
目标报告
目标-反向目标路径报告显示访问者在哪个页面上实现了他们的目标,以及在此之前他们访问的三个页面。
图片由作者提供
(未设置的)值将始终出现在此报告中,因为访问者可以在不到三步的时间内实现他们的目标。如果访问者进入网站并立即注册,第 2 步和第 3 步将(未设置),如上面截图中的字符串 2 所示。
结论
(未设置的)值可以在大多数谷歌分析报告中找到。表示系统要么没有数据,要么不识别。发生这种情况有各种原因。有些我们可以解决,有些我们不能,但我们可以在分析数据时考虑到这一点。
如果您忽略报表中的(未设置的)值,您的业务将会付出高昂的代价。谁喜欢把钱投在低效的措施上?数据损坏会导致曲解报告和不可靠的决策。
表格识别中如何识别和修复断线
使用 OpenCV / Python 修复表格的孔洞和缺失轮廓
来源:unsplash(agrêBarros)。
当文档通过扫描或照片数字化时,图像质量可能会受到错误设置或恶劣条件的影响。在表格识别的情况下,这可能导致表格结构损坏。因此,一些线条可能会有轻微的瑕疵,甚至有洞,并且桌子作为一个整体不能被认为是一个连贯的系统。有时创建的表格在单元格的某些边上没有线条。在这种情况下,系统会用上面单元格的行来关闭这些行。有各种各样的表格和单元格类型,因此建议的代码可能并不完全适用于所有情况。尽管如此,只要稍加修改,它在许多情况下都是有用的。
大多数细胞识别算法是基于线和细胞结构。由于“遗忘”的细胞,没有线导致识别率差。这种方法也是如此。台词是必须的。如果你的桌子没有清晰可辨的线条,那就没用了。但是现在,让我们来看看代码!
首先,我们需要进口。在这种情况下,它仅限于两个导入:OpenCV 和 NumPy。
import cv2
import numpy as np
然后我们需要加载包含表格的图像/文档。如果它是一个完整的文档,文本围绕着表格,你需要首先识别表格,然后将图像剪切到表格大小。(要了解更多关于表格识别和切割到表格尺寸的信息,点击此处。)
**# Load the image** image = cv2.imread(‘/Users/marius/Desktop/holes.png’, -1)
输入图像。
正如您在输入图像中看到的,第二行中的单元行没有完全连接。在表格识别中,算法不会识别和考虑第二行,因为单元格不是封闭的方框。本文提出的解决方案不仅适用于这种间隙情况。它也适用于表格中的其他折线或孔洞。
现在,我们需要获取图像的大小(高度和宽度)并将其存储在变量 hei 和 wid 中。
(hei,wid,_) = image.shape
下一步是通过高斯滤波器进行灰度和模糊处理。这有助于识别线条。关于灰度的更多信息,点击这里。
**#Grayscale and blur the image**
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
然后我们需要设定图像的阈值。如果你想了解更多关于阈值处理的知识,你可以阅读这篇文章:点击这里(一直到二值化图像)。
**#Threshold the image**
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
然后使用 OpenCV 的 findContours 算法获得所有轮廓的位置。对于所有轮廓,绘制边界矩形以创建表格的框/单元。然后,这些框与四个值 x,y,width,height 一起存储在列表框中。
**#Retrieve contours**
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)**#Create box-list**
box = []**# Get position (x,y), width and height for every contour** for c in contours:
x, y, w, h = cv2.boundingRect(c)
box.append([x,y,w,h])
然后,所有的高度、宽度、x 和 y 分别存储在列表中,并计算最小高度、宽度以及 x 和 y。此外,y 和 x 的最大值是必要的。
**#Create separate lists for all values**
heights=[]
widths=[]
xs=[]
ys=[]**#Store values in lists** for b in box:
heights.append(b[3])
widths.append(b[2])
xs.append(b[0])
ys.append(b[1])**#Retrieve minimum and maximum of lists** min_height = np.min(heights)
min_width = np.min(widths)
min_x = np.min(xs)
min_y = np.min(ys)
max_y = np.max(ys)
max_x = np.max(xs)
存储的值现在用于了解表的位置。最小 y 值可用于获取表格的最上面一行,该行可视为表格的起点。最小 x 值是表格的左边缘。为了获得大概的大小,我们需要检索最大的 y 值,也就是表格底部的单元格或行。最后一行的 y 值表示单元格的上边缘,而不是单元格的底部。要考虑单元格和表格的整体大小,需要将最后一行的单元格高度加上最大值 y,以获得表格的完整高度。最大值 x 将是表格的最后一列和最右边的单元格/行。x 值是每个单元格的左边缘,我们需要连续地将最后一列的宽度加上最大 x 值,以检索表格的整个宽度。
**#Retrieve height where y is maximum (edge at bottom, last row of table)**
for b in box:
if b[1] == max_y:
max_y_height = b[3]**#Retrieve width where x is maximum (rightmost edge, last column of table)** for b in box:
if b[0] == max_x:
max_x_width = b[2]
在下一步中,提取所有水平线和垂直线并分别存储。这是通过创建一个内核来实现的,该内核设定阈值并应用形态学操作。水平内核的大小为(50,1)。你可以根据图片的大小来调整尺寸。垂直内核的大小为(1,50)。
形态学操作根据其几何形状对检测到的结构进行变换(Soille,第 50 页,1998)。膨胀是应用最广泛、最基本的形态学操作之一。如果内核下至少有一个像素是白色的,则原始图像中正在查看的像素将被视为白色。因此,白色区域被扩大。请注意,由于反转,背景是黑色的,前景是白色的,这意味着表格线目前是白色的。扩张可以被看作是最重要的一步。孔洞和断线现在已修复,为了进一步识别表格,将考虑所有单元格。
**# Obtain horizontal lines mask** horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,1))
horizontal_mask = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
horizontal_mask = cv2.dilate(horizontal_mask, horizontal_kernel, iterations=9)**# Obtain vertical lines mask**
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,50))
vertical_mask = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=1)
vertical_mask= cv2.dilate(vertical_mask, vertical_kernel, iterations=9)
表格的水平和垂直掩码。
然后使用 OpenCV 的 bitwise_or 操作将水平和垂直两个掩码合并到一个表中。为了检索原始的背景和前景,通过从 255 中减去 cv2.bitwise_or 来反转图像。
**# Bitwise-and masks together**
result = 255 — cv2.bitwise_or(vertical_mask, horizontal_mask)
合并和反转水平和垂直遮罩。
如果表格被文本包围,而不是孤立的(在我的例子中,它没有被包围),我们把它剪下来,放在白色背景上。现在我们需要之前检索的表的大小。我们通过使用最小 y(它是顶部的边缘)、最大 y +最大 y 单元的高度(它是底部的边缘)、最小 x(它是左侧边缘)和最大 x +最大 x 单元的宽度(它是右侧边缘)将最终图像切割成表格大小。然后,图像被裁剪为表格的大小。创建文档原始大小的新背景,并用白色像素完全填充。检索图像的中心,修复后的表格与白色背景合并,并设置在图像的中心。
**#Cropping the image to the table size**
crop_img = result[(min_y+5):(max_y+max_y_height), (min_x):(max_x+max_x_width+5)]**#Creating a new image and filling it with white background** img_white = np.zeros((hei, wid), np.uint8)
img_white[:, 0:wid] = (255)**#Retrieve the coordinates of the center of the image**
x_offset = int((wid — crop_img.shape[1])/2)
y_offset = int((hei — crop_img.shape[0])/2)**#Placing the cropped and repaired table into the white background**
img_white[ y_offset:y_offset+crop_img.shape[0], x_offset:x_offset+crop_img.shape[1]] = crop_img**#Viewing the result**
cv2.imshow(‘Result’, img_white)
cv2.waitKey()
修复了虚线的结果图像。
这就是结果。该方法可用于表格中多种类型的折线、间隙和孔洞。这个结果是进一步表格识别的基础,在我的另一篇文章中有解释。所解释的方法应用于一个空表。您也可以将它应用于包含文本或被文本包围的表格。对于包含文本的表格,仍然需要将包含具有数据的表格的原始图像与具有修复的孔洞的最终图像合并。这可以通过 OpenCV 位运算来完成,并且不会太复杂。
我希望你喜欢我的教程,并能将其用于你自己的项目。
想看更多这样的故事?
上手
如何招(和留!代表不足群体的个人
十七(…和计数…)提高公司多样性的策略
这份名单是与朋友、同事和其他社交媒体关系合作创建的。有些观点特别关注招募女性的方式,因为这是我最了解和理解的,但我相信这也是从其他代表性不足的群体中引入个人的有用方式。
- 看看你的招聘启事中的语言。寻找不经意的性别术语,并利用在线资源进行筛选。
- 将“需求”改为“首选技能”——除非它们是真正的需求。urg 成员倾向于只申请那些拥有大部分所需技能的职位,而白人 cis 性别的男性更愿意申请那些只有少量要求的职位。
- 针对代表性不足的人口统计向职务公告板发布。这不仅会给你带来更多来自这些群体的申请人,还会向你的申请人表明你对多样性的重视。(作为一名白人女性数据科学家,我熟悉编写代码的女性和女性以及机器学习和数据科学,但还有更多,谷歌搜索会帮助你找到适合你职位的发布位置)
- 在您的办公室为[在此插入字段]的女性举办一次活动。看看你是否能在你的领域找到一个有兴趣在活动中发言的摇滚女明星,即使她不在你的公司工作。这同样适用于在你的领域招聘黑人,LGBTQIA 等。依我看,最好是单独举办针对特定群体的活动,而不是为几个代表性不足的群体举办一次联合活动,以专注于特定的事情。(我当然愿意接受其他观点!)
- 如果你喜欢用内部引荐,那就找你所在领域的女性朋友引荐。他们可能认识很多有才华的女性。(同样,这也适用于其他人口统计)。
- 不要让它看起来像一件坏事,你需要“走出你的方式”来招募那些不像你的团队已经有的人。这是一个问题,解决它的唯一方法是走出自己的路。
- 给新员工真正的机会,让他们在你的公司茁壮成长,展示他们的技能。
- 表扬好的工作。对需要改进的地方给予一致和建设性的反馈。
- 为所有候选人设立一个多元化的面试小组。这有助于你筛选候选人中有问题的态度或偏见,告诉你的员工他们的意见是有效的,并给候选人一个更平等的机会。
- 聘用一名多元化、公平和包容(DEI)主管。
- 设定明确的多元化目标。追踪你在这些方面的进展,注意哪些策略对实现你的目标最有效。
- 向推荐来自代表性不足的背景的候选人的员工提供奖金。
- 进行无意识偏见训练。
- 在你的公司建立员工资源组(亲和组)。
- 使推广标准不分性别。关注你需要从个人身上得到的结果,而不是你在这些方面描述候选人的方式。
- 浏览你发送给候选人、新员工和雇员的所有信息,寻找性别代词(例如,“他将具备以下技能…”)
- 对于每个职位,至少面试两个来自代表名额不足的候选人,以防止一个候选人相对于一群同质的候选人来说看起来像局外人。
我作为 STEM 女性的就业背景
通过自由职业和全职工作,我在很多团队工作过。大多数时候,团队成员都是异性恋、独联体性别的白人或亚裔男性。有几次,我是队里唯一的女性。在一家公司工作的 9 年里,我是第一个加入 7 人团队的女性。(在我的第一天,有人评论说我是“试验品”,以决定团队是否有能力让一名女性加入。)
三年前,我为自己制定了一个规则。我不会参加所有白人或亚裔男性面试小组的面试。当招聘人员给我安排了一次面试时,我礼貌地要求他们在面试者名单中特别添加一名女性或非二元个体。
这一要求对招聘人员产生了两极分化的影响。我已经让招聘人员感谢我提出这个要求,并把令人印象深刻的女性列入我的面试名单。
我的要求也激怒了招聘人员。我被告知这个要求是不可能满足的。有人告诉我,我不愿意加入一个全是男性的大型团队,这是他们的团队中没有女性的原因,我是真正的问题所在。
在 Instagram 上吸引我的朋友
几天前,我和一位招聘人员进行了一次特别糟糕的交流,我在 Instagram stories 上发泄了一下。让我特别沮丧的是,有一种观点认为面试名单缺乏多样性的原因是“管道问题”,是雇主无法控制的。我的机械工程专业毕业班女生比男生多。我不知道他们还需要这条管道走多远。
在发泄情绪的过程中,我遇到了意想不到的转机。我收到了很多人真诚的请求,询问如何让他们的申请人多样化。我开始张贴我对我的故事的回答,然后张贴别人发给我的想法。我从一个女性的角度写了一些吸引女性的方法,然而,我认为这些是吸引来自任何代表不足群体(URG)的候选人的有用技巧:
我的 insta rant
(9b 的荣誉归于凯西·韦塞尔。10-15 的荣誉属于兰登·迪基。)
朋友的反馈
一个朋友要求澄清什么会被认为是“不经意的性别术语”我很难准确定义这些是什么,但我认为任何暗示“兄弟”文化的东西,无论是说所有员工都是“摇滚明星忍者”,还是要求候选人喜欢“努力工作,努力聚会”。
我众包了一些更好的方法来描述无意中性别化的术语, Maddie Mirzoeff 向我推荐了这篇文章。她还在列表中添加了下面的第一个要点。
这些截图之后,我收到的一些建议包括:
- 使推广标准不分性别。为了做到这一点,你必须确保你描述的是结果而不是个人。所以不要说“有高管风度”,而要说“能够主持会议,并得出结果。”
- 通过软件发布招聘信息,这些软件会检查有问题的措辞,比如 Textio ( 劳伦·毕晓普)
- 浏览你所有的表格回答中的性别代词,确保不要用“他将具备这些技能”来描述一个好的候选人
- 面试至少两个 urg 的候选人,而不是一个。这篇文章解释了为什么会显著增加 URGs 的候选人获得该角色的公平机会。
后续步骤:
我希望继续补充这个列表。我也希望通过分享我的白人男性朋友的问题来帮助我制定这个列表,那些不是 URGs 成员的人会觉得加入对话和提问很舒服。我对其他人的想法很感兴趣,他们可以创建一个名单,任何人都可以添加到名单中,同时也给人们的贡献带来荣誉。
成功地包容未被充分代表的群体,尤其是处于这些群体交汇点的个人,将需要一些创造力来颠覆现状。确定可操作的清晰的后续步骤并不容易。但我相信 STEM 领域的专业人士能够应对这一挑战。
如何使用 tf.data 减少深度学习模型的训练时间
学习为图像创建输入管道,以高效地使用 CPU 和 GPU 资源来处理图像数据集,并减少深度学习模型的训练时间。
在这篇文章中,你将学到
- 在模型训练中,CPU 和 GPU 资源是如何以一种天真的方式使用的?
- 如何高效利用 CPU 和 GPU 资源进行数据预处理和训练?
- 为什么要用 tf.data 构建高效的输入管道?
- 如何使用 tf.data 为图像建立高效的输入数据管道?
一个朴素的方法如何为输入数据管道和模型训练工作?
在创建输入数据管道时,我们通常执行 ETL(提取、转换和加载)过程。
- 提取、**从不同的数据源提取数据,如本地数据源、**可以从硬盘或远程数据源提取数据,如云存储。
- 转换,你将洗牌数据,创建批次,应用矢量化或图像放大。
- 加载数据包括清理数据并将其塑造成一种我们可以传递给深度学习模型进行训练的格式。
数据的预处理发生在 CPU 上,模型通常在 GPU/TPU 上训练。
在简单的模型训练方法中, CPU 预处理数据,为模型训练做好准备,而 GPU/TPU 处于空闲状态。当 GPU/TPU 开始训练模型时,CPU 是空闲的。这不是管理资源的有效方式,如下所示。
原始数据预处理和训练方法
有哪些方法可以加快培训过程?
为了加快训练,我们需要优化数据提取、数据转换和数据加载过程,所有这些都发生在 CPU 上。
数据提取:优化从数据源读取的数据
数据转换:并行化数据扩充
数据加载:在训练前一步预取数据
这些技术将有效地利用 CPU 和 GPU/TPU 资源进行数据预处理和训练。
怎样才能实现输入管道的优化?
优化数据提取
通过同时处理多个文件来优化数据提取。 tf.data.interleave()通过交错读取文件的 I/O 操作和应用数据预处理的 map()优化数据提取过程。
来源:https://www . tensor flow . org/guide/data _ performance # parallelising _ data _ extraction
要重叠的数据集数量由 cycle_length 参数指定,而并行度由num _ parallel _ calls参数设置。您可以使用 AUTOTUNE 来决定要实现的并行级别。
num _ parallel _ calls产生多个线程,利用机器上的多个内核,通过使用多个 CPU 来并行化数据提取过程。
如何知道要用多少个 CPU 或核心?
您可以找到机器上的内核数量并指定,但更好的选择是使用**tf.data . experimental . auto tune .**将并行级别委托给 TF . data
- 自动调优将要求 tf.data 在运行时动态调优该值。
- tf.data 将在所有可调操作中找到正确的 CPU 预算。
- 自动调优决定缓冲区大小、CPU 预算以及 I/O 操作的并行级别。
并行数据转换
图像增强是预处理的一部分,发生在 CPU 上。图像的每一次放大、归一化、重新缩放都是一种代价高昂的操作,并且会减慢训练过程。
如果你能通过并行处理利用所有内核来运行所有这些图像操作。
tf.data.map() 可以采用用户定义的函数,该函数包含您想要应用于数据集的所有图像增强。
tf.data.map()有一个参数 num_parallel_calls 来产生多个线程,以利用机器上的多个内核来使用多个 CPU 并行化预处理。
缓存数据
cache() 允许将数据缓存在指定文件或内存中。
- 在内存中缓存时,第一次迭代数据时,将缓存数据,所有后续迭代,将从缓存中读取数据。
- 当缓存一个文件时,即使是第一次迭代数据也会从缓存的文件中读取。
- 缓存为每次迭代产生相同的元素,在缓存数据后,使用 shuffle()随机化迭代中的元素。
通过重叠数据处理和训练来预取数据
**TF . data 中的预取功能重叠了数据预处理和模型训练。数据预处理比训练提前一步运行,**如下所示,这减少了模型的总训练时间。
来源:https://www . tensor flow . org/guide/data _ performance #预取
要预取的元素数量应该等于或大于用于单个训练步骤的批量大小。我们可以使用 AUTOTUNE 来提示 tf.data 在运行时动态分配缓冲区大小值。
所有的操作:映射,预取,交错,批量,重复,混洗,和缓存都是 tf.data 的一部分,可以让你构建
- 通过使用计算资源、GPU、CPU 和 TPU,更快、更高效地建立数据管道,高效地从数据源获取数据。
- 灵活处理不同的数据格式,如文本数据、图像和包含数字和分类数据的结构化数据。
- 通过应用数据扩充、混排数据集和创建用于训练的批量数据,轻松构建复杂的输入数据管道
如何使用 tf.data 为自定义影像数据集构建数据管道?
在本节中,您将为来自 Kaggle 的流行的 C ats 和 Fogs 数据集构建一个数据输入管道。
这里我们将使用 MobileNetV2 和 TensorFlow 2.3 进行迁移学习
导入所需的库
**import tensorflow as tf
config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.compat.v1.Session(config=config)
import numpy as np
import pandas as pd
import pathlib
import os
from os import getcwd
import pandas as pd
from glob import glob
import multiprocessing
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input**
为数据集设置 train 和 val 目录
**train_dir=r'\dogs-vs-cats\train_data'
val_dir=r'\dogs-vs-cats\validation_data'**
将文件转换为数据集对象
使用***TF . data . dataset . list _ files()**根据匹配的 glob 模式返回文件名。这里我们希望所有的文件都来自 train_dir 和 val_dir 文件夹下的子文件夹,所以我们指定了“*\”
**train_files = tf.data.Dataset.list_files(str(train_dir + '\\*\\*'), shuffle=False)**
**val_files = tf.data.Dataset.list_files(str(val_dir + '\\*\\*'), shuffle=False)**#getting the number of files in train and val dataset
**train_num_files=len([file for file in glob(str(train_dir + '\\*\\*'))])
val_num_files=len([file for file in glob(str(val_dir + '\\*\\*'))])** print("No. of files in Train folder: ",train_num_files)
print("No. of files in Val folder: ",val_num_files)
预处理训练和验证数据集
设置参数
epoch=10
batch_size = 32
img_height = 224
img_width = 224
应用 MobileNet 的预处理技术
**#Get class names from the folders
class_names = np.array(sorted([dir1 for dir1 in os.listdir(train_dir)]))
class_names**#To process the label
**def get_label(file_path):**
# convert the path to a list of path components separated by sep
** parts = tf.strings.split(file_path, os.path.sep)**
# The second to last is the class-directory
**one_hot = parts[-2] == class_names**
# Integer encode the label
**return tf.argmax(tf.cast(one_hot, tf.int32))**# To process the image
**def decode_img(img):**
# convert the compressed string to a 3D uint8 tensor
** img = tf.image.decode_jpeg(img, channels=3)**
# resize the image to the desired size
**return tf.image.resize(img, [img_height, img_width])****def process_TL(file_path):
label = get_label(file_path)**
# load the raw data from the file as a string
**img = tf.io.read_file(file_path)
img = decode_img(img)
img = preprocess_input(img)
return img, label**
通过交错优化数据提取和数据转换过程
Interleave() 将通过交错读取文件的 I/O 操作和 map() 将数据预处理应用到数据集的内容来并行化数据加载步骤。
#Interleaving the train dataset to read the file and apply preprocessing **train_dataset = train_files.interleave(lambda x: tf.data.Dataset.list_files(str(train_dir + '\\*\\*'), shuffle=True), cycle_length=4).map(process_TL, num_parallel_calls=tf.data.experimental.AUTOTUNE)**#Interleaving the val dataset to read the file and apply preprocessing **val_dataset = val_files.interleave(lambda x: tf.data.Dataset.list_files(str(val_dir + '\\*\\*'), shuffle=True), cycle_length=4).map(process_TL, num_parallel_calls=tf.data.experimental.AUTOTUNE)**
要重叠的数据集数量设置为 4,这由 cycle_length 参数指定。并行级别由 num_parallel_calls、 指定,后者设置为 AUTOTUNE
加载数据集进行训练
在内存中缓存数据集
##Cache the dataset in-memory
**train_dataset = train_dataset.cache()
val_dataset = val_dataset.cache()****train_dataset = train_dataset.repeat().shuffle(buffer_size=512 ).batch(batch_size)
val_dataset = val_dataset.batch(batch_size)**
TF . data . dataset 类的 repeat() 方法用于重复数据集中的张量。
shuffle () 使用大小为 512 的缓冲区对 train_dataset 进行混洗,以便挑选随机条目。
batch() 将根据设置的批量大小获取前 32 个条目,并从中进行批量处理
**train_dataset = train_dataset.repeat().shuffle(buffer_size=512 ).batch(batch_size)****val_dataset = val_dataset.batch(batch_size)**
TF . data 中的预取功能重叠了数据预处理和模型训练
**train_dataset =train_dataset.prefetch(tf.data.experimental.AUTOTUNE )
val_dataset =val_dataset.prefetch(tf.data.experimental.AUTOTUNE )**
创建用于垂直和水平翻转图像、旋转图像、缩放和应用对比度的数据增强。
**data_augmentation = tf.keras.Sequential([ tf.keras.layers.experimental.preprocessing.RandomFlip('horizontal'), tf.keras.layers.experimental.preprocessing.RandomFlip('vertical'), tf.keras.layers.experimental.preprocessing.RandomRotation(0.45), tf.keras.layers.experimental.preprocessing.RandomContrast(0.2), tf.keras.layers.experimental.preprocessing.RandomZoom(0.1),])**
通过首先应用数据扩充来创建迁移学习模型
**def create_model():
input_layer = tf.keras.layers.Input(shape=(224, 224, 3))
x= data_augmentation(input_layer)
base_model = tf.keras.applications.MobileNetV2(input_tensor=x, weights='imagenet',include_top=False)
base_model.trainable = False
x = tf.keras.layers.GlobalAveragePooling2D()(base_model.output)
x = tf.keras.layers.Dense(2, activation='softmax')(x)
model = tf.keras.models.Model(inputs=input_layer, outputs=x)
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
return model****model= create_model()**
创建检查点阈值,训练将继续,直到我们获得 99.96%的验证准确率,或者直到完成指定数量的时期。
**class MyThresholdCallback(tf.keras.callbacks.Callback):
def __init__(self, threshold):
super(MyThresholdCallback, self).__init__()
self.threshold = threshold****def on_epoch_end(self, epoch, logs=None):
val_acc = logs["val_accuracy"]
if val_acc >= self.threshold:
self.model.stop_training = True****my_callback = MyThresholdCallback(threshold=0.9996)**
将训练数据集拟合到模型中
**import time
start_time= time.perf_counter()
history_tfdata =model.fit(train_dataset,
steps_per_epoch=int((train_num_files)/batch_size),
validation_data= val_dataset,
validation_steps=int(val_num_files/batch_size),
callbacks=[my_callback],
epochs=epoch)****print(time.perf_counter()-start_time)**
如果我们使用如下所示的 ImageDataGenerator 训练数据集,我们可以比较训练时间的差异。
**from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img, array_to_img
from tensorflow.keras.models import load_model
from tensorflow.keras import optimizers, callbacks**#Creating Image Train DataGenerator
**image_gen_train = ImageDataGenerator(rescale=1./255,
zoom_range=0.1,
rotation_range=45,
shear_range=0.1,
horizontal_flip=True,
vertical_flip=True)
train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size, directory=train_dir,
shuffle=True, target_size=(224,224), class_mode='sparse')**# Val data generator
**image_gen_val = ImageDataGenerator(rescale=1./255)
val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,directory=val_dir, target_size=(224,224),class_mode='sparse')****def create_model():
input_layer = tf.keras.layers.Input(shape=(224, 224, 3))
input_layer=preprocess_input(input_layer)
base_model = tf.keras.applications.MobileNetV2(input_tensor=input_layer,
weights='imagenet',
include_top=False)
base_model.trainable = False
x = tf.keras.layers.GlobalAveragePooling2D()(base_model.output)
x = tf.keras.layers.Dense(2, activation='softmax')(x)
model = tf.keras.models.Model(inputs=input_layer, outputs=x)
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
return model****model_idg=create_model()****start_time2= time.perf_counter()
history = model_idg.fit(
train_data_gen,
steps_per_epoch=len(train_data_gen),
epochs=10,
callbacks=[tboard_callback],
validation_data=val_data_gen,
validation_steps=len(val_data_gen)
)
print(time.perf_counter()-start_time2)**
将使用 tf.data 输入管道完成训练的时间与使用 ImageDataGenerator 完成训练的时间进行比较
您可以看到使用 tf.data 完成训练的时间是 290.53 秒,而使用 ImageDataGenerator 完成相同数据的训练是 2594.89 秒,这在训练时间方面是一个很大的进步
代码可用此处
结论:
tf.data 允许您通过高效地使用 GPU、CPU 和 TPU 等计算资源,为不同的数据格式构建高效的输入数据管道,从而减少训练时间。
参考资料:
[## tf.data:构建 TensorFlow 输入管道| TensorFlow 核心
创建数据集有两种不同的方法:将 TensorFlow 作为 tf 导入 pathlib 导入 os 导入…
www.tensorflow.org](https://www.tensorflow.org/guide/data) [## 使用 tf.data API | TensorFlow 核心提高性能
GPU 和 TPU 可以从根本上减少执行单个训练步骤所需的时间。实现最佳性能…
www.tensorflow.org](https://www.tensorflow.org/guide/data_performance)
https://github . com/tensor flow/docs/blob/master/site/en/guide/data . ipynb
如何重构 Jupyter 笔记本
现实世界中的 DS
使用这些技术改进您的代码库并变得更加高效
(TL;DR——这张图片总结了整篇文章)
在 ML 世界中,代码会很快变得混乱。
最初令人惊叹的 ML 模型很容易变成一大堆难以理解的代码。修改代码变得痛苦且容易出错,对于 ML 从业者来说,发展他们的 ML 解决方案以满足新的{业务需求、特征工程策略、数据}变得越来越困难。
在这篇文章中,我将与你分享我重构 Jupyter 笔记本的过程,并向你展示我是如何将它从不可维护的状态提升到{可读、测试、可维护}的状态。一旦我们的代码库经过全面测试并且容易理解,扩展和发展我们的 ML 解决方案就容易多了。
注意:如果你不确信需要重构你的 Jupyter 笔记本,看看这篇文章👻
1.支持重构的六个准备步骤
重构是改变代码,使其更容易理解和修改,而不改变其可观察到的行为。(转述自重构 —马丁·福勒)
代码并不总是可重构的。
很长一段时间以来,我一直盯着我的 Jupyter 笔记本,思考如何让它变得更好。我知道我应该写测试和重构我的代码。但是有一些阻碍我迈出第一步的障碍:(1)担心我可能会破坏一些东西,(2)担心我可能会删除其他人需要的代码,(Jupyter 笔记本中繁琐的重构机制(例如,尝试重命名变量)。
在运行了两次重构研讨会之后,我发现了六个启动和加速重构的准备步骤:
- 从头到尾运行笔记本,确保一切正常。
- 这将为您省去不必要的麻烦,即必须弄清楚我们是否在重构时破坏了某些东西,或者代码是否已经被破坏了。
2.制作原始笔记本的副本。
- 这是令人惊讶的重要一步。它将把我们从任何情感依恋中解放出来(“嗯,我不知道,有人可能需要这个图表”),并允许我们无情地清理任何不必要的代码。
- 通过分离这些关注点(即,核心数据转换和表示),我们可以有两个独立的东西来很好地处理每个关注点,而不是一个巨大的笔记本同时以灾难性的方式处理这两个问题。
3.将 Jupyter notebook 转换成普通的 Python 文件。
- 这将使您获得使用 IDE 的所有好处(例如,自动完成、智能感知、内联文档、格式化、自动重命名、林挺、键盘快捷键等)。这让你在重构中更有效率。
- 命令:
jupyter nbconvert —-to script mynotebook.ipynb
4.删除打印报表,如print(...)
、df.head()
、df.plot(...)
- 这消除了噪音和视觉混乱,使下一步变得更加容易。
5.看笔记本,列出代码气味。
- 您识别的代码气味列表成为您重构中的待办事项列表(参见示例)。这也将为您节省一些精神资源,避免您不断思考下一步要重构什么。
6.定义重构边界,并添加一个特征测试。
- 一个特征测试将你的程序视为一个黑盒,而描述它的行为(例如,我的笔记本输出一个准确率为 68%的模型)并断言该特征(例如,如果我们运行我们的代码,得到一个准确率低于 68%的模型,测试失败)
- 这可以说是👏这👏最👏重要的👏迈步!
- 进行特征测试会给你快速的反馈,因为它可以在你重构的时候持续运行。如果你不小心引入了突破性的变化,它会在几秒钟内告诉你。
- 如果没有特性测试,你将不得不经常手动重启和重新运行整个 Jupyter 笔记本——这很麻烦,对你的流程有很大的破坏性,所以 2019 年。
- 这一步有点难以用文字来解释,所以我录制了这个演示来告诉你如何定义重构边界并编写一个特性测试来支持你的重构。
2.重构周期(耶!)
在重构循环中,我们通过(I)添加单元测试和(ii)将复杂的实现细节抽象成模块化和可读的函数,增量地和迭代地改进我们的代码。
重构周期如下:
1.确定可以提取到纯函数中的代码块(例如,无论何时何地运行,对于给定的输入都返回完全相同的输出的函数)。
2.编写一个单元测试。(参见演示)
- 在观察模式下运行单元测试:例如
nosetests -—with-watch —-rednose
- 为代码块编写单元测试
3.让测试通过。
- 定义一个新函数,并将其放在一个新的 Python 模块/文件中(或者一个现有的模块/文件,如果您的代码中有合适的模块/文件的话)
- 将现有实现从笔记本移到该功能中
- 让失败的测试通过
4.在笔记本中,用新定义的函数替换原来的代码块。
5.确保特性测试仍然通过。
6.将您的更改提交给 git。
现在,如果你觉得你刚刚提取的代码还可以再改进一些,你可以用你的新单元测试的安全网进一步重构它。
否则,您可以在 Jupyter 笔记本中的另一个代码块上重复重构周期,并重复直到代码库被全面测试并重构为可读和可维护的状态。
3.我们费了那么大劲得到了什么?
简而言之,你可以从一个凌乱难读的笔记本变成一个经过测试的模块化代码库。🎉🎉🎉
自动化测试和重构的结合可以极大地帮助我们:
- 缩短反馈周期。有了自动化测试,您现在可以立即知道错误和缺陷是如何引入的。如果测试覆盖面是全面的,它还会给你信心,让你知道一切都准备好了,并且节省你手动测试整个 Jupyter 笔记本的时间。
- 减少浪费。通过测试和模块化功能,您可以减少花费在以下方面的精力:(I)阅读与您的任务无关的实现细节,(ii)即使我们只想更改一个简单的东西,也要记住整个 Jupyter 笔记本,(iii)修复我们昨天不小心引入的错误,以及(iv)[插入让您连续几个小时盯着笔记本看的东西]。
- 增加流量。所有这些意味着您可以专注于手头的任务(例如,集成一组新功能)并交付价值,而不是上面提到的乏味、浪费的任务😎
谢谢你读到这里!我希望这对你有所帮助🚀🚀🚀
要查看我如何重构 Jupyter 笔记本的实际例子,请查看这个演示!
这是数据科学家的 编程习惯 系列教程的一部分,旨在帮助数据科学家通过学习良好的编程习惯变得更有生产力。