TowardsDataScience 博客中文翻译 2020(一千零八)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

词向量和降维

原文:https://towardsdatascience.com/word-vectors-and-decoding-autoencoder-for-dimensionality-reduction-407815ead4b7?source=collection_archive---------28-----------------------

将高维数据转换为低维数据并开始使用自动编码器

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

马库斯·斯皮斯克在 Unsplash 上的照片

所以这是我的第一篇媒体文章,我写这篇文章是为了从我在自然语言处理领域的研究冒险中休息一下,主要是基于方面的情感分析(ABSA)和机器翻译,所以今天你会读到自然语言理解中最基本的概念之一,即词向量和维数约简(DR),我们将主要关注 DR,然后讨论自动编码器,因为它们非常流行,并且当前的系统在某种程度上基于相同的思想。首先,我们将深入了解一些历史,然后逐步转向事情本身。

词语和语义简史

所以,如果你读过一些关于单词和语法的知识,你可能会遇到这句名言"你应该知道它所保持的公司的一个单词",作者是弗斯,1957 年,这是我们大部分自然语言知识的基础,也很有逻辑性。一个词在使用时应该和它周围的词有关系。在更高的层面上,我们可以看到,在进行语义和句法分析时,某些词类跟随某些词类,如在句子“她承诺支付账单”中,下面代表的是词性标签,你经常会看到这种结构,介词跟随动词,动词跟随限定词。但这完全是基于句子,我们的语言比未来更模糊。

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

标有词性标签的例句

所以一个更好的策略是用向量的形式来表示一个句子中的单词,其中一个很容易理解的向量是“一键编码”,我们在单词出现的地方加 1,所以这是一个长 n×1 的向量,其中 n 是句子的长度。所以这个向量将会非常稀疏和巨大,这是不切实际的。所以我们通常使用两种类型的向量,术语文档矩阵和术语上下文矩阵,我们将在下一节讨论。

矩阵重装

让我们从术语文档矩阵开始,这与我们的一次性编码非常相似,但它很简短,因为我们只查看对我们重要的单词。因此,下面的矩阵显示了列中单词和行中文档的计数向量。

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

突出显示的部分是剧中“如你所愿”的字数。

所以现在如果我们推断这张表,我们就能很好地了解单词之间的相似性。这个想法是,如果单词是相似的,如果他们的计数足够接近。

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

这说明傻子和小丑很亲近,战斗和士兵也是

我并不反对小丑,但这些都是事实。但是这个模型并不实用,因为从庞大的数据集中提取单词并创建这样一个矩阵会花费很多时间,而且也不实用。我提到的另一个矩阵是术语上下文矩阵。现在,我们不再使用完整的文档,而是使用一些段落来定义单词的上下文。这里是我们的“文集”。

1。等量的糖,一片柠檬,一汤匙 蜜饯或果酱,丁香和肉豆蔻各一撮。

2。让他们在船上享受。她小心翼翼地品尝了她的第一个 菠萝 和另一种水果,她把它们的味道比作。

3。属于递归类型,非常适合在数字计算机上编程。在寻找最优 R 阶段策略时。

4。对商业产生实质性影响,目的是收集本第一部分授权的研究所需的数据和信息。

粗体字是我们的 so 目标,我们必须找到类似的单词。所以让我们用上下文做一个矩阵。

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

在这里,我们可以立即看到,杏和菠萝相似是有道理的,但这对机器来说有什么意义,我们如何将它扩展到更大的集合。所以现在我们必须使用 PMI(逐点突变信息)来获得单词之间的相似度:

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

这给出了单词一起出现的概率。

这个函数现在做的是,得到单词在上下文中存在的概率,除以单词独立出现的概率。但是为什么呢?因为这将给出相似性的真正本质,即如果单个事件的数量较低,并且总体相对较高,则日志内部的项较大,PMI 较高,相似性也较高。神秘对数函数包装了逆概率。PMI 的另一个变化是 PPMI,它用零代替 PMI 的负值。我们可以做的另一个变化是添加 K 拉普拉斯平滑,它不会导致负值或无关紧要的值。我们可以更进一步,通过余弦距离计算这些单词之间的距离,余弦距离由下式给出

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

这里 Vi 是单词 V 在 W 的上下文中的 PMI

现在这将告诉我们最终的相似性。

尺寸以及为什么要缩小尺寸

因此,我们人类是复杂的,但我们不会想象超过 3 个维度,如果你想阅读更多关于这个的内容点击这里我只是觉得它太酷了,但《阿凡达》也给出了一个很好的想法。让我们来谈谈这些维度,当我们谈到结构时,它指的是 x,y,z 轴,但这里我们没有任何物理结构,这里我们有数据,数据中的高维意味着有一些不直接可见的信息。所以这就是为什么我们减少它来得到直接依赖和更小的矩阵。尽管这在应用线性代数领域中被广泛研究,但它在 NLP 和一般机器学习领域中非常重要。

一个小小的免责声明,通过这篇文章,我希望给你一些自动编码器背后的直觉,但是如果你想了解更多关于降维的数学知识,请参考我的一位教授的 论文

潜在语义分析(LSA)

在这一节中,我将尝试提供 LSA 背后的一点直觉,它是进入自动编码器的一种踏脚石。所以 LSA 是基于一个截断的 SVD(单值分解),如果你知道它在推荐系统中被广泛使用。如果你不知道,这是他们背后的想法,所以你拿一个矩阵,你把它转换成 3 个不同的矩阵,每一个都给出了初始矩阵的一些信息,所以,这里你可以看到我真的打破了我的矩阵,以便更直观地理解它。

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

来源

关于这些矩阵的一个事实是,它们是分层排列的,这意味着它们从最重要的开始排列。那么我们为什么需要这个呢?为了回答这个问题,我们将使用前面给出的术语上下文方法的例子。因此,如果我们计算 SVD,我们将得到这些矩阵:

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

a、b、c 分别对应于 u、s、V^T 的值

现在,如果我们截断,只看第一个矩阵的前 2 列和所有行,即 b 的前 2×2 子矩阵,然后乘以它,我们得到:

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

现在这些值告诉我们的是相似性,所以如果你仔细看,前两行是相同的,这意味着我的前两个单词是相似的,后两个也是,它们产生一个正值。现在有两件事提出了一个问题,我是如何选择 2 的,对于截尾,我们是如何得到较远的值的,因为-1.4142 和 1.7486 很接近,但这两个词似乎不相似。第一个问题的答案是,你可以有任何值,而不是两个值,它只取决于矩阵的大小,你也可以尝试绘制性能图,以获得最佳值。

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

在这里,我们可以看到两种情况,因此最好使用 hit 和 trial。

现在回答第二个问题,我们实际上应该使用我们之前谈到的 PMI 矩阵来获得更好的结果。

最终解码我们的自动编码器

再来看我们的第一个深度学习降维方法。这个自动编码器非常酷的一点是,它基于无监督学习的原理工作,我们稍后会讲到。因此,autoencoder 有 2 层和编码器(duh)和一个解码器。

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

这看起来有点像一个瓶颈(来源)。

在这个模型中,我们可以看到,我们输入计数向量,进行一系列运算,得到一个与向量维数相同的值。实际情况是,你看到的这些层基本上是最小化我的原始计数向量的操作,也就是说,它们正在编码,解码层试图利用瓶颈重新创建原始输入。重建的唯一方法是在我们从瓶颈转移到解码层时使用 back prop。对这种方法一个改进是执行 LDA 以实质上压缩输入向量。

结论:

虽然我们在这里更多地讨论了自然语言方面的事情,但降维也是计算机视觉中的一个问题(这里是),自动编码器仍然发挥着作用(更多信息)。我希望这篇文章已经很好地解释了维度是如何工作的,以及为什么需要降维,但是这仅仅是朝着这个方向迈出的一步。对于自动编码器的工作来说,还有更多的数学降维是不需要的。要了解更多关于自动编码器的信息,请查看致谢部分。

致谢:

[## 大数据降维技术分析——IEEE 期刊杂志

由于数字化,医疗保健、生产等多个行业都产生了大量数据

ieeexplore.ieee.org](https://ieeexplore.ieee.org/document/9036908) [## 无监督特征学习和深度学习教程

到目前为止,我们已经描述了神经网络在监督学习中的应用,其中我们将训练标记为…

ufldl.stanford.edu](http://ufldl.stanford.edu/tutorial/unsupervised/Autoencoders/)

http://proceedings.mlr.press/v27/baldi12a/baldi12a.pdf

词向量和语义

原文:https://towardsdatascience.com/word-vectors-and-semantics-2863e7e55417?source=collection_archive---------49-----------------------

观点挖掘还是情感人工智能

Word2vec 是一个处理文本的双层神经网络。它的输入是文本语料库,输出是一组向量,这些向量本质上是该语料库中单词的特征向量。Word2vec 的目的和用途是在向量空间中对相似词的向量进行分组。它用数学方法检测相似性。它创建向量,这些向量是单词特征的分布式数字表示,例如单个单词的上下文,并且它在没有人工干预的情况下完成。

给定足够的数据、用法和上下文,Word2vec 可以根据单词过去的出现对其含义做出高度准确的猜测。这些猜测可以用来建立一个单词与其他单词的联系,就像男人对于男孩就像女人对于女孩一样。

Word2vec 根据输入语料库中与单词相邻的其他单词来训练单词。它通过两种方式实现,或者使用上下文来预测目标单词,或者使用单词来预测目标上下文,这被称为 skip-gram。

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

它们都是彼此相反的。

CBOW 方法我们有几个输入单词,然后我们的投影基本上是试图预测在给定这些周围单词的上下文的情况下,出现的概率最高的单词是什么。另一方面,Skip-gram 方法需要更长的时间来训练和开发,因为它本质上是在做相反的事情。给定使用自动编码器神经网络投影的单个单词的输入,尝试输出将在该输入单词的上下文周围出现的其他单词的加权概率

我们还必须记住,每个单词都由一个向量来表示。这意味着我们可以使用余弦相似度来衡量单词向量之间的相似程度

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

向量值

那么单词向量是什么样子的呢?因为 spaCy 采用 300 维,所以单词向量被存储为 300 个元素的数组。

注意,我们会看到与 en_core_web_mden_core_web_lg 相同的一组值,

*# Import spaCy and load the language library*
**import** **spacy**nlp = spacy.load('en_core_web_lg')  
*# make sure to use a larger model!*#or*# Import spaCy and load the language library*
**import** **spacy** nlp = spacy.load('en_core_web_md') 
 *# make sure to use a larger model!*doc = nlp(u'The quick brown fox jumped over the lazy dogs.')

doc.vector

这将返回一个大数组。(参见 GitHub 链接中的输出)

识别相似向量

公开向量关系的最佳方式是通过。文档标记的 similarity()方法。

我们知道狮子和猫有一点相似之处,因为它们都是一个大家庭的成员。此外,猫和宠物之间也有关系,因为猫在世界各地大多作为宠物饲养

*# Create a three-token Doc object:*
tokens = nlp(u'lion cat pet') *# Iterate through token combinations:*
**for** token1 **in** tokens:
    **for** token2 **in** tokens:
        print(token1.text, token2.text, token1.similarity(token2)) Output-lion lion 1.0
lion cat 0.526544
lion pet 0.399238
cat lion 0.526544
cat cat 1.0
cat pet 0.750546
pet lion 0.399238
pet cat 0.750546
pet pet 1.0

我们看到我们得到了一些正确的和可理解的结果。

对立面不一定是不同的

意思相反但经常出现在相同上下文中的单词可能有相似的向量。

在这里,我们可以认为喜欢、爱和恨有完全不同的含义,但是如果我们在一个句子中一起使用它们,它是有意义的,并且模型可以识别它。

*# Create a three-token Doc object:*
tokens = nlp(u'like love hate') *# Iterate through token combinations:*
**for** token1 **in** tokens:
    **for** token2 **in** tokens:
        print(token1.text, token2.text, token1.similarity(token2)) Output-like like 1.0
like love 0.657904
like hate 0.657465
love like 0.657904
love love 1.0
love hate 0.63931
hate like 0.657465
hate love 0.63931
hate hate 1.0

向量范数

有时将 300 个维度聚合成一个欧几里德(L2)范数会很有帮助,这个范数被计算为矢量平方和的平方根。这可作为。vector_norm 令牌属性。其他有用的属性包括。has_vector 和 is_oov 或出词汇

比如我们的 685k 矢量库可能没有“ nargle 这个词。要测试这一点:

tokens = nlp(u'dog cat nargle')**for** token **in** tokens:
    print(token.text, token.has_vector, token.vector_norm, token.is_oov) Output-dog True 7.03367 False
cat True 6.68082 False
nargle False 0.0 True

事实上,我们看到“nargle”没有向量,所以 vector_norm 值为零,它被标识为词汇表之外的*。*

向量运算

信不信由你,我们可以通过加减相关向量来计算新的向量。一个著名的例子表明

"queen" - "woman" + "man" = "king"

我们来试试吧!

**from** **scipy** **import** spatial

cosine_similarity = **lambda** x, y: 1 - spatial.distance.cosine(x, y)

queen = nlp.vocab['queen'].vector
woman = nlp.vocab['woman'].vector
man = nlp.vocab['man'].vector *# Now we find the closest vector in the vocabulary to the result of "man" - "woman" + "queen"*
new_vector = queen - woman + man
computed_similarities = [] **for** word **in** nlp.vocab:
    *# Ignore words without vectors and mixed-case words:*
    **if** word.has_vector:
        **if** word.is_lower:
            **if** word.is_alpha:
                similarity = cosine_similarity(new_vector, word.vector)
                computed_similarities.append((word, similarity))

computed_similarities = sorted(computed_similarities, key=**lambda** item: -item[1])

print([w[0].text **for** w **in** computed_similarities[:10]])['queen', 'king', 'kings', 'queens', 'prince', 'lord', 'throne', 'royal', 'god', 'monarch']

你可以在下面提供的我的 Github 库中查看完整的代码。

[## aditya-beri/词向量和语义

通过在 GitHub 上创建一个帐户,为 aditya-beri/词向量和语义开发做出贡献。

github.com](https://github.com/aditya-beri/Word-Vectors-and-Semantics.git)

结论

在这篇博客中,我们学习了如何进一步进行机器学习,并试图从复杂的短语中提取出想要表达的意思。一些简单的例子包括:

  • 数据科学相对容易学。
  • 那是我看过的最好的电影。

然而,像这样的短语会让事情变得更难:

  • 我不讨厌绿鸡蛋和火腿。(需要否定处理)

实现的方式是通过复杂的机器学习算法,如 word2vec 。目的是为大型语料库中的每个单词创建数字数组或单词嵌入。每个单词都被赋予了自己的向量,在这种方式下,在同一上下文中频繁出现的单词被赋予了相互靠近的向量。结果是一个模型可能不知道“狮子”是一种动物,但知道“狮子”在上下文中比“蒲公英”更接近“猫”。

需要注意的是构建有用的模型需要很长时间——训练一个大的语料库需要几个小时或几天——为了我们的目的,最好是导入一个现有的模型,而不是花时间训练我们自己的模型。

这只是对什么是语义和词向量以及它们如何工作的一个小小的窥探。

如有任何疑问和澄清,请随时回复本博客。

Word2Vec 至变压器

原文:https://towardsdatascience.com/word2vec-to-transformers-caf5a3daa08a?source=collection_archive---------3-----------------------

单词嵌入的演变,来自 CS224n 的注释。

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

你的论点是合理的,完全合理

介绍

开发有意义的单词表示是 NLP 成立以来的主要目标之一。在 2010 年代,这一基础性任务一直是该领域进步和创新的主要驱动力之一。在这篇文章中,我将介绍完成这项任务的一些主要方法,以及大大提高我们在不同上下文中捕捉词义和相似性的能力的主要观点。

一袋单词

解决这个问题最简单的方法叫做词汇袋。这种方法为文本中出现的每个单词分配一个唯一的标记,通常是一个数字。因此,例如短语“你的论点是声音,除了声音什么都没有”将被表示为*“1-2-3-4-5-6-4”。*通过这种基线方法,我们可以捕捉到单词之间同一性的概念,也就是说,当一个单词被多次使用时,我们可以识别出来。此外,使用像 Tf-Idf 这样的使用单词包的技术,我们可以在一定程度上成功地测量文档之间的相似性,只要基于哪些单词在文档中使用以及使用的频率。使用像 WordNet 这样的资源,一个支持的字典,我们也可以发现文本中单词的多种含义,并将字典中列为同义词的那些连接起来。但是这种表示本身并没有捕捉到单词的相似性,也没有捕捉到单词在我们的文本中被使用的特定意义。

Word2Vec (CBOW 或 Skip-Gram)

可以说,NLP 在 2010 年代早期的最大发展是 Word2Vec,这是一种无监督的学习技术,用于学习单词的连续表示。在许多方面,Word2Vec 建立在 BoW 的基础上,但它不是为单词分配离散的标记,而是为训练语料库中的每个单词学习连续的多维向量表示。更具体地说,它通过学习预测给定一个中心单词在其周围固定大小的窗口中最有可能出现的单词来做到这一点 (Skip-Gram)

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

Word2Vec 示例

或者通过学习根据上下文单词*(连续单词袋)*预测中心词。像许多其他机器学习技术一样,Word2Vec 使用梯度下降来最小化整个语料库的交叉熵损失,即预测错误单词的概率。实际上,基本思想是在中心词和外部词的任何给定配置下,最大化给定中心词时预测外部词的条件概率。

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

表达式最大化,其中 u_o 是中心词上下文中外部词之一的向量表示,v_c 是中心词的表示。直观上,向量积是线性代数中相似性的度量,因此我们想要最大化当前外部单词和中心单词之间的相似性,该相似性基于中心单词相对于语料库中所有单词的相似性的总和来归一化。指数是用来使一切都为正的。

现在,如果我们想在所有语料库中最大化这种表达,我们需要逐步学习更好地捕捉单词之间相似性的向量。因此,Word2Vec 通过设计来捕捉我们语料库中单词的相似性,这要归功于一个单词在向量空间中与其他单词有多远或多近的概念,即词义的概念。

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

经典的国王-男人+女人=女王 Word2Vec 如何捕捉相似性的直觉。

然而,这种相似性的概念只能带我们到此为止。Word2Vec 的主要问题是,它为一个词提供了一种单一的表示,无论上下文如何,这个词都是相同的。因此,像“*bank”*这样有几个不同含义的词,例如 river bank 和 investment bank,最终会有一个表示,它是没有很好地表示任何一个含义的平均表示。

上下文单词嵌入(ELMo)

基于单词出现的上下文来证明每个单词有多个表示是上下文单词嵌入背后的核心思想。这个想法是使用 RNN 语言模型来实现的,该模型以无监督的方式类似于 Word2Vec 来训练。更具体地,我们使用 RNNs 来预测,给定句子中的当前单词,下一个单词。我们使用这些网络是因为它们有能力在隐藏状态下捕获和维护长期依赖关系。

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

隐藏状态(红色)可以保持关于句子中前一个单词的信息

其思想是,在输入当前单词后,我们可以将隐藏状态连接到通常的 Word2Vec 表示,以维护当前单词和过去上下文的信息。

第一个实现这个想法的系统是 Peters 和 co .的 TagLM。

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

TagLM 使用预训练的双 LSTM 语言模型来产生单词嵌入的“上下文部分”,该单词嵌入连接到 Word2Vec 矢量或更复杂的字符级 CNN/RNN 生成的单词表示。这种表示现在是新的嵌入,有效地取代了 NLP 管道中的 Word2Vec 或 GloVe 向量。

ELMo 嵌入的工作方式非常相似,主要区别在于 ELMo 对预训练语言模型使用两层双 LSTM,而嵌入连接是一种可学习的,在微调期间,两层的组合将针对特定任务进行优化。ELMo 还完全抛弃了 Word2Vec,只依赖字符级的 CNN/RNNs 作为单词表示的第一部分。

变形金刚(伯特,GPT)

训练单独的语言模型以产生更好的上下文单词表示的想法已经被证明在许多 NLP 任务中非常成功地改善了 SOTA,但是 RNN 语言模型由于其循环、顺序的性质而倾向于训练缓慢并且非常难以并行化。因此,在 2017 年,Aswani 和他的同事开发了一种非递归替代 RNNs 的方法,其核心是变压器块。

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

变压器架构的编码器

Transformer 的主要特点是它使用注意力(seq2seq 架构中有助于翻译对齐的概念)来捕捉句子中单词之间的关系,类似于卷积的方式。就像卷积一样,我们可以让多个注意力头来计算每个单词应该将注意力集中在哪里,以及注意力所代表的关系。然而,注意力不同于卷积,因为它捕捉空间中单词之间的相似性,在该空间中,不同头部的权重矩阵投射它们的表示。为了捕捉更远的依赖性,类似于卷积,我们可以堆叠多个变压器块。

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

解码器模块的工作方式有些不同,但是你所需要的是引起注意的图像纸张让一切变得更加清晰。

BERT 使用 transformer block 来训练一个使用屏蔽技术的语言模型,其中系统的任务不是猜测下一个单词,而是猜测句子中屏蔽掉的一个单词。

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

这样,它能够使用整个上下文进行预测,而不仅仅是左上下文。

这些笔记是对 CS224n Stanford 课堂第 13 和 14 课的最基本的总结。这些材料的学分属于克里斯·曼宁教授和这门课的助教。
更多资料可在
课程网站 YouTube上获得。

构建神经网络时,更聪明地工作,而不是更努力

原文:https://towardsdatascience.com/work-smarter-not-harder-when-building-neural-networks-6f4aa7c5ee61?source=collection_archive---------16-----------------------

用一个简单的例子来说明神经网络的设计原则

应用数学中的一个基本技巧是找到一个坐标变换,将一个困难或不可能的问题转化为一个简单的问题。也许我最喜欢的例子是圆的方程。如果我们在笛卡尔坐标中写下单位圆的方程,我们可以将圆的几何概念表达为隐函数:

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

当我们试图通过求解 y 得到一个显式表达式时,情况会变得更糟。

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

现在我们必须从平方根的两个分支(圆的上半部分和下半部分)拼凑这个简单的函数。超越美学使用这种圆的表示法会使我们看不到一个完美的圆所固有的几何简单性。相比之下,如果我们用极坐标来表示同样的数学对象,那么单位圆就变得非常容易处理。

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

点的极坐标。

在极坐标中,我们的单位圆采用简单的形式 r=1,θ ∈ [0,2π]。所以在极坐标中,圆是矩形的(高=1,宽=2π)。笛卡尔坐标是矩形的自然坐标,极坐标是圆形的自然坐标。在本文中,我将应用这种思路来构建简单的神经网络,并构建一个说明选择正确坐标的重要性的示例。

曲线拟合的人工神经网络

如果你正在阅读这篇文章,那么你可能听说过人工神经网络(ANN)。在这次讨论中,我们将集中讨论最简单的人工神经网络,称为前馈网络。简而言之,人工神经网络只是通过将简单的非线性函数(层)链接在一起而构建的网络(函数)。

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

前馈神经网络

通过将(许多)这些简单的层链接在一起,通用逼近定理告诉我们,我们可以将任何(好的)函数表示为具有有限深度和宽度的人工神经网络。在开始的极坐标例子中,这就像说我们可以在极坐标中写下(x,y)平面中的每一点(没有洞)。这是一个很好的必要属性,可以确保我们在改变坐标时不会错过任何东西,但是它没有告诉我们两件事:

  • 如何找到实际坐标?
  • 如果这是表达信息的好方法。

对于人工神经网络来说,“坐标是神经网络的参数(神经网络中每一层的权重和偏差)。对于神经网络,我们没有快速的数学公式来找到这些坐标,而是使用优化来最小化损失函数。损失函数测量我们指定的函数和训练数据之间的距离。

让我们看一个简单的例子,用神经网络来构建正弦函数的近似值。

使用神经网络拟合正弦函数

让我们应用这种强大的函数逼近方法来构建简单函数 sin(x)的神经网络版本。我将使用用 Julia 编写的奇妙的神经网络库 Flux。这个软件包为构建神经网络提供了一个非常简单而强大的框架。它还增加了很少的额外语法来构建神经网络,并让我们专注于基本的构建模块。我将在文章中包含一些代码片段。完整代码请查看github repo

下面的代码使用一个标准的非线性函数 tanh 构建了一个具有两个隐藏层的简单网络。

 ann = Chain(Dense(1,20,tanh),Dense(20,20,tanh),Dense(20,1)); 

我们可以把这个神经网络想象成:

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

学习正弦函数的简单前馈神经网络

上图显示我们有一个输入和输出值,中间有两个层。这些层被称为隐藏层,因为如果您仅跟踪输入和输出,它们是不可见的。回想一下,该图用于表示某个函数族,其中通过固定参数来指定特定的成员。

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

上述神经网络的函数形式。参数由两个权重矩阵 W 和偏置向量 b 给出。参数 C 给出线性输出层的截距。

好,所以我们希望,我们可以用这个函数族中的某个成员来近似表示函数 sin(x)。为了尝试找到最接近这个的成员,我们应该使用一些训练数据来最小化损失函数。

function loss(x, y)
    pred=ann(x)
    loss=Flux.mse(ann(x), y)
    #loss+=0.1*sum(l1,params(ann)) #l1 reg
    return loss
end[@epochs](http://twitter.com/epochs) 3000 Flux.train!(loss,params(ann), data, ADAM())

拟合我们的神经网络后,我们可以看到它对训练数据和一组测试数据有多好。

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

具有 tanh 非线性的前馈神经网络适合 3000 个时期的训练数据,如蓝色圆圈所示。测试数据显示为绿色十字。

显然,这种神经网络在捕捉信号的基本周期特性方面表现不佳。对测试数据(绿色)的异常差的概括突出了这一点。这不仅仅是我的网络

哪里出了问题?经常被引用的通用逼近定理告诉我们,我们可以使用有限深度和宽度的前馈神经网络来表示这个函数。然而,正如我经常重新发现的——有限仍然可以非常大。仅仅因为存在一些权重和偏差,可以用来从典型的神经网络组件中构建这个函数,并不意味着我们可以很容易地找到它们。

我们可以尝试使用蛮力,建立更大的网络或训练更长的时间,等等。然而,一个非常简单的改变会在很短的时间内给我们一个近乎完美的近似值。

更好的主意

给定这个数据,我们可以看到它是周期性的。现在,我们还没有将那条信息包含在我们的神经网络设计中。不使用双曲正切非线性函数,让我们使用正弦函数。这样,第一层的输出将是周期性的。

下图显示了该系统更好的近似网络的想法。

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

具有正弦非线性神经网络。

以函数形式写出,我们使用的函数族采用以下形式:

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

其中 Q 是隐藏层中神经元的数量。这在 Flux 中很容易做到:

Q = 20;
ann = Chain(Dense(1,Q,sin),Dense(Q,1));

让我们试试这种新型号,看看能否找到更好的型号。

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

更好的神经网络基础

这是一个更好的模型,因为它抓住了周期性的本质。然而,请注意,我们仍然在训练和测试数据上略有偏差。这是因为我们的模型实际上仍然有一些不需要的额外自由度。我们可以通过对参数应用一些正则化来解决这个问题,但在这种情况下,我们可以求助于一些非常酷的数学来寻求解决方案。

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

让·巴普蒂斯特·约瑟夫·傅立叶 1768–1830

上述神经网络的函数形式非常接近于傅立叶级数的形式。傅立叶级数是一个基(一组坐标),可以用来描述有限域[a,b]上所有的好的函数。如果一个函数是周期的,那么有限域上的描述可以扩展到整条实直线上。因此,傅里叶级数常用于周期函数。

傅立叶级数采用以下形式:

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

我们的神经网络和傅立叶级数之间的唯一区别是,我们的神经网络允许权重 W 变化,而傅立叶只有 A 系数和偏差(b)项,可以选择它们来拟合函数。

在 Flux library 中,从可训练的参数集中移除第一层中的那些权重参数是足够容易的。通过将它们设置为整数值,我们实际上可以创建一个神经网络,其中傅立叶级数。

# Fourier Series ANNQ = 20;
ann = Chain(Dense(1,Q,sin),Dense(Q,1));function loss(x, y)
    pred=ann(x)
    loss=Flux.mse(ann(x), y)
    return loss
endopt = ADAM()ps=params(ann)for j=1:20
    ann[1].W[j]=j
end
delete!(ps, ann[1].W) #Make the first layers weights fixed[@epochs](http://twitter.com/epochs) 3000 Flux.train!(loss,ps, data, opt)

以下是使用 sin(x)函数和傅立叶人工神经网络的拟合:

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

傅立叶人工神经网络方法推广良好。

如果你想知道这种方法对其他更复杂的周期函数也同样适用。考虑一个混有一些高次谐波的周期信号。

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

更复杂周期信号的傅立叶级数神经网络的第二个例子。

一些外卖课程

这个简单的例子为 ANN 的工作方式以及我们如何改进我们的模型提供了一些重要的见解。

  1. 你的神经网络的设计会对结果产生巨大的影响。问题和领域的具体知识会产生巨大的影响。
  2. 确保你所知道的关于问题的一切都被传送到神经网络。这里我们看到,告诉函数产生一个周期信号比增加网络规模或训练时间做得更多。
  3. 按照上一课的思路,领域知识很重要。
  4. 应用数学家和物理学家已经研究函数逼近问题很长时间了。傅立叶级数在 19 世纪 20 年代被发现用来解决热传导问题。这些技术可能值得您花时间去学习,即使您并不关心应用程序。
  5. 用人工神经网络计算傅立叶级数是非常愚蠢的。上世纪十大算法之一就是为了这种计算而发明的。重点是展示一个小小的改变,加入更多的领域知识,是如何极大地改善结果的。

关于构建更智能的神经网络,我推荐查看内森·库兹的作品

担任数据科学顾问

原文:https://towardsdatascience.com/working-as-a-data-science-consultant-e626669ab72b?source=collection_archive---------17-----------------------

具有企业意识的数据科学家的职业战略

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

资料来源:Freepik

在《哈佛商业评论》写道数据科学是“21 世纪最热门的工作”几年后,许多年轻人才现在被这一利润丰厚的职业道路所吸引。此外,大公司的高层管理人员现在几乎所有的重要决策都使用数据驱动的方法和分析工具。

随着数据驱动的决策和自动化趋势的发展,许多大公司正在采用各种数据科学工具来生成可行的建议或自动化其日常运营。咨询行业的主要功能是为其客户,主要是跨国公司,提供外部的合格人才。这些全球性公司遵循其业务增长的战略路线图,通常通过增加收入或有效管理成本来实现。为了实现这些目标,他们需要在其业务的不同领域采用人工智能和大数据技术。

另一方面,这些全球公司中有许多不一定是拥有大型数据科学团队的科技公司。因此,他们需要将数据科学开发案例外包给长期或短期的咨询公司。咨询公司和他们的客户以短期和长期项目的形式参与,这些项目可以持续几个月到几年。因此,咨询公司通过招募和培训顶级数据科学人才来满足客户的需求,并将这些人才分配到咨询项目中。

咨询是初级数据科学家的典型职业道路,他们希望获得不同行业的广泛项目经验。对于那些在大客户的许多不同项目中工作的数据科学家来说,在咨询行业工作通常是积累不同领域业务知识的一个很好的选择。通过这些项目,人们可以了解寻求数据科学解决方案的企业决策者和大公司高层管理人员的心态。人们甚至可以期待与客户的董事总经理级别的员工进行互动,向他们提供他们新的花哨的分析仪表板。

咨询公司的数据科学家可以担任许多不同的角色,具体取决于他们的能力或工作部门。咨询公司通常做两种重要的面向客户的工作;(a)在交付项目中担任数据科学家,( b)开展业务开发和销售活动。前者通常通过与软件开发、数据工程或业务分析团队合作来完成。数据科学家的日常工作包括了解客户的需求、创建数据管道、探索性数据分析、创建仪表板、构建预测模型、在企业软件中部署模型,以及将见解传达给利益相关方。对于后者,数据科学家通常从事高层次的问题解决、用例定义、撰写提案以及向潜在客户进行技术演示。

一名成功的数据科学顾问需要广泛的技能,包括领域知识、商业敏锐度、分析思维和解决问题、团队合作和项目管理、沟通和演示、机器学习、大数据和软件开发。上述技能也适用于数据科学咨询工作的面试。简而言之,你必须证明你是一名合格的数据科学家,能够使用广泛的技术。然而,你必须能够跳出框框思考,对业务有坚实的理解,并尝试利用数据科学来实现。

咨询公司也有自己的文化。工作量通常是很大的,需要敏捷和以结果为导向的工作方式才能按时交付价值。咨询工作还包括大量的旅行和与不同客户的会议。商务旅行可能一周长达 4 天,这意味着顾问通常从周一到周四不在家。与其他公司的竞争也非常激烈,有时甚至在同一家公司内部也是如此。所以你也必须为一些公司政治做好准备。在咨询公司里,带着正确的期望前进是这类职业成功的关键。你必须知道你在咨询公司工作期间得到了什么,以及为此付出的代价。

有些人选择在进入咨询行业之前、期间或之后学习工商管理硕士(MBA)。尽管考虑到 MBA 学位对你的商业头脑有用,但如今,在一些大型咨询公司工作相当于上商学院。在咨询行业工作有助于你更好地了解业务,并在不同的业务流程中获得实践经验。

也有机会成为特定领域的顾问。例如,如果你做过营销分析方面的项目,并且对这个能力感兴趣,你可以选择营销分析方面的专家路径。作为特定领域的顾问,通常在一些大型咨询公司工作,但主要是在高级管理层,他们有主题专家或能力领导。另一个选择是做一名自由职业顾问,或者在你是专家的特定领域开办你的咨询机构。

总之,做数据科学顾问对你的职业有很多好处。你可以和世界上最成功的公司一起工作。你可以向周围最好的专家学习,你还可以受益于与大型咨询公司合作,这些公司为你提供了无数发展职业和技能的机会。你还必须注意到,所有这些好处都是以在高压环境下的高强度工作负荷为代价的。如果你已经为这一切做好了准备,进入数据科学咨询行业可能是你职业生涯中走向成功的最重要的一步。

关于作者:

Pouyan R. Fard 是 Fard 咨询公司的首席执行官&首席数据科学家。Pouyan 在数据科学、人工智能和营销分析方面拥有多年的公司咨询经验,从初创公司到全球公司。他曾与医药、汽车、航空、运输、金融、保险、人力资源和销售&营销行业的财富 500 强公司合作。

Pouyan 也在指导活跃在大数据行业的初创公司和人才。他的热情是通过职业培训培养下一代数据科学家,并帮助他们找到数据科学领域的顶级工作机会。

Pouyan 已经完成了关于消费者决策预测建模的博士研究工作,并对开发机器学习和人工智能领域的最先进解决方案保持兴趣。

作为一名机器学习工程师

原文:https://towardsdatascience.com/working-as-a-machine-learning-engineer-a364545ae93c?source=collection_archive---------39-----------------------

人工智能和数据人才的职业战略

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

资料来源:Freepik

数据科学是一个跨学科的领域,有许多相关的领域,如机器学习或大数据。随着行业的发展,数据科学相关领域之间的相似性和区别也变得更加具体。机器学习工程是一个与数据科学非常接近的领域,在一些公司中,这两种职业道路几乎没有区别。机器学习工程到底是什么?

机器学习工程是一个复杂的软件工程领域,专注于开发智能软件,这些软件可以利用人工智能和机器学习技术的力量自动化类似人类的任务。与数据科学家相比,机器学习工程师在软件工程方面的工作多于分析方面。机器学习工程师将主要致力于将人工智能集成到软件解决方案中。现在机器学习工程师工作的最突出的领域是自动驾驶和云计算。

在 2010 年代,机器学习研究人员的注意力回到了深度学习算法。此外,训练深度学习算法变得更加可行,因为云计算技术以相对较低的价格为个人和企业提供了更高的计算和存储能力。此外,随着大数据时代社交媒体的出现,许多训练数据集可供专家们用来增强他们的机器学习模型。

对机器学习工程师的需求一直在稳步增长。几乎所有的公司,无论是公司还是初创公司,都希望在业务中采用人工智能技术。仅在 2015 年和 2018 年之间,机器学习工程职位发布数量就增长了 344%。这一增长率使机器学习工程师或人工智能专家成为 2020 年最热门的职位之一。
成为高评价的机器学习工程师有几个要求。首先,你需要计算机编程技能,如 Python 或 R,软件工程技能是绝对必须的。可靠的概率统计知识以及机器学习算法是必要的。人们需要知道如何训练机器学习模型,并将训练好的模型用于预测任务。机器学习算法应用于广泛的问题,知道哪个模型是解决每个问题最有效的模型是这项工作的关键。

机器学习框架的实践经验确实至关重要。现在,几乎所有的机器学习算法都已经在一个库或框架中实现了。一个庞大的学术和行业专家社区为传统的机器学习和深度学习算法提供了现有的资源。现有技术,如 scikit-learn、Tensorflow、Keras 和 PyTorch,以及亚马逊、微软和谷歌等大型科技公司的云解决方案,使机器学习工程师的工作变得更加简单。然而,了解在特定用例中采用这些技术的最佳实践非常重要,不容忽视。

此外,训练模型通常遵循特定的数据获取、数据准备、探索性数据分析和模型性能评估流程。为此,机器学习工程师需要完成与数据工程和数据科学相关的任务。经过训练的模型通常需要部署在操作软件和具有高复杂性级别的现有架构中。因此,软件工程的实际知识也是必需的。

从数据科学过渡到机器学习工程相对简单。一个人需要展示工程思维和快速可靠地交付实际结果的能力。这样的候选人也需要有一些领域的知识。毕竟,交付满足业务需求的有用产品是数据科学领域每项工作成功的关键。

最后,一些新兴领域已经变得如此突出,以至于它们变得独立于数据科学和机器学习工程领域。这些领域就像计算机视觉工程、语音技术工程和自然语言处理工程。

关于作者:

Pouyan R. Fard 是 Fard 咨询公司的首席执行官&首席数据科学家。Pouyan 在数据科学、人工智能和营销分析方面拥有多年的公司咨询经验,从初创公司到全球公司。他曾与医药、汽车、航空、运输、金融、保险、人力资源和销售&营销行业的财富 500 强公司合作。

Pouyan 也在指导活跃在大数据行业的初创公司和人才。他的热情是通过职业培训培养下一代数据科学家,并帮助他们找到数据科学领域的顶级工作机会。

Pouyan 已经完成了关于消费者决策预测建模的博士研究工作,并对开发机器学习和人工智能领域的最先进解决方案保持兴趣。

解决数据科学设置中的问题

原文:https://towardsdatascience.com/working-out-the-kinks-in-your-data-science-setup-3cc82e02f31d?source=collection_archive---------52-----------------------

数据科学团队面临的一些常见问题指南

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

JESHOOTS.COMUnsplash 上拍照

这是本博客关于 数据科学团队设置 的姊妹篇

正如许多实践中的数据科学家会告诉你的,将数据科学有效地集成到组织中的道路绝不是一帆风顺的。围绕数据科学可以提供什么有很多宣传和期望,但当处理数据问题、错误和计算限制的日常现实出现时,这些希望可能会动摇。这个博客是数据科学家或数据科学团队可能遇到的一些常见问题的指南,并就如何处理这些问题提出了一些建议。

我整天都在寻找和管理数据

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

你需要的数据就在这里的某个地方……(照片由 Ashim D’Silva 在 Unsplash 上拍摄)

这是最常见的问题之一,表明数据科学家在被聘用时没有充分考虑他们取得成功所需的环境和设置。对大多数组织来说,管理数据是一项持续的挑战,可靠地确保正确的数据在正确的时间位于正确的位置对于有效的数据科学至关重要。因此,要解决这个问题,我们通常需要查看我们的数据工程设置。他们足够多吗?他们有时间关注你的需求吗?

人们通常认为数据工程师和数据科学家的比例是 1:2,但实际上这取决于你需要的数据有多杂乱。如果一切都已经很好地组织成易于查询和可靠的数据存储,那么你可能只需要一个数据工程师和几个数据科学家。如果您的数据分散在各种 prem 和 cloud 存储中,并且混合了一些 excel 电子表格和 SQL server 实例,那么您可能需要更高的 1:1 比率。

我建立了这个很棒的模型,但是利益相关者讨厌它

这可能是最令人沮丧的问题之一,因为当一个有影响力的利益相关者转身说:“不”时,几周甚至几个月的工作就会化为乌有。当在范围界定过程中遗漏了关键点或需求,或者在构建算法时丢失了关键点或需求时,往往会发生这种情况。

因此,密切关注您的产品管理流程是理解这些问题发生的原因并防止其再次发生的关键。我说产品管理流程是因为虽然可能是产品管理失误,但也可能是数据科学或工程团队没有遵守产品要求,或者没有充分听取利益相关者的意见。风险承担者也会在项目接近尾声时发明新的需求,或者在项目开始时忘记提及关键点。

避免这些问题的几种方法是:

  • 确保在项目开始时清楚地记录了算法要求,并且所有利益相关者都已经审查并签署了该文件
  • 在可能的情况下,尽快在主要利益相关者面前获得算法输出的第一个版本。这不必是花哨的,或者使用最终算法将使用的相同模型和功能集。目的是展示“这大致就是输出的样子”。围绕初稿的讨论往往比抽象地讨论算法更有成效。

我整天都在追虫子!

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

照片由玛利亚·特内娃Unsplash 上拍摄

不幸的是,bug 是生活中的事实,虽然我很想给出一个“魔术子弹”式的技巧来避免它们,但恐怕我不得不让你失望了。然而,错误不应该是数据科学家处理的全部,如果你发现你总是在寻找错误,所以你的交付速度变慢了,那么可能是时候看看工程设置了。

部署 ML 算法可以通过多种方式完成,从数据科学家自己构建整个管道,使用现成的或内部的 AI 平台,到工程团队将数据科学家的规范直接写入软件本身。错误的性质也有很大的不同,从零星的怪异到全面的系统故障。也就是说,围绕错误数据科学的一些常见主题仍然值得讨论。

数据科学家在没有必要培训/技能的情况下编写生产代码

这并不是说数据科学家不应该编写生产代码,许多人可以而且做得很好。但是,尽管数据科学培训确实涉及大量编码,但它往往不会专注于培养伟大的软件工程师。所以如果你的算法在不断地表演,它可能需要有更相关的训练和经验的人来重写。

算法不太适合工程现实

你可以建立世界上最好的产品推荐算法,但如果它需要 20 秒才能呈现在页面上,那么客户就会成群结队地放弃这个网站。确保算法在它将运行的环境中工作良好,而不仅仅是在训练环境中,这是至关重要的,也是机器学习工程师的角色在过去几年中变得突出的原因。这里的修复可以简单到删除依赖于错误数据存储的特性,也可以复杂到重新访问算法类型和训练/评分方法。

我整天都在回答问题

算法和数据产品产生问题。效果如何?它在特定的环境下表现如何?我们应该探索不同的要素或数据集吗?这些可以来自涉众,也可以来自团队成员,因为他们寻求发展和改进算法/产品。在这里,分析的技能得到了最好的利用:快速查询、数据可视化和讲故事。

当一个算法吸引了许多相关的问题,但手头的分析资源太少(或没有)来回答这些问题时,就会出现一个常见的问题。这往往会将分析工作推给其他团队成员,如产品、数据科学和工程,从而减慢交付速度(通常不是他们的核心专业知识)。这可能会导致一个矛盾的情况,一个已知的问题没有得到解决,因为团队太忙于回答有关它的问题!

需要大量的迭代来使事情正常工作

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

斯蒂夫·约翰森Unsplash 上拍照

任何算法或产品都需要一些迭代来解决问题并真正实现跨越,但这应该是一个提炼广泛好的解决方案的问题,而不是不断地让事情偏离正轨并定期回到绘图板。客户和利益相关者只有这么多的耐心。

将这类问题完全归咎于产品管理可能很有诱惑力,尽管这可能是问题的一部分,但仅仅关注这一点可能会错过更广泛的问题。虽然产品管理确实负责选择正确的工具和方法来解决给定的问题,但要使该解决方案真正发挥作用,构建该解决方案的数据科学家和工程师还需要对问题和背景有详细的了解,并彻底理解为什么选择了特定的解决方案。

当数据科学家/工程师和他们正在构建的业务或问题领域之间存在太大差距时,事情就会出错。这里有几个原因:

  • 项目中人员的频繁轮换
  • 不允许数据科学家和工程师参与解决方案设计和范围界定流程的流程
  • 数据科学家和工程师没有时间深入了解他们所服务的客户或利益相关方。

这些问题在概念上都不难解决,但在流程和设置上做出改变可能需要时间和说服。不过,这是值得的,伟大的产品和算法是由经验丰富、知识渊博的人构建的。相反,数据科学家和工程师只是“做他们被告知的事情”,是脱离和平庸软件的配方。

摘要

在将机器学习部署到组织中时,确保您始终处于有效工作的位置,并且不被不属于关键数据科学家技能集的任务偏离,这可能是一个持续的挑战。我已经概述了如何思考这些问题,但是实际的解决方案将取决于你工作的组织的环境。本质上,每当你持续遇到阻碍你完成工作的问题时,不要只是用头撞它们,而是后退一步,看看更广泛的背景,以及你可能需要更多帮助或不同方法的地方。祝你好运!

作为数据科学家远程工作

原文:https://towardsdatascience.com/working-remote-data-science-e2f392776d1a?source=collection_archive---------22-----------------------

如何让过渡变得更容易的 5 个技巧

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

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

两个月前,我离开了 DC 的公寓,准备搬回家住几个月。节省房租、与老朋友重新联系、每天远程工作的想法确实很诱人。从那以后,我了解了作为一名数据科学家远程工作的现实(有好有坏)。

根据我的经验,尤其是新冠肺炎目前的情况,我想分享我所学到的,并为那些适应远程工作生活方式的人提供一些建议。

有利的方面

1.在家工作的舒适

首先也是最重要的——我每天都穿着便装去上班。再也不用担心干洗或每天早上挑选商务休闲装;我起床,穿上那天我想穿的*,花更多的时间专注于我的实际工作。*

我的环境也变了。我的整个“办公室”现在是一个开放的工作空间。我可以坐在沙发上、高桌旁、书桌旁,甚至是我的门廊上,这取决于我一天的心情。在家工作时,你可以获得“开放式办公室”设计的所有好处,而没有“开放式聊天”的缺点。

2.我的重点已经转移到更技术性的工作上

正如我在之前的文章中所写的,作为数据科学家,我们工作的很大一部分是与我们的客户和利益相关者互动。过去几年,我一直在平衡客户交付和技术工作,但最近决定将我的重点更多地转向数据科学的技术方面。

远程工作给了我这个机会。客户交付的责任已经转移到我团队的其他成员身上,这使得我的重点从解释和翻译业务需求转移到交付分析、可视化和模型。

3.我的工作效率和专注度更高了

我还发现,我需要参加的非必要会议的数量大幅下降。我和我的团队不会默认让我参加每个会议,而是在让我参加之前,会仔细考虑我是否有必要参加。

这导致我的注意力和精力空前高涨。我不仅把重点转移到了更具技术性的工作上,而且一整天都没那么分心了。当我即将完成我花了一个小时制作的模型时,不再有“路过”或被拍肩膀。我的时间不受干扰,这使我能够在工作日始终保持专注并完成工作。**

不利之处

1.分享想法变得越来越难

远程工作时很难与团队成员协作。我的工作方式是在团队会议上跳起来,抓起白板,和我的团队一起头脑风暴。我发现这是最有收获也是最有效率的工作方式。现在,我不得不使用虚拟白板来代替,或者在不使用视觉辅助的情况下更清楚地交流我的想法。

2.知道什么时候该休息会很困难

机会很难获得。相反,很难知道什么时候该休息了。是吃午饭的时候了吗?我怎么会知道?没有社交指标,如聊天增加、同事起身离开等。唯一知道的方法就是盯着时钟。

3.总有一种证明我在线的感觉

我经常担心回到电脑前展示“我有多努力”,而不是享受 5 到 10 分钟的咖啡休息时间或与同事快速聊天。我总是担心团队成员会想“他在线吗?”或者“他只是在浏览亚马逊、阅读新闻,还是在 Instagram 上发布模因?”。当然,这很愚蠢——任何数量的员工都会在某一天这样做,甚至在办公室里。但是,我仍然觉得远程工作时会受到更多的审查。

简化过渡的技巧

1.建立日常生活

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

照片由我的生活日志Unsplash

建立一个早晨的习惯对我帮助很大。鉴于从准备工作和通勤中解放出来的时间,我现在有时间在早上做我想做的事情——醒来,享受我的咖啡,沉思,写作,慢慢过渡到我的工作心态,然后在上午 9 点登录。这一点,加上能够专注于手头的任务,每天按时下班,让我精神上重新充电,准备好每天早上开始新的一天。

我推荐顶部空间用于冥想,而大步用于常规/习惯追踪。另外,顶空是在当前新冠肺炎危机期间提供的 免费内容

2.设置边界

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

照片由狄龙在 Unsplash 上摇动

一天之内,在你的沙发上、餐桌上和门廊上工作是很棒的。这在一两周内很棒,但是我开始意识到我需要把我的工作空间和我的生活空间分开。幸运的是,我有一间空房可以用作办公室。

试着在你的房子里找一个特定的房间或者去咖啡店。你不必每天都这样做,但是把它混合起来,试着把你的工作和生活分开是很重要的。设定心理界限也很重要。我已经把每天下午 5 点完全关机列为优先事项。令人惊讶的是,这比我在办公室工作时容易多了。

3.关闭 Skype

我发现这非常有帮助。关闭 messenger 可以减少分心和虚拟的“路过”。我觉得 Skype 更多的是用于社交,而不是做生意,这一点在远程工作时似乎被放大了。关闭 Skype 也消除了持续在线的焦虑。如果你一直开着 Skype,你会有一种必须保持状态不空闲的感觉,这会导致很多焦虑,即使你在做一些像喝杯咖啡这样简单的事情。

我最近完全关闭了 Skype,转而使用群发短信和微软团队,这带来了同样的易用性,但压力却小了很多。这也引出了我的下一个技巧…

4.使用正确的协作工具

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

万花筒Unsplash 上拍摄的照片

幸运的是,我们生活在一个有工具帮助我们远程工作的世界。我最喜欢的一些是 Zoom微软团队、GitHub/git lab

开始的时候,我对使用视频聊天有些犹豫,但是我很快就喜欢上了它。没有什么可以替代面对面的交流,但是 Zoom 视频聊天让我从整天盯着电脑屏幕中解脱出来,坦白地说,在过去的几周里,它变得非常有趣。

Teams: 类似于 Slack,但有微软产品的全力支持,Teams 是一个很棒的应用程序,允许你在远程工作时进行协作、共享文件、安排会议,以及做你需要的几乎任何事情。我绝对喜欢它。

GitHub/GitLab: 如果你在一个数据科学团队,你应该使用 GitHub 或 GitLab 进行代码协作。这两个平台都有很好的工具用于虚拟代码审查、聊天/评论和其他形式的虚拟协作。而且,GitHub 刚刚推出了他们的手机 app !(最后)

5.保持沟通不变

我的最后一点建议很简单:与你的团队保持开放的对话。正如本文所示,过渡到远程数据科学是一个过程。让你的团队了解什么对你有效,什么可以改进,并确保你也在倾听他们的意见。解决所有问题可能需要几周时间,但最终,远程工作的好处是值得的。**

感谢阅读!如果你想更新我正在写的东西、我正在构建的辅助项目或我觉得有趣的文章,请随时加入我的时事通讯— 【有抱负的数据科学家】 —或关注我的 博客

使用 3D 数据— fastai2

原文:https://towardsdatascience.com/working-with-3d-data-fastai2-5e2baa09037e?source=collection_archive---------50-----------------------

了解 fastai 数据块并创建构建块以处理深度学习应用程序的图像序列

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

奥拉夫·阿伦斯·罗特内在 Unsplash 上拍摄的照片。

介绍

处理 3D 数据或图像序列对于许多应用程序都很有用。假设您正在处理 3D 医疗数据、视频数据、卫星图像的时间序列,甚至是一幅更大图像的几个片段。如果你正在处理这样的问题,除了简单的基于图像的问题之外,你还需要一些额外的考虑。如何对 3D 数据进行增强?这些转换应该应用于图像方式还是序列方式,或者两者都应用?如何洗牌训练数据?是否应该保留序列顺序?这些问题的答案取决于问题的类型。然而,拥有一个能够适应所有这些不同可能性的框架是有用的。fastai 是模块化的,非常通用,这使它成为深度学习实践者和研究人员非常有用的工具。

在这个故事中,我将从 fastai 数据块的一个基本例子开始——创建数据加载器的构建块。然后,我将展示如何编写附加功能,以允许使用数据块创建包含 3D 数据的数据加载器,包括图像序列的扩充。

这个 Kaggle 内核上提供了一个工作示例。

fastai 数据块

下面的代码显示了一个典型的基于图像的数据集的 fastai DataBlock 类的示例。如果你是 fastai 的新手,你可以在 fastai 文档中找到更多的例子。

dblock **=** DataBlock(blocks     **=** (ImageBlock, CategoryBlock),
                   get_items  **=** get_image_files,
                   get_y      **=** label_func,
                   splitter   **=** RandomSplitter(),
                   item_tfms  **=** Resize(224),
                   batch_tfms **=** aug_transforms())
  • —该参数接收一个元组,该元组定义了数据块的输入类型和目标。在这种情况下,我们有 ImageBlock 和 **CategoryBlock,**因此 DataBlock 需要图像输入和分类目标——顾名思义。
  • get_items —该参数接收一个返回项目列表的函数。在这种情况下, get_image_files 简单地返回所有图像文件的路径是一些目录和子目录。
  • get_y —该参数接收一个定义标签的函数。在这种情况下是相应项目的类别。 get_x 也可以用在一些情况下,我们稍后会看到。请记住, get_xget_y 接收 get_items 的输出,并分别定义 ImageBlockCategoryBlock 将接收哪些信息。在本例中,不需要 get_x ,因为 get_image_files 已经返回了图像的列表路径,这是 ImageBlock 所需要的。
  • 拆分器 —用于指示如何将数据拆分为训练集和验证集。
  • item_tfms —用于定义应用于 CPU 上每个图像的变换。
  • batch_tfms —用于定义应用于 GPU 上每个批处理的转换。

如您所见,这种结构非常灵活,几乎可以处理任何类型的数据,只要相应地定义了每个参数。对于更高级的应用,可能需要定义自定义块类型和函数来创建批处理。处理 3D 数据就是这种情况。然而,正如我将在下面的六个步骤中展示的,一旦你熟悉了数据块的结构,这个过程就相当简单了。

步骤 0。数据源

在这个例子中,我将使用 MNIST 的数据。我将从 0 到 9 的数字序列定义为我们的图像序列——这使得调试更加容易。下表显示了数据帧的组织方式。

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

数据帧的例子。作者创造的形象。

数据框架中的列有:

  • 文件 : 每个图像的路径;
  • sequence_id :每个序列的唯一标识符;
  • sequence_order :序列中元素的顺序——在这种情况下,对应于从 0 到 9 的数字;
  • 标签:每个序列的目标标签。

第一步。定义图像序列块

为了定义一个图像序列块,我使用了以下代码:

class **ImageSequence**(Tuple):
    *@classmethod*
    def create(cls, image_files): 
        return cls(tuple(PILImage.create(f) for f in image_files))

def **ImageSequenceBlock**(): 
    return TransformBlock(type_tfms  = ImageSequence.create,
                          batch_tfms = int2float)
  • 首先,创建一个图像序列。它只是一个元组,包含 image_files 列表中给出的所有图像。
  • 那么 ImageSequenceBlock 只是一个使用 ImageSequence 返回 fastai TransformBlock 的函数。这是对图像块的 fastai 代码的简单改编。

第二步。定义获取项目功能

请注意, ImageSequence 中的 create 函数接收图像文件列表作为参数。我们需要创建将返回这样的列表的函数,并将其交给数据块中的 get_items 参数。

基于数据源格式(步骤 0 中的 dataframe ),我定义了以下类来创建序列和标签列表:

class **SequenceGetItems**():
    def __init__(self, filename_col, sequence_id_col, label_col):
        self.fn    = filename_col
        self.seq   = sequence_id_col
        self.label = label_col

    def __call__(self, df):
        data = []
        for fn in progress_bar(df[self.seq].unique()):
            similar = df[self.seq] == fn
            similar = df.loc[similar]
            fns = similar[self.fn].tolist()
            lbl = similar[self.label].values[0]
            data.append([*fns, lbl])
        return data
  • 该类所需的属性只是数据帧上对应于文件名、唯一序列标识符和标签的列名。
  • 调用时,call 方法将接收数据帧“df”作为参数,并将返回一个列表列表。每个子列表由序列的图像文件名和子列表最后一个元素中的相应标签组成。

现在,请记住上面的数据块示例,即 get_xget_y 接收来自 get_items 的输出,并且应该将输入和目标分开。在这种情况下,它们就像这样简单:

get_x = lambda t : t[:-1]
get_y = lambda t : t[-1]
  • 如你所见, get_x 将选择列表中的所有元素,但最后一个将由 get_y 选择。这只是前面讨论的序列创建方式的结果——标签只是每个子列表中的最后一个元素。

第三步。把所有东西放在数据块里

有了上面两步定义的代码,为我们的图像序列构建一个数据块是一项简单的任务:

dblock = DataBlock(
    blocks    = (ImageSequenceBlock, CategoryBlock),
    get_items = SequenceGetItems('file', 'sequence_id', 'label'), 
    get_x     = lambda t : t[:-1],
    get_y     = lambda t : t[-1],
    splitter  = RandomSplitter())
  • 注意,对于参数,我们现在给出步骤 1 中定义的图像序列块
  • 然后对于 get_items 参数,我们使用在步骤 2 中定义的 SequenceGetItems 类。
  • get_xget_y 非常简单,如前一节所述。
  • 对于**分离器,**可以使用常用的 fastai 功能。另一个常见的选项是 IndexSplitter ,它允许精确地指定哪些项在验证集上。

我还没有包含任何 item_tfmsbatch_tfms ,但是我很快就会包含。然而,让我们首先来看一个重要的附加部分——创建数据加载器。

第四步。创建数据加载器

为了从数据块中创建一个数据加载器,我们需要定义一个自定义函数来告诉如何创建批处理。到目前为止,我们只有图像序列。我们想要的是 PyTorch 张量。

def **create_batch**(data):
    xs, ys = [], []
    for d in data:
        xs.append(d[0])
        ys.append(d[1])
    xs = torch.cat([TensorImage(torch.cat([im[None] for im in x], dim=0))[None] for x in xs], dim=0)
    ys = torch.cat([y[None] for y in ys], dim=0)
    return TensorImage(xs), TensorCategory(ys)
  • 这段代码将单个图像张量连接成一个具有额外维度的新张量,然后将单个序列成批连接在一起。如果图像是 3 通道,128×128 大小,序列的长度是 10,批量大小是 8,那么张量将具有[8,10,3,128,128]的形状。
  • 注意,当我返回张量时,我使用了 TensorImageTensorCategory 。这些是 fastai 类型,提供了非常有用的功能。

现在,要创建数据加载器,下面一行代码可以完成:

dls = dblock.dataloaders(df, bs=8, create_batch=create_batch)
  • 请注意, df 是我们在步骤 0 中提到的数据帧, bs 是批量大小,并且给出了一个自定义的 create_batch 函数。

第五步。可视化数据

这一步对于功能来说并不是最基本的,但是能够显示数据并检查是否一切正常总是好的!

def **show_sequence_batch**(max_n=4):
    xb, yb = dls.one_batch()
    fig, axes = plt.subplots(ncols=10, nrows=max_n, figsize=(12,6), dpi=120)
    for i in range(max_n):
        xs, ys = xb[i], yb[i]
        for j, x in enumerate(xs):
            axes[i,j].imshow(x.permute(1,2,0).cpu().numpy())
            axes[i,j].set_title(ys.item())
            axes[i,j].axis('off')

show_sequence_batch 函数

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

show_sequence_batch 函数的结果作者创造的形象。

请注意,每个图像上方的数字对应于序列标识符,这就是为什么在本例中它们在每一行都是相同的。

第六步。添加图像和序列增强

由于我们的批次有一个“额外的”维度—对应于序列长度—我们不能开箱即用地应用 fastai 图像增强。此外,正如在本文的引言中所讨论的,在一些问题中,我们需要按顺序应用变换,以便在我们可能希望提供给 3D 卷积模型的序列元素之间保持空间一致性。

获得想要的结果的最简单的方法——或者至少是我能想到的最简单的方法——是注意到变换被同样地应用到每个图像通道。因此,如果我们使用张量形状——使用 PyTorch view 方法——我们可以按顺序或按图像应用基本的图像变换。变换之后,我们可以把张量重塑回原来的形状。

为此,我定义了两个类:sequence FMSBatchTfms 。这两个类被定义为 fastai Transform 的子类。关于 fastai 转换需要注意的一个方面是编码方法类似于 PyTorch 模块中的 forward 方法或者 Python 类中常见的 call 方法。

class **SequenceTfms**(Transform):
    def __init__(self, tfms): 
        self.tfms = tfms

    def encodes(self, x:TensorImage): 
        bs, seq_len, ch, rs, cs = x.shape
        x = x.view(bs, seq_len*ch, rs, cs)
        x = compose_tfms(x, self.tfms)
        x = x.view(bs, seq_len, ch, rs, cs) 
        return x

class **BatchTfms**(Transform):
    def __init__(self, tfms): 
        self.tfms = tfms

    def encodes(self, x:TensorImage): 
        bs, seq_len, ch, rs, cs = x.shape
        x = x.view(bs*seq_len, ch, rs, cs)
        x = compose_tfms(x, self.tfms)
        x = x.view(bs, seq_len, ch, rs, cs) 
        return x
  • 这两个类都用要应用于数据的扩充列表进行初始化。
  • ****sequence FMS按顺序应用变换(同一序列中所有图像的变换相同)。
  • BatchTfms 按图像方式应用变换。

完整的数据块现在看起来像这样:

affine_tfms, light_tfms = aug_transforms(flip_vert=True)
brightness = lambda x : x.brightness(p=0.75, max_lighting=0.9)
contrast   = lambda x : x.contrast(p=0.75, max_lighting=0.9)

dblock = DataBlock(
    blocks     = (ImageSequenceBlock, CategoryBlock),
    get_items  = SequenceGetItems('file', 'sequence_id', 'label'), 
    get_x      = lambda t : t[:-1],
    get_y      = lambda t : t[-1],
    splitter   = RandomSplitter(valid_pct=0.2, seed=2020),
    item_tfms  = Resize(128),
    batch_tfms = [SequenceTfms([affine_tfms]), 
                  BatchTfms([brightness, contrast])])

dls = dblock.dataloaders(df, bs=8, create_batch=create_batch)

注意:到目前为止,我还不能用这种方法使用由 aug_transforms 给出的light _ tfms**——这就是为什么我手动定义了亮度对比度转换,它们只是 ImageTensor fastai 类型的方法。**

show_sequence_batch 可视化的结果如下:

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

使用图像和序列增强的 show_sequence_batch 函数的结果。作者创造的形象。

  • 请注意,在第一行中,图像是旋转的,但由于仿射变换是按顺序应用的,因此它们在序列中是一致旋转的。
  • 对于亮度和对比度变换,请注意它们是随机应用于各个图像的。

如您所见,该框架可以根据数据/问题的类型轻松定制。

结论

在这个故事中,我讲述了如何使用 fastai DataBlock 为深度学习应用处理 3D 数据。这个代码的一个工作示例在这个 Kaggle 内核上提供。

我花了一些时间和几次迭代才得到这里展示的代码,但所有这些学习过程让我对 fastai DataBlock 有了更好的理解,以及它是如何如此通用的!

关于我

** [## 我的 3 年历程:从零 Python 到深度学习竞赛高手

自从 2017 年开始学习 Python 以来,我一直遵循的道路是成为一名独自参加 Kaggle 比赛的大师…

towardsdatascience.com](/my-3-year-journey-from-zero-python-to-deep-learning-competition-master-6605c188eec7)

感谢阅读!如果你对这个话题还有其他问题,请随时发表评论。**

通过 Boto3 使用亚马逊 S3 桶

原文:https://towardsdatascience.com/working-with-amazon-s3-buckets-with-boto3-785252ea22e0?source=collection_archive---------7-----------------------

完整的备忘单。

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

杰夫·金玛Unsplash 上的照片

亚马逊简单存储服务(Amazon Simple Storage Service,简称 S3)通过微调访问控制提供了存储、保护和共享数据的空间。当使用 Python 时,人们可以通过 Boto3 包轻松地与 S3 进行交互。在这篇文章中,我将把我在使用 S3 时经常用到的 Python 命令汇总在一起。希望你会觉得有用。

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

让我们从关于 S3 数据结构的几句话开始。在你自己的电脑上,你将文件存储在文件夹中。在 S3,这些文件夹被称为。在桶里面,你可以存储对象,比如。csv 文件。您可以通过名称来引用存储桶,而通过关键字来引用对象。为了使代码块更易处理,我们将使用表情符号。以下是符号的关键:****

🗑 — a bucket’s name, e.g. “mybucket”🔑 — an object’s key, e.g. "myfile_s3_name.csv"📄 - a file's name on your computer, e.g. "myfile_local_name.csv"

🗑和🔑可以表示 S3 上已经存在的名称,也可以表示您希望为新创建的桶或对象指定的名称。📄表示您已经拥有或希望拥有在您的计算机本地某处的文件。

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

设置客户端

要用 Boto3 访问任何 AWS 服务,我们必须用一个客户机连接到它。在这里,我们创建一个 S3 客户端。我们指定数据所在的区域。我们还必须传递访问密钥和密码,这可以在 AWS 控制台中生成,如这里的所述

存储桶:列出、创建和删除

要列出 S3 上现有的存储桶,删除一个或创建一个新的,我们只需分别使用list_buckets()create_bucket()delete_bucket()函数。

对象:列表、下载、上传和删除

在一个桶中,存在对象。我们可以用list_objects()把它们列出来。MaxKeys参数设置列出的对象的最大数量;这就像在打印结果之前调用head()一样。我们还可以使用Prefix参数只列出键(名称)以特定前缀开头的对象。
我们可以使用upload_file()上传一个名为📄以这个名字去 S3🔑。同样,download_file()会保存一个名为🔑在 S3 当地以这个名字📄。
为了获得一些关于对象的元数据,比如创建或修改时间、权限或大小,我们可以调用head_object()
删除一个对象的工作方式与删除一个桶相同:我们只需要将桶名和对象键传递给delete_object()

将多个文件加载到单个数据框中

通常,数据分布在几个文件中。例如,您可以将不同商店或地区的销售数据保存在具有匹配列名的不同 CSV 文件中。对于分析或建模,我们可能希望将所有这些数据放在一个 pandas 数据框中。下面的代码块就可以做到这一点:下载🗑境内名称以“some_prefix”开头的所有数据文件,并将其放入单个数据框中。

使用访问控制列表(ACL)将对象设为公共或私有

在 S3 上管理访问权限的一种方法是使用访问控制列表或 ACL。默认情况下,所有文件都是私有的,这是最好的(也是最安全的!)练习。您可以将文件指定为"public-read",在这种情况下,每个人都可以访问它,或者指定为"private",使您自己成为唯一授权的人。查看此处获取访问选项的详细列表。
当文件已经在 S3 上时,你可以使用put_object_acl()来设置它的 ACL,也可以在上传时通过将适当的ExtraArgs传递给upload_file()来设置。

使用预签名的 URL 访问私有文件

您还可以通过使用generate_presigned_url()函数生成一个临时的预签名 URL 来授予任何人对私有文件的短期访问权。这将产生一个字符串,可以直接插入到 pandas 的read_csv()中,例如,下载数据。您可以通过ExpiresIn参数指定该临时访问链接的有效时间。这里,我们创建一个有效期为 1 小时(3600 秒)的链接。

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

感谢阅读!

如果你喜欢这篇文章,为什么不在我的新文章上 订阅电子邮件更新 ?通过 成为媒介会员 ,你可以支持我的写作,并无限制地访问其他作者和我自己的所有故事。

需要咨询?你可以问我任何事情,也可以在这里 预定我 1:1

也可以试试 我的其他文章 中的一篇。不能选择?从这些中选择一个:

[## 通过 Boto3 使用 Amazon SNS

完整的备忘单。

towardsdatascience.com](/working-with-amazon-sns-with-boto3-7acb1347622d) [## 增强你对助推的把握

揭秘著名的竞赛获奖算法。

towardsdatascience.com](/boost-your-grasp-on-boosting-acf239694b1) [## 线性回归中收缩法和选择法的比较

详细介绍 7 种流行的收缩和选择方法。

towardsdatascience.com](/a-comparison-of-shrinkage-and-selection-methods-for-linear-regression-ee4dd3a71f16)

通过 Boto3 使用 Amazon SNS

原文:https://towardsdatascience.com/working-with-amazon-sns-with-boto3-7acb1347622d?source=collection_archive---------7-----------------------

完整的备忘单。

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

照片由杰米街上的 Unsplash

亚马逊简单通知服务,或 SNS,允许我们自动发送消息,如电子邮件或短信。我们还可以通过 HTTP Post 向指定的 URL 发送消息,这是一种分离微服务的便捷方式。当使用 Python 时,人们可以通过 Boto3 包轻松地与 SNS 进行交互。在这篇文章中,我将把我在使用 SNS 时经常用到的 Python 命令汇总在一起。希望你会觉得有用。

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

让我们从一些基本的社交网络概念开始。你可以把 SNS 系统想象成一个群聊。一旦您在聊天中发布消息,所有参与者都会收到一个弹出消息。在 SNS 设置中,群聊将被称为话题。您可以向主题发布消息,一旦发布,所有主题订阅者都会收到通知。

AWS 资源可以通过亚马逊资源名称 (ARNs) 唯一标识。每个主题都有其独特的主题卡,每个主题的订阅都有其独特的订阅号。

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

设置客户端

要用 Boto3 访问任何 AWS 服务,我们必须用一个客户机连接到它。在这里,我们创建一个 SNS 客户端。我们指定保存消息的区域。我们还必须传递访问密钥和密码,这可以在 AWS 控制台中生成,如这里的所述

主题:列出、创建和删除

为了创建一个新的主题,我们使用了create_topic()函数,传递所需的名称。一旦主题被创建,我们可以通过从由craete_topic()返回的对象中提取TopicArn键来获得它的 ARN。
要列出 AWS 上已经存在的主题,我们可以使用list_topics()函数并从其输出中提取Topics键。要删除一个主题,我们只需将它的 ARN 传递给delete_topic()函数。

订阅:列出、创建和删除

为了创建主题的新订阅,我们调用subscribe()函数,传递要订阅的主题的 ARN、协议(例如 SMS 或电子邮件)和端点(例如 SMS 协议的电话号码或电子邮件协议的电子邮件地址)。一旦订阅被创建,我们可以通过从由subscribe()返回的对象中提取SubscriptionArn键来获得它的 ARN。
要列出 AWS 上现有的所有订阅,我们可以使用list_subscriptions()函数并从其输出中提取Subscriptions键。类似地,为了列出特定主题的订阅,我们调用了list_subscriptions_by_topic()函数。
要删除一个订阅,我们将它的 ARN 传递给unsubscribe()函数。为了删除多个订阅,例如具有相同协议的所有订阅,我们可以使用下面代码块底部的 for-loop 对它们进行循环。

发布到主题

要向主题发布消息,我们只需调用publish()函数,传递主题的 ARN、想要的消息和可选的主题(它将只用于电子邮件消息)。
或者,我们可以发布没有任何主题或订阅的 SMS 消息。为此,我们调用publish()函数,将电话号码和消息文本作为参数传递。

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

感谢阅读!

如果你喜欢这篇文章,为什么不订阅电子邮件更新我的新文章呢?并且通过 成为媒介会员 ,可以支持我的写作,获得其他作者和我自己的所有故事的无限访问权限。

需要咨询?你可以问我任何事情,也可以在这里为我预约 1:1

也可以试试 我的其他文章 中的一篇。不能选择?从这些中选择一个:

** [## 使用 Boto3 处理亚马逊 S3 桶。

完整的备忘单。

towardsdatascience.com](/working-with-amazon-s3-buckets-with-boto3-785252ea22e0) [## 非线性回归:基础扩展、多项式和样条

如何用多项式和样条捕捉非线性关系?

towardsdatascience.com](/non-linear-regression-basis-expansion-polynomials-splines-2d7adb2cc226) [## 模型选择和评估

超越火车价值测试分裂

towardsdatascience.com](/model-selection-assessment-bb2d74229172)**

使用 Kotlin 使用 Android WorkManager

原文:https://towardsdatascience.com/working-with-android-workmanager-using-kotlin-36167a143579?source=collection_archive---------19-----------------------

向后兼容的调度未来任务的理想方式

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

Fernando Jorge 在 Unsplash 上拍摄的照片

工作管理器是一个 API,它可以调度你未来的异步任务,并且可以在后台运行它们。即使用户在应用程序之外或应用程序关闭,分配给工作管理器的任务也会执行。WorkManager 只能运行您的任务一次,也可以多次运行或定期运行。

工作管理器的功能

  • 它提供了高达 API 级别 14 的向后兼容性
  • 您可以添加一个或多个约束条件,如仅在手机充电或重启时执行任务等。
  • 您可以计划一次性任务或定期任务
  • 您还可以链接多个任务。例如,任务 (B) 应该只在任务 (A) 完成时执行。
  • 它可以帮助您在特定事件上执行任务。

**注意:**工作管理器不适用于在 app 进程结束时可以安全终止的正在进行的后台工作,也不适用于需要立即执行的任务。

上层社会

**工作人员:**这里定义了需要完成的工作。

WorkRequest: 它决定将要执行哪个 worker 类。这是一个抽象类,所以我们将使用它的直接类,它们是 OneTimeWorkRequestPeriodWorkRequest

工作管理器:它对工作请求进行排队和管理。

WorkInfo: 它给我们关于工作的信息,不管是成功的、运行的还是失败的。

让我们现在开始编码吧…

我们会创造什么?

我们将在后台创建一个通知,这个通知只能创建一次,因为我们使用的是 OneTimeWorkRequest 类。稍后,我们将使用一些约束来基于事件生成通知。

首先,添加以下依赖项。

implementation **"androidx.work:work-runtime-ktx:2.3.4"**

我们将首先通过扩展***Worker***类来创建我们的 worker 类,并覆盖它的***doWork()***方法用于后台处理。当工作管理器调用***doWork()***方法时,它调用用户定义的方法***createNotification()***

在我们的***MainActivity.kt***类中,我创建了一个按钮,当用户点击按钮时,就会立即生成通知。

在这里,我创建了 OneTimeWorkRequest 的对象,并传递了我们的***MyWork***类的类名。在现实世界中,我们可以有很多 worker 类,所以应该执行哪个类是由这个 request 对象决定的。

**val** request = *OneTimeWorkRequestBuilder*<MyWork>().build()

当用户单击按钮时,WorkManager 将请求排队。

WorkManager.getInstance(**this**).enqueue(request)

在这里,我们创建一个 toast 来显示我们任务的状态,无论是运行成功还是失败***getWorkInfoByIdLiveData***方法获取请求 id 并给出关于任务的信息。

WorkManager.getInstance(**this**).getWorkInfoByIdLiveData(request.*id*)
            .observe(**this**, *Observer* **{

                val** status: String = **it**.*state*.**name** Toast.makeText(**this**,status, Toast.*LENGTH_SHORT*).show()
            **}**)

}

现在运行你的应用程序,点击按钮,你会看到一个通知。

现在,我们将了解如何添加约束,以便仅在手机充电时创建通知。

添加以下代码行以创建约束并修改您的请求对象。在请求对象中,我只是设置约束,仅此而已。现在,只有当满足这个特定标准时,才会生成通知。

**val** constraints = Constraints.Builder()
        .setRequiresCharging(**true**)
        .build()**var** request = *OneTimeWorkRequestBuilder*<MyWork>()
        .setConstraints(constraints)
        .build()

**注意:**当指定了多个约束时,您的任务只有在所有约束都满足时才会运行。

如果你点击按钮,而你的手机没有在充电,那么你会看到一个状态显示**“排队”,**这意味着你的请求已经被放入队列,只有当你的手机正在充电时才会执行。

如果你在运行代码时遇到任何问题,那么你可以从我的 Github 账户下载这个项目。

我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。

订阅 我的邮件列表,以便在您的收件箱中直接获得我的文章,并且不要忘记关注我自己在 Medium 【代码怪兽】 上发表的文章,以丰富您的技术知识。

结论

我们已经看到了如何使用 WorkManager 类来执行一些后台处理。在本文中,我在后台创建了一个通知。也看到了我们如何根据未来将要发生的事件来安排未来的任务。

使用匿名函数

原文:https://towardsdatascience.com/working-with-anonymous-functions-d985003edb3d?source=collection_archive---------40-----------------------

使用这些简单的操作快捷方式节省您的时间

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

塔里克·黑格在 Unsplash 上拍摄的照片

当我们处理数据时,我们很少会让它保持原样、不被转换或不被聚集。必须以高效的方式清理、转换、总结和描述数据,以便为实现准备数据。创建函数是一种标准且可靠的做法,在对数据应用预定义的操作时可以节省时间。然而,尽管定义自己的函数有时非常重要,但有时却完全没有必要。我们可以对一个特定的级数使用特殊类型的函数来加速这个过程,而不牺牲计算效率或精度。

匿名函数

我们在 Python 中实现了一些函数,但没有给它们命名(因此,匿名)。这些函数只能在数据操作过程的特定时刻使用一次。介绍,λ函数:

lambda <arguments>: <expression>

以上是 lambda 函数的一般语法。我们稍后将进一步深入,但首先让我们定义语法。

  • Lambda 函数可以有任意数量的参数(将要操作的内容),但只能有一个表达式(将要计算和返回的内容)
  • lambda 函数可以应用于任何需要函数对象的地方
  • 仅限于单一表达式

让我们来比较一下匿名 lambda 函数和传统定义的 function 对象在试图将一个列表的所有内容加倍时的情况:

繁体:

list1 = [1, 2, 3]def double_items(list1): list2 = []    
     for x in list1:
          list2.append(x*2) return list2

它返回:

double_items(list1)Output: [2,4,6]

兰巴:

list1 = [1,2,3]
list2 = list(map(lambda x: x*2, list1))

它返回:

print(list2)Output: [2,4,6]

我们可以清楚地看到,第二个 lambda 函数允许我们在一行代码中完成相同的计算,而不需要定义不必要的代码块。

Lambda 场景:

lambda 匿名函数可以应用于三种主要场景:filter()、map()和 reduce()。

过滤器()

这个函数将一个列表作为它的参数,然后过滤掉列表中的元素,为表达式返回“true”。我们下面看到的函数将只返回偶数列表项(使用了“%”模数运算符)。

ex_list = [33, 2, 42, 1, 47, 430, 23, 98, 12]
final = list(filter(lambda x: (x%2 == 0), ex_list))

输出:

print(final)[2, 42, 430, 98, 12]

地图()

在我处理数据的经验中,这个函数是目前使用最多的匿名函数。我们已经在最初的例子中使用了 map。当与熊猫系列对象结合使用时,贴图功能也非常有效。Map 是一个带两个参数的函数:函数名(或函数本身)和序列(一个列表、一个熊猫系列等)。).因此,map 函数与 lambda 函数一起将映射序列中所有元素的函数。

a = [1, 2, 3, 4]
b = [5, 6, 7, 8]sum_a_b = map(lambda x, y: x + y, a, b)

输出:

print(sum_a_b)[6, 8, 10, 12]

示例 map/lambda 组合采用函数 *x + y,*以及序列 a 和 b。它将每个序列中的各个项目分别视为函数的 x 和 y,以返回结果。

减少()

这个函数也将一个列表作为参数,然后,你猜对了,通过对列表对的重复操作来减少列表。我们可以在“functools”模块中找到这个函数,我们必须首先导入它。

from functools import reduceex_list = [24, 5, 6, 45, 96, 123, 69]
difference = reduce(lambda x: y: x - y, ex_list)

输出:

print(difference)-320

这个函数取列表中第一项和第二项的差,然后继续下去,直到列表的最后一个元素被求差。类似地,它将 lambda 函数作为要执行的操作,将 ex_list 作为执行函数的序列。

在数据科学中使用 APIs 探索新加坡 HDB 转售市场中的位租理论

原文:https://towardsdatascience.com/working-with-apis-in-data-science-explore-bit-rent-theory-in-singapores-hdb-resale-market-d7760fdfc601?source=collection_archive---------25-----------------------

你在数据分析和机器学习方面 80%的工作是争论和清理数据,这可能是非常乏味的工作。使用 API 实现数据提取、数据丰富和数据清理的自动化可以节省大量手工提取的工作。

本文的重点将是如何在 Python 中与 API 交互,我们将在新加坡公共住房数据集的背景下工作,并探索住房市场中的出价-租金理论。它将更侧重于 Python 脚本,而不是实际分析,所以如果本文的任何部分缺乏学术和统计的严谨性,请原谅我。

完整代码 Github 链接:https://github . com/yuan-西野/hdb-转售-api/blob/master/script.py

比特租金理论

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

我们基本上是在探索每平方米价格和房产到 CBD 的距离之间的关系。

我们想要实现的目标:

  • 从转售 API 查询 2017 年 1 月以后的所有交易数据
  • 获取交易地址的地理位置(纬度,经度)
  • 获取所有捷运站(新加坡地铁)的地理位置(经度,纬度)
  • 获取所有购物中心的地理位置(纬度,经度)
  • 获取每笔交易与最近的捷运站和购物中心之间的距离
  • 获取每次交易到中央商务区(莱佛士广场)的距离
  • 不要使用一行 PD . read _ CSV();)

太好了,既然已经不碍事了,那就让我们进入正题吧。

首先让我们检查一下我们今天将使用的 API:

  1. https://data.gov.sg/dataset/resale-flat-prices——Data.gov.sg 的转售价格提供了自 1990 年以来的历史交易数据。我们将专门检查 2017 年 1 月以后的数据集。
  2. https://docs.onemap.sg/—一个强大的 API,提供特定于新加坡的位置坐标、交通、路线、人口数据等数据。我们将利用这个 API 来获取这个练习所需的地理坐标。

通过 API 获取数据并转换成 Pandas 数据框架

首先,我们将从导入一些基本库开始

import json
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

为了发出我们的第一个 API 请求,我们将使用请求库。但在此之前,让我们检查一下浏览器中的 API:

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

这是我们将发出的 HTTP GET 请求,用于请求数据集的服务

我们可以看到,我们可以通过使用“limit”或“q”参数来查询结果,以获得搜索结果。既然我们需要整个数据集,我们只需要指定整个数据集的限制。

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

快速检查我们浏览器中的示例查询,我们可以看到我们感兴趣的事务的“记录”嵌套在 JSON 响应的“结果”数组中。有了我们正在处理的数据结构的概念,我们可以移动到我们的 Python IDE 并查询我们的数据。

query_string='[https://data.gov.sg/api/action/datastore_search?resource_id=42ff9cfe-abe5-4b54-beda-c88f9bb438ee&limit=500'](https://data.gov.sg/api/action/datastore_search?resource_id=42ff9cfe-abe5-4b54-beda-c88f9bb438ee&limit=500')
resp = requests.get(query_string)#Convert JSON into Python Object 
data = json.loads(resp.content)

因为这是一个相当大的数据集(在撰写本文时有 101994 条),所以在转移到整个数据库之前,我们将对 500 条记录进行采样,以便于处理。在使用 API 的探索阶段,这是一个很好的实践,可以避免耗尽不必要的服务器资源,重复查询巨大的数据集。现在,让我们在 IDE 中检查一下服务返回给我们的内容。

# Notice that data type is actually 'dict'
print(type(data))#Output
<class 'dict'>

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

这看起来类似于我们在浏览器中看到的返回的 JSON

不错,从服务器返回的 JSON 对象基本上是大家熟悉的 Python 字典!我们的工作变得简单了一点,不需要额外的工作来转换我们的 JSON 对象,使其在 Python 中工作。

len(data['result']['records'])#Output
500

我们所要做的就是访问 dictionary 对象中的 records 键,以获得存储为 dictionary 键-值对的事务数据列表。

# Access the first record in our list
hdb_price_dict_records[0]#Output
{'town': 'JURONG EAST',
 'flat_type': '3 ROOM',
 'flat_model': 'New Generation',
 'floor_area_sqm': '67',
 'street_name': 'JURONG EAST ST 21',
 'resale_price': '270000',
 'month': '2018-07',
 'remaining_lease': '63 years 08 months',
 'lease_commence_date': '1983',
 'storey_range': '10 TO 12',
 '_id': 1,
 'block': '214'}

既然我们已经习惯了遍历嵌套数据,那么让我们创建 Pandas 数据框。

在上面的示例中,我们创建了一个 for 循环,将每个记录中的字典值附加到相关列表中,该列表将构成数据框中的每一列。

df_hdb_price = pd.DataFrame({
    'town': town,
    'flat_type': flat_type,
    'flat_model': flat_model,
    'floor_area_sqm': floor_area_sqm,
    'street_name': street_name,
    'resale_price': resale_price,
    'month': month,
    'remaining_lease': remaining_lease,
    'lease_commence_date': lease_commence_date,
    'storey_range': storey_range,
    '_id': _id,
    'block': block
})

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

酷!我们的数据框看起来不错!

通过 OneMap API 获取经度和纬度数据

接下来的部分有点棘手。我们需要使用地理位置坐标(纬度、经度)找到 HDB 单位所在的最近的捷运站。我们将使用另一个 API 来实现这一点 OneMap API。

首先让我们创建一个新加坡所有捷运站的列表。由于 MRT 站点随时间变化相对较慢,我们可以利用静态列表来代替。我获得了(好的 ole 副本和面食;) )维基百科的数据,其中也提供了即将到来的捷运站的数据。我们暂时只考虑现有的捷运站。

list_of_mrt = [
    'Jurong East MRT Station',
    'Bukit Batok MRT Station',
    'Bukit Gombak MRT Station',
    'Choa Chu Kang MRT Station',
    'Yew Tee MRT Station',
    'Kranji MRT Station',
    'Marsiling MRT Station',
    ....
    ]

快速查看 OneMap API —我们可以看到,我们需要将我们的地址输入到 URL 的“searchVal”参数中,如下所示:

[https://developers.onemap.sg/commonapi/search?searchVal='+str(address)+'&returnGeom=Y&getAddrDetails=Y](https://developers.onemap.sg/commonapi/search?searchVal='+str(query_address)+'&returnGeom=Y&getAddrDetails=Y')

让我们使用 OneMap API 来获取每个 MRT 站的(lat,long)坐标。下面的 for 循环遍历 MRT 车站列表并返回结果,然后我们将获取响应的第一个结果,类似于我们之前遍历转售 JSON 对象时所做的。

然后我们将它存储到一个新的数据帧中:

# Store this information in a dataframe
mrt_location = pd.DataFrame({
    'MRT': list_of_mrt,
    'latitude': mrt_lat,
    'longitude': mrt_long
})

现在,让我们找出使用相同方法处理的每个单元的地理位置。但是等一下,这是一个很大的数据集…我们可以让它更有效率。我们知道,在同一个 HDB 公寓区会有多个单位进行交易。我们可以对我们的数据帧进行重复数据消除,并仅获得数据帧中的唯一地址。

# Let's combine the block and street name to form the address of our transacted unit.
df_hdb_price['address'] = df_hdb_price['block'] + " " + df_hdb_price['street_name']# Dedup Address List
df_dedup = df_hdb_price.drop_duplicates(subset='address', keep='first')
len(df_dedup)

我们现在可以通过 OneMap API 获得(经度,纬度)坐标

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

当我们遍历地址列表时,我们在控制台中打印出坐标数据

完成后,让我们将它保存到一个日期框架中:

df_coordinates = pd.DataFrame({
    'latitude': latitude,
    'longitude': longitude,
    'blk_no': blk_no,
    'road_name': road_name,
    'postal_code': postal_code,
    'address': address
})
len(df_coordinates)

我们需要为下一个任务找到解决方案,为数据框中的每个地址找到最近的 MRT,并获得以米为单位的距离。

寻找坐标列表之间的最短距离

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

来源:维基百科

我们将使用哈弗辛公式来获得曲面上两点之间的距离。或者,我们也可以使用毕达哥拉斯定理,因为我们两点之间的距离并不大。但是为了简单起见,我们将使用 geopy 包来计算哈弗辛公式。

***# Lists of all the coordinates we will need to iterate through***
list_of_lat = df_coordinates['latitude']
list_of_long = df_coordinates['longitude']
mrt_lat = mrt_location['latitude']
mrt_long = mrt_location['longitude']***# Zipping the respective Lat and Long lists together as a list of tuples***
list_of_coordinates = []
for lat, long in zip(list_of_lat, list_of_long):
    list_of_coordinates.append((lat,long))for lat, long in zip(mrt_lat, mrt_long):
    list_of_mrt_coordinates.append((lat, long)) 

要获得地图上任意给定点的最近的 MRT 车站,我们需要:

  • 遍历我们的地址列表
  • 获取地址和所有捷运站之间的距离
  • 将它存储到列表中
  • 从那里我们将采取最短的距离作为最近的捷运站

上面我们有一个嵌套的 for 循环,它遍历 2 组坐标,为数据集中的每个唯一地址获取最近的 MRT。尽管这肯定不是最有效的解决方案。例如,我们可以将我们的地址分组到新加坡的各个地区,并且只检查位于给定距离内的 MRT 站列表,而不是整个列表。但是现在我们将依靠 CPU 的强大力量来为我们制造这些数字:)

我们将同样计算到 CBD 的距离和到最近的购物中心的距离。

***# Storing our distance into our data frame as a new column***
df_coordinates['min_dist_mrt'] = min_dist_mrt
df_coordinates['min_dist_mall'] = min_dist_mall
df_coordinates['cbd_dist'] = cbd_dist

把它们放在一起

最后,我们将使用外部连接将距离数据与主数据框合并。

combined = df_coordinates.merge(df_hdb_price, on="address", how='outer')

一些数据卫生步骤

# Converting to the right data types
combined['resale_price'] = combined['resale_price'].astype('float')
combined['floor_area_sqm'] = combined['floor_area_sqm'].astype('float')combined['lease_commence_date'] = combined['lease_commence_date'].astype('int64')
combined['lease_remain_years'] = 99 - (2019 - combined['lease_commence_date'])
combined.columnscombined.dropna(inplace=True)combined['price_per_sqm'] = combined['resale_price'].div(combined['floor_area_sqm'])

我们已经准备好绘制我们的价格/平方米到 CDB 的距离关系图!

绘制钻头租金关系

g = sns.scatterplot(x=combined['cbd_dist'],y=combined['price_per_sqm'], hue=combined['flat_type'], ax=ax1)box = g.get_position()
g.set_position([box.x0, box.y0, box.width * 0.85, box.height]) # resize positiong.legend(loc='center right', bbox_to_anchor=(1.2, 0.5), ncol=1)plt.show()

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

新加坡转售组屋的位租关系

从我们的数据中,我们明确看到了距离和每平方米价格之间的负相关关系。但是在房地产和中央商务区之间的某些距离间隔上也有很大的差异。除了我们的 cdb_dist 变量之外,肯定还有其他决定地价的解释性因素。让我们探索变量之间的相关性。

corrMatrix = df_numerical_cols.corr()
sns.heatmap(corrMatrix, 
        xticklabels=corrMatrix.columns,
        yticklabels=corrMatrix.columns,
        cmap='coolwarm',
        annot=True)

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

看起来,到关键设施的距离,如捷运和购物中心,对我们的土地价格有影响。而土地租赁也可以解释部分价格。

线性回归模型

我们将使用一个简单的多元线性回归模型进一步探索我们的特征的解释能力。让我们设计我们需要的功能。

**# Separate our numerical and categorical variables**
cat_features = ['town', 'flat_type', 'storey_range']
num_features = ['min_dist_mrt', 'min_dist_mall', 'cbd_dist','floor_area_sqm', 'lease_remain_years', 'resale_price', 'price_per_sqm']
target = ['resale_price']**# New data frame for our regression model**
df_reg = combined[['town', 'flat_type', 'storey_range', 'min_dist_mrt', 'min_dist_mall', 'cbd_dist','floor_area_sqm', 'lease_remain_years', 'resale_price', 'price_per_sqm']]

映射序数分类特征:

flat_type_map = {
    'EXECUTIVE': 7,
    'MULTI-GENERATION': 6,
    '5 ROOM': 5,
    '4 ROOM': 4,
    '3 ROOM': 3,
    '2 ROOM': 2,
    '1 ROOM': 1
}df_reg['flat_type_mapped'] = df_reg['flat_type'].map(lambda x: flat_type_map[x])def split_mean(x):
    split_list = x.split(' TO ')
    mean = (float(split_list[0])+float(split_list[1]))/2
    return meandf_reg['storey_mean'] = df_reg['storey_range'].apply(lambda x: split_mean(x))

一键编码我们的“城镇”功能:

# One-Hot Encoding for our categorical variables and drop 1 of our dummy variables
df_reg = pd.get_dummies(data=df_reg, columns=['town'], drop_first=True)

创建我们的回归模型:

import statsmodels.api as sm

X = df_reg[['min_dist_mrt', 'min_dist_mall',
       'cbd_dist', 'floor_area_sqm', 'lease_remain_years',
       'flat_type_mapped', 'storey_mean']] 
y = df_reg["price_per_sqm"]X = sm.add_constant(X)model = sm.OLS(y, X).fit()
predictions = model.predict(X)# Print out the statistics
model.summary()

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

解释我们的结果

  • r 平方-这是预测的解释方差的百分比。我们的模型只能解释约 0.728 或 72.8%的数据差异
  • 我们的系数的统计显著性-我们在模型中的 p 值很小(< 0.05),因此我们可以拒绝零假设,并得出结论,我们的变量与我们的每平方米价格有因果关系。事实上,我们与 CDB 的距离每增加 1 米,每平方米的价格就会下降 0.19 美元

检查回归误差指标

from sklearn import metricsprint('Mean Absolute Error:', metrics.mean_absolute_error(df_reg["price_per_sqm"], predictions))  
print('Mean Squared Error:', metrics.mean_squared_error(df_reg["price_per_sqm"], predictions))  
print('Root Mean Squared Error:', np.sqrt(metrics.mean_squared_error(df_reg["price_per_sqm"], predictions)))***# Output***
Mean Absolute Error: 476.06461056219956
Mean Squared Error: 395453.2820071103
Root Mean Squared Error: 628.8507629057234

平均结果误差给出了模型预测的实际数据的平均绝对值。与我们的数据集中每平方米价格的范围和平均值相比,我们的 MAE 相当小。

从我们的 RMSE 值来看,我们的模型的预测平均会与实际价值相差 628.85 美元。如果我们将 RMSE 作为每平方米平均价格的一个百分比,那么我们将得到 14%的误差率,这是相当高的。

改进我们的模型

让我们通过在回归中引入分类变量来提高模型的解释力。

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

  • 我们可以看到,我们的 R 平方值提高了,可以解释我们数据集中 82.3%的方差
  • 我们看到,房产所属的城镇对其每平方米价格有着巨大而显著的影响
  • 武吉提马、海洋游行、碧山,这些都是新加坡非常受欢迎的房地产,与芽笼(红灯区)、后港(反对党)等传统上不太受欢迎的地区相比,溢价要高得多
Mean Absolute Error: 382.26714892132696
Mean Squared Error: 257854.07863672107
Root Mean Squared Error: 507.7933424501754

我们的平均误差、均方误差和 RSME 都降低了,这意味着我们的模型有了更高的精度。我们的 RMSE/均值也降到了 11.2%。

结论

显然,从统计数据来看,到中央商务区的距离和每平方米的价格之间存在显著的关系。但是,这并不能完全解释新加坡 HDB 的房价。从我们的回归模型中,我们看到住宅地产定价有更多的因素,如设施、政治边界、物业的成熟度以及它所在的地产,这些因素可能比到中央商务区的距离具有更强的解释力和预测力。

感谢阅读到最后!这是一篇很长的文章,希望它能帮助你更好地理解如何与 Python 中的 API 进行交互,以及它如何帮助你自动化数据处理和分析工作流!更多数据和 Python 相关内容请关注我:)

在 Azure 函数中使用 Azure Cosmos DB

原文:https://towardsdatascience.com/working-with-azure-cosmos-db-in-your-azure-functions-cc4f0f98a44d?source=collection_archive---------1-----------------------

如何使用 Azure 函数和 Cosmos DB 实现无服务器数据库计算

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

开发使用 Azure Cosmos DB 作为数据存储的 Azure 函数很容易实现。我们可以使用 CosmosDB 触发器调用我们的 Azure 函数,我们可以使用输入和输出绑定从我们的 Cosmos DB 集合中获取数据,或者我们可以使用 Azure 函数支持依赖注入到我们的 Cosmos DB 客户端的单例实例中,以供我们的函数使用。

在本文中,我将讨论在 Azure Function 应用程序中使用 Cosmos DB 的各种不同方式,每种方式的优缺点,并展示一些过程中的代码。出于本文的目的,我将用 C#编写代码示例。

要跟随代码,请在 GitHub 上查看这个回购

使用 CosmosDB 触发器

我们可以使用 CosmosDB 触发器来创建事件驱动的函数,这些函数使用 Cosmos DB Change Feed 功能来监视我们的 Cosmos DB 数据库中容器的变化。

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

来源:https://docs . Microsoft . com/en-us/azure/cosmos-db/server less-computing-database # why-choose-azure-functions-integration-for-server less-computing

使用 Cosmos DB 触发器,我们可以使用变更提要对容器中的项目执行操作,并将这些操作的结果存储在另一个容器中。我们还可以使用触发器来实现数据的归档策略。例如,我们可以在 Cosmos DB 中存储热数据,使用 change feed 来监听进入该容器的新项目并将其保存在 Azure 存储中,然后删除 Cosmos DB 中的热数据。

让我们来看一个例子:

这是在函数中使用 CosmosDB 触发器的最简单的例子。我们正在监听数据库中的一个集合,通过为它指定一个集合并在我们的绑定中不存在该集合时创建它来管理变更提要的租约。

然后,当变更提要检测到受监控容器中的变更时,我们只记录该容器中修改了多少个文档,以及第一个文档的 id 是什么。默认情况下,更改提要每 5 秒钟轮询一次指定的容器,但是如果需要更改该值,可以在绑定中指定不同的值。

如果你想进一步了解你可以在 Azure Functions 中使用变更提要,我在这里写了一个更详细的帖子。

使用输入绑定

我们可以将 Azure 函数绑定到 Cosmos DB 中的容器,这将允许函数在执行时从容器中读取数据。

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

来源:https://docs . Microsoft . com/en-us/azure/cosmos-db/server less-computing-database # why-choose-azure-functions-integration-for-server less-computing

假设我们有一个使用 HTTP 触发器调用我们的函数的无服务器 API,我们可以使用输入绑定获得一个存储在 Azure Cosmos DB 中的文档。

我们可以这样做:

我们可以指定希望在 Cosmos DB 输入绑定中运行的 SQL 查询,并将结果作为产品类的 IEnumerable 返回。如果我们没有得到结果,我们可以返回 Not Found,否则,在 16 行代码中,我们可以将产品项返回给我们的客户端。

使用输出绑定

通过输出绑定,我们可以连接到 Cosmos 中的容器并将数据写入这些容器。

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

来源:https://docs . Microsoft . com/en-us/azure/cosmos-db/server less-computing-database # why-choose-azure-functions-integration-for-server less-computing

以我们的无服务器 API 为例,这次我们可以使用一个 Cosmos DB 输出绑定来连接到我们的容器,以便将我们的新产品项目插入其中。

这里有一个例子:

我们在输出绑定中所做的就是指定我们想要将项目插入哪个容器,它位于哪个数据库中,以及到我们的 Cosmos DB 帐户的连接字符串。

如您所见,使用 Cosmos DB 绑定构建一些简单的应用程序非常容易,但这是有代价的。

绑定的缺点

在我写这篇文章的时候, Azure 函数触发器和绑定只支持 SQL API 。如果我们想在我们的 Azure 函数中使用另一个 Cosmos DB API,我们必须创建一个静态客户端,或者像我们接下来要做的那样,为我们正在使用的 API 创建一个客户端的单独实例。

默认情况下,Cosmos DB 绑定使用版本 2 的。NET SDK。这意味着如果您想在函数中使用新的 V3 特性,比如事务批处理,那么您必须使用依赖注入来创建一个兼容 V3 的客户端。

使用依赖注入

Azure Functions 的 v2 提供了对依赖注入的支持。它建立在。NET 核心依赖注入特性。函数中的依赖注入提供了三种服务生存期:

  • 瞬态:这些是为服务的每个请求创建的。
  • 作用域:这个作用域匹配一个函数的执行生存期。每次执行都会创建一次。
  • Singleton :它们的生命周期与主机的生命周期相匹配,并在该实例上的所有函数执行中被重用。因此,我们可以在函数应用程序中为每个函数提供共享服务,而不是让客户端连接到每个绑定。这是我们将用于我们的 CosmosClient 的范围。

为了在我们的函数中实现依赖注入,我们需要在 Startup.cs 文件中注册我们的 CosmosClient 服务:

这里,我添加了一个 CosmosClientBuilder 的单例实例,通过我的 CosmosDB 帐户的连接设置。然后,我使用 fluent API 添加属性,比如设置应用程序区域和支持批量执行。

现在我有了我的 Singleton 实例,我可以将它注入到我们的函数中:

在这里,我使用构造函数注入来使我的依赖项对我的函数可用。注意这里我不再为我的函数使用静态类,因为这是构造函数注入所需要的。

如果你想了解依赖注入在 Azure 函数中是如何工作的,这份文档有一个非常简单的指南。

结论

我希望读完这篇文章后,您对在函数中使用绑定和依赖注入来处理 Cosmos DB 的含义有一个很好的了解。

我的偏好是使用单例实例而不是绑定,但是就像我前面说的,这取决于您的用例。

我希望你喜欢这篇文章,并发现它很有用。一如既往,如果你有任何问题,请在评论中告诉我!

处理数据:纽约时报“新词”数据集

原文:https://towardsdatascience.com/working-with-data-new-york-times-new-words-dataset-524d7a550beb?source=collection_archive---------34-----------------------

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

卢克·切瑟Unsplash 上拍摄的照片

在本文中,我将介绍和分析我用来清理、构建和可视化这个数据集的几个步骤。

几周前,我在推特上看到一个数据集。这个数据集是由两个 NLP 的博士生放在一起的,目的是激励 NLP 和语言学研究。在最初检查了数据集并与创建者交谈后,我从 GitHub 中分出了存储库,并开始探索数据(https://github.com/yuvalpinter/nytwit)。

我碰到的原推文(谢谢,推特!)

在处理这个数据集时,我首先采取了一些初步的 EDA 措施:

  1. 查看我们正在使用的数据集的类型
  2. 检查形状(行、列)
  3. 查看头部和尾部(第一行和最后 5 行)
  4. 检查所有行和列(。信息())
  5. 获取一些介绍性的统计数据(。describe())
  6. 用获取值计数。值计数()

1.查看数据集的类型

这将使我们更好地理解我们正在处理的数据,以及可用于转换数据和检索见解的方法。为此,我们只需运行以下代码:

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

类型(数据)告诉我们,我们正在处理一个熊猫数据帧

告诉我们,我们正在处理一个“二维的、大小可变的、潜在异构的表格数据集”虽然这个语句中有很多内容需要解开,但这意味着我们的数据具有可以转换和过滤(大小可变)的行和列(二维),并且这些可转换的行和列包含潜在的异构数据类型(int64、float64、object、bool、datetime64 的混合)。

2.检查形状(行、列)

接下来,为了检查数据集中有多少行和列,我们编写以下代码行:

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

data.shape 将返回数据集的行数和列数

如上所述,(2587, 4)告诉我们有 2587 行和 4 列。

3.查看头部和尾部(第一行和最后 5 行)

现在我们对数据的大小和类型有了更好的了解,我们想检查一下我们正在处理哪种单元格值和列。这将让我们更好地了解我们可以用数据集合理地计算/测量什么。以下代码行将完成这一任务:

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

data.head()将返回数据集的前五行

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

data.tail()将返回数据集的最后 5 行

4.检查所有行和列

在我们知道数据的类型、大小和条目之后,分析每一列的数据类型是很重要的。这是通过使用。info(),如下面的操作所示:

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

。info()正在运行!

因此,到目前为止,我们的一些主要收获是:

  1. 我们正在处理一个熊猫数据框架
  2. 我们有 2587 行和 4 列
  3. 这 4 列是时间、单词、类别和 URL。时间列统一表示为{Day of Week} {Month} {Day of Month} {Minutes}:{Hours}:{Seconds} {Year}。这将很容易将时间和日期分成单独的列,并通过索引访问特定的值。最后,类别和时间列包含重复的条目(我们可以从。头()和。tail())。
  4. 我们的列中没有空值,所有的数据类型(Dtype)都是Object(我们正在处理干净的数据)。

这些都是我们可以用来构建的好点子。形容()。value_counts()

5.获取一些介绍性的统计数据(。describe())

现在,关键是我们要对数据集进行一些基本的统计测量。即使我们所有的数据类型都是对象(而不是 int64),我们仍然可以通过使用。describe(include = NP . object)(注意: **include = np.object** 当你有对象非对象 Dtypes 时很有帮助,但也可以只包含对象 Dtypes )。代码如下所示:

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

。描述(include=np.object)

在这里,我们找到了数据集中最频繁出现的时间、单词、类别和 URL 值。这让我们深入了解我们可以可视化的内容(使用直方图、散点图等)。)以及我们可能需要在数据集中清理的东西(冗余行、不正确或意外的值等)。).接下来,我们需要更好地了解数据集列中的唯一值。这将有助于我们发现创建有效子数据集和将重要值分组在一起的方法。

6.用获取值计数。值计数()

我们可以利用。value_counts()在我们所有的列上,但是为了这个例子,我将把重点放在“Category”列上。代码如下:

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

类别列中的所有唯一值及其频率。

现在,我们对数据集有了很好的感觉,并准备构建一些可视化工具来传达我们发现的信息。首先,让我们使用 Plotly 和我们的“T3”数据框架构建一个直方图。

我喜欢 Plotly 的一点是它的简单性和交互性,这是我构建的每个情节的标准。虽然下面的截图不会包括交互式触摸,但我鼓励你去看看他们的图形库(https://plotly.com/)。代码如下:

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

这是输出结果:

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

简单易用的直方图

最后,让我们用 Seaborn 构建一个 distplot。在这里,我们将可视化每个月的每个日期有多少单词被添加到数据集(跨所有月份)。下面是我们将编写的代码,用于设置适当的 panda 系列和 distplot:

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

我们输入 sns.distplot()的熊猫系列代码

这里,我们创建一个空列表(date_df),我们可以用来自df['Time']的值填充它。每次我们遍历df['Time']时,我们都会将val[8:10](一个月中的几天)添加到date_df列表中。我们用[8:10]索引val,因为这是{Day of Week} {Month} {**Day of Month**} … {Year} 中日期索引出现的地方。在这之后,我们将date_df转换成熊猫系列(因为我们想要一个一维数组,而不是一个列表)。最后,我们用kde = False(删除内核密度估计值)、fit = norm(用正态分布拟合我们的图)和color='g'(将我们的频率条格式化为绿色)来配置 distplot。总的来说,这产生了以下视觉效果:

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

使用 Seaborn 快速简单地构建 distplot()。

这是一个非常基本的视觉需要一些工作,但它是一个良好的开端,为我们提供了宝贵的信息!

在本文中,我们讨论了 ed a 的许多重要部分。在接下来的几周里,我希望通过使用其他数据集和讨论我在处理数据时采取的其他重要步骤来继续建立这一基础。一些策略可能广泛适用于不同的数据集,而另一些策略可能专门针对我在文章中讨论的那个数据集。无论如何,我相信它会帮助你继续像数据科学家一样思考!直到下次,

丹·m。

附注:和往常一样,请随时向 danielmurph8@gmail.com 提出问题、意见和担忧。谢谢!

在 Pandas 数据框架中使用日期时间

原文:https://towardsdatascience.com/working-with-datetime-in-pandas-dataframe-663f7af6c587?source=collection_archive---------0-----------------------

帮助你开始数据分析的一些熊猫技巧

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

照片由卢卡斯·布拉塞克Unsplash 上拍摄

Datetime 是数据科学项目中常见的数据类型。通常,您会使用它并遇到问题。我发现 Pandas 是一个了不起的库,它包含了处理日期和时间的广泛功能和特性。

在本文中,我们将讨论以下常见的日期时间问题,应该可以帮助您开始数据分析。

  1. 将字符串转换为日期时间
  2. 从多个列组合一个日期时间
  3. 获取年、月和日
  4. 获取一年中的星期、星期几和闰年
  5. 从出生日期得到年龄
  6. 通过将日期列设置为索引来提高性能
  7. 选择特定年份的数据并执行聚合
  8. 选择具有特定月份和特定日期的数据
  9. 选择两个日期之间的数据
  10. 处理缺失值

源代码请查看我的 Github repo

1.将字符串转换为日期时间

Pandas 有一个名为to_datetime()的内置函数,可以用来将字符串转换为日期时间。让我们看一些例子

使用默认参数

Pandas to_datetime()能够将任何有效的日期字符串解析为 datetime,而不需要任何额外的参数。例如:

df = pd.DataFrame({**'date': ['3/10/2000', '3/11/2000', '3/12/2000']**,
                   'value': [2, 3, 4]})df['date'] = **pd.to_datetime(df['date'])** df

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

首日格式

默认情况下,to_datetime()会先解析月( MM/DD 、 **MM DD、**或 MM-DD )格式的字符串,这种安排在美国是比较独特的。

在世界上的大多数地方,日期是先写的( DD/MMDD MMDD-MM )。如果你想让熊猫先考虑日子而不是月份,你可以将参数dayfirst设置为True

df = pd.DataFrame({**'date': ['3/10/2000', '3/11/2000', '3/12/2000']**,
                   'value': [2, 3, 4]})df['date'] = pd.to_datetime(df['date'], **dayfirst=True**)
df

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

或者,您可以向参数format传递一个自定义格式。

自定义格式

默认情况下,使用来自dateutil.parser.parse的 Pandas 内置解析器解析字符串。有时,您的字符串可能是自定义格式,例如, YYYY-DD-MM HH:MM:SS 。熊猫to_datetime()有一个名为format的参数,它允许你传递一个自定义格式:

df = pd.DataFrame({**'date': ['2016-6-10 20:30:0', 
                            '2016-7-1 19:45:30', 
                            '2013-10-12 4:5:1']**,
                   'value': [2, 3, 4]})df['date'] = pd.to_datetime(df['date'], **format="%Y-%d-%m %H:%M:%S"**)
df

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

infer_datetime_format加速解析

如果不是 ISO8601 格式而是常规格式,传递infer_datetime_format=True通常可以加快解析速度。根据[1],在某些情况下,这可以将解析速度提高 5-10 倍。

# Make up 3000 rows
df = pd.DataFrame({'date': ['3/11/2000', '3/12/2000', '3/13/2000'] * 1000 })%timeit pd.to_datetime(df['date'], **infer_datetime_format=True**)
100 loops, best of 3: **10.4 ms** per loop%timeit pd.to_datetime(df['date'], **infer_datetime_format=False**)
1 loop, best of 3: **471 ms** per loop

处理解析错误

如果日期字符串不符合时间戳格式,您将得到一个类型错误

df = pd.DataFrame({'date': ['3/10/2000', **'a/11/2000'**, '3/12/2000'],
                   'value': [2, 3, 4]})
df['date'] = pd.to_datetime(df['date'])

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

to_datetime()有一个名为errors的参数,允许您忽略错误或将无效值强制给NaT

df['date'] = pd.to_datetime(df['date'], **errors='ignore'**)
df

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

df['date'] = pd.to_datetime(df['date'], **errors='coerce'**)
df

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

此外,如果您想在从 CSV 文件中读取数据时解析日期列,请查看下面的文章

[## 您应该知道的用 Pandas read_csv()解析日期列的 4 个技巧

一些最有用的熊猫把戏

towardsdatascience.com](/4-tricks-you-should-know-to-parse-date-columns-with-pandas-read-csv-27355bb2ad0e)

2.从多个列组合一个日期时间

to_datetime()也可以用于从多个列组合一个日期时间。键(列标签)可以是常见的缩写,如 [‘年’,‘月’,‘日’,‘分钟’,‘秒’,’ ms ‘,’ us ‘,’ ns’]) 或相同的复数。

df = pd.DataFrame(**{'year': [2015, 2016],
                   'month': [2, 3],
                   'day': [4, 5]}**)df['date'] = pd.to_datetime(df)
df

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

3.获取年、月和日

dt.yeardt.monthdt.day是从熊猫日期时间对象中获取年、月和日的内置属性。

首先,让我们创建一个伪 DateFrame 并将 DoB 解析为 datetime。

df = pd.DataFrame({'name': ['Tom', 'Andy', 'Lucas'],
                 'DoB': ['08-05-1997', '04-28-1996', '12-16-1995']})
df['DoB'] = **pd.to_datetime(df['DoB'])**

得到年、月、日

df['year']= **df['DoB'].dt.year**
df['month']= **df['DoB'].dt.month**
df['day']= **df['DoB'].dt.day**
df

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

4.获取一年中的星期、星期几和闰年

类似地,dt.weekdt.dayofweekdt.is_leap_year是获取一年中的星期、星期几和闰年的内置属性。

df['week_of_year'] = **df['DoB'].dt.week**
df['day_of_week'] = **df['DoB'].dt.dayofweek**
df['is_leap_year'] = **df['DoB'].dt.is_leap_year**
df

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

请注意,Pandas **dt.dayofweek**属性返回一周中的某一天,并且假设该周从星期一开始,用 0 表示,到星期天结束,用 6 表示。要用全名替换号码,我们可以创建一个映射并将其传递给map():

**dw_mapping={
    0: 'Monday', 
    1: 'Tuesday', 
    2: 'Wednesday', 
    3: 'Thursday', 
    4: 'Friday',
    5: 'Saturday', 
    6: 'Sunday'
}** 
df['day_of_week_name']=df['DoB'].dt.weekday**.map(dw_mapping)**
df

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

5.从出生日期得到年龄

获得年龄的最简单方法是减去年份:

**today = pd.to_datetime('today')**
df['age'] = **today.year - df['DoB'].dt.year**df

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

然而,这是不准确的,因为人们今年可能还没有过生日。更准确的解决方案是考虑生日

# Year difference
today = pd.to_datetime('today')
diff_y = today.year - df['DoB'].dt.year
# Haven't had birthday
b_md = df['DoB'].apply(lambda x: (x.month,x.day) )
**no_birthday = b_md > (today.month,today.day)**df['age'] = **diff_y - no_birthday**
df

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

6.通过将日期列设置为索引来提高性能

按日期选择数据的常见解决方案是使用布尔马克斯。例如

condition = (df['date'] > start_date) & (df['date'] <= end_date)
df.loc[condition]

该解决方案通常要求start_dateend_date日期列为日期时间格式。事实上,当您在大型数据集中按日期进行大量选择时,这种解决方案会很慢。

如果您要按日期进行大量选择,首先将日期列设置为索引会更快,这样您就可以利用 Pandas 内置的优化。然后,您可以使用df.loc[start_date:end_date]按日期选择数据。让我们看一个示例数据集 city_sales.csv,它有 1,795,144 行数据

df = pd.read_csv('data/city_sales.csv',parse_dates=['date'])
df.info()RangeIndex: **1795144** entries, 0 to 1795143
Data columns (total 3 columns):
 #   Column  Dtype         
---  ------  -----         
 0   date    datetime64[ns]
 1   num     int64         
 2   city    object        
dtypes: datetime64[ns](1), int64(1), object(1)
memory usage: 41.1+ MB

将日期列设置为索引

**df = df.set_index(['date'])**
df

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

7.选择特定年份的数据并执行聚合

假设我们想要选择 2018 年的所有数据

df.loc['2018']

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

并对选择执行聚合,例如:

获取 2018 年总人数

df.loc**['2018','num'].sum()****1231190**

获取 2018 年每个城市的总人数

df['2018']**.groupby('city').sum()**

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

8.选择具有特定月份和特定日期的数据

选择特定月份的数据,例如 2018 年 5 月

df.loc[**'2018-5'**]

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

类似地,选择一个月中特定日期的数据,例如 2018 年 5 月 1 日

df.loc[**'2018-5-1'**]

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

9 选择两个日期之间的数据

要选择两个日期之间的数据,可以使用df.loc[start_date:end_date]例如:

选择 2016 年到 2018 年之间的数据

df.loc[**'2016' : '2018'**]

选择 2018 年 5 月 2 日 10 点到 11 点之间的数据

df.loc[**'2018-5-2 10' : '2018-5-2 11'** ]

选择 2018 年 5 月 2 日 10:30 至 10:45 之间的数据

df.loc[**'2018-5-2 10:30' : '2018-5-2 10:45'** ]

而要选择时间之间的数据,我们要用between_time(),比如 10:30 和 10:45

df.**between_time('10:30','10:45')**

10 处理缺失值

我们经常需要计算窗口统计,例如滚动平均值滚动总和。

让我们计算 3 个窗口期间的滚动总和,然后看看前 5 行。

df['rolling_sum'] = **df.rolling(3).sum()**
df.head()

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

我们可以看到,只有当有 3 个周期可以回顾时,它才开始有有效值。处理这个问题的一个解决方案是通过回填数据

df['rolling_sum_backfilled'] = **df['rolling_sum'].fillna(method='backfill')**
df.head()

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

有关回填的更多细节,请查看以下文章

[## 处理熊猫中缺失的值

关于 Pandas 中缺失值以及如何使用内置方法处理它们的教程

towardsdatascience.com](/working-with-missing-values-in-pandas-5da45d16e74)

好了

感谢阅读。

请在我的 Github 笔记本上查看源代码。

如果你对机器学习的实用方面感兴趣,请继续关注。

以下是为您挑选的一些文章:

参考

使用 Geofence API 锁定特定区域的用户

原文:https://towardsdatascience.com/working-with-google-geofence-api-2e04a227ff27?source=collection_archive---------32-----------------------

Geofence API 为设备提供了监控全球圆形区域的能力

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

KOBU 机构Unsplash 上拍摄的照片

当你想通知用户他们是否在世界上的特定区域附近(如购物中心、机场、餐馆等)时,可以使用谷歌地理围栏 API。).例如,客户安装了一个在线购物应用程序,如果他们在与该应用程序有联系的商店附近,商店可以在应用程序中生成通知,让客户知道他们附近的商店正在为他们提供特价。这样你可以吸引更多的用户。

获取谷歌地图 API 密钥

要使用 Geofence API,您必须通过以下步骤获取 Google Maps API 密钥。

  • 点击 这个 链接打开谷歌云平台,如果你还没有登录,那么请先登录并创建项目。

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

你可以给这个项目起任何名字,或者你可以让它保持原样,然后现在点击创建

  • 现在确保您已经选择了新创建的项目。

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

  • 现在点击左边的菜单图标,选择API&服务,然后从子列表中选择
  • 搜索 Maps SDK for Android启用该 API(不要忘记启用 API,因为这很重要)
  • 现在再次点击左侧的菜单图标,选择凭证,然后选择 +创建凭证,最后点击 API 键
  • 复制这个 API 键并将其粘贴到 string.xml 文件中

我们将要建造的

我们将构建一个应用程序,允许您在地图上设置您的位置(或者准确地说是特定区域)。当你到达指定位置时,你的应用程序会用你在设置位置时定义的消息通知你。

最后,你会有一个看起来像这样的应用程序。

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

要在地图上设置位置,您可以将标记拖到所需的位置。指定位置后,您可以点击继续按钮继续。

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

你现在兴奋吗?我相信你会的。

我们会学到什么?

  • 谷歌地图 API
  • 谷歌地理围栏 API
  • 服务
  • 广播收音机
  • 创建通知
  • 在共享首选项中存储和检索自定义对象

现在,让我们快速设置我们的项目并继续。

设置 Android 项目

  • 创建一个新的 Android 项目,选择空的 activity,给你的项目起一个你想要的名字。
  • 在您的应用程序级 build.gradle 中添加以下依赖项
implementation **'com.google.android.gms:play-services-location:16.0.0'** implementation **'com.google.android.gms:play-services-maps:16.0.0'** implementation **'com.google.code.gson:gson:2.8.5'** implementation **"com.google.android.material:material:1.1.0-alpha02"**
  • 如果在 activity.xml 文件中遇到以下错误

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

然后在您的 build.gradle 中添加以下代码行

android{ defaultConfig {
vectorDrawables.useSupportLibrary = **true**}}
  • 在您的**AndroidManifest.xml**文件中添加以下权限
<**uses-permission android:name="android.permission.INTERNET"** />
<**uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"** />
  • 现在将下面的元数据标签添加到您的**AndroidManifest.xml**文件中的**<application>**标签内,稍后我们将在这里添加 API_KEY
<**meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="API_KEY"**/>
  • 最后,同步项目和设置完成。

让我们快速熟悉一下这个应用中使用的一些 android 组件

**服务:**服务是一个 android 组件,用来执行后台任务。任务可以是任何类型的,例如上传照片、同步服务器上的用户数据和生成通知等。

**广播接收器:**广播接收器也是 android 的一个基本组件,当你想在你的应用程序中执行一些基于未来事件的操作时,它会很方便。例如,您只希望在手机连接到 WIFI 时将用户数据上传到您的服务器,另一个例子是,您希望在用户重启手机时启动后台服务。

pending content:PendingIntent类似于正常意图,只是不会立即执行。

让我们了解我们的班级

我把所有的类、实用函数和常量都放在根包中。

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

让我们逐一了解。

  • MainActivity.kt: 这是我们的主屏幕,它将向您显示谷歌地图和右下角的两个操作按钮。第一个按钮将允许您放大地图,加号操作按钮将打开新活动**ReminderActivity.kt**,在此屏幕上您可以选择您的位置,设置消息并保存提醒。
  • ReminderActivity.kt: 正如我所解释的,这个活动将允许您设置位置并将提醒保存在共享首选项中。当您选择位置并保存它时,您将看到您指定的位置周围的圆圈。这个圆是你将在代码中定义的边界或半径。圆圈越大,意味着它将覆盖更大的区域。当你进入这个区域,你会收到一个通知。
  • MyBroadcast Receiver: 这是一个广播接收器,当用户到达指定位置时会被触发。当这个广播接收器触发时,它将执行后台服务。
  • 这是一个将由我们的广播接收机执行的服务,当它执行时,它生成通知,让用户知道他已经到达他的目的地。
  • 这是一个数据类,在它的构造函数中,我接受了四个参数,它们是唯一的 id、纬度、经度、半径和一条消息。我们将把这些信息保存在共享首选项中,同时,我们将设置待定意向来生成通知。
  • **myreminderRepository . kt:**如你所知,repository 帮助我们获得应用程序的核心方法来执行我们为应用程序定义的操作。所以只有这个类负责添加一个新的提醒,删除提醒和设置挂起的意图。我们将总是创建这个类的对象并执行动作。
  • Utils.kt: 这是我们的实用程序类,包含四个方法。**vectorToBitmap()**方法将矢量图像转换为位图,**showReminderInMap()**方法在地图上显示提醒,这是围绕指定位置画圆的方法。而**sendNotification()**方法创建了通知,这个方法由我们的后台服务调用。最后一个方法是**getUniqueId()**,它显然返回唯一的 id。
  • AppConstants.kt: 这个类包含了这个应用使用的所有常量。这个类还包含一个服务方法和两个调用MainActivity.ktReminderActivity.kt活动的方法。

说够了吗?让我们回到正题…

当您创建一个项目时,您将拥有一个默认的 ActivityMain.kt 类。我要你再创建一个类,命名为 ReminderActivity.kt,在这个类中扩展AppCompatActivity()类,实现OnMapReadyCallback谷歌地图的接口,实现其方法onMapReady()。现在,让一切保持原样。

现在我们将创建我们的 AppConstants 类,并定义所有的常量和一些方法。

在这个类中,我们定义了我们的常量和三个方法,**newintentforminactivity()**方法将调用MainActivity.kt类,**newIntentForReminderActivity()**将调用ReminderActivity.kt类。我们的广播接收器将调用 enqueueWork()方法来生成通知。
**提示:**所有的常量和方法都定义在**companion object**里面,这是因为无论何时我们需要调用这些方法中的任何一个,我们都不必创建这个类的对象。

现在我们将创建 Utils.kt 并添加以下方法

vectorToBimap()

当我们调用这个方法时,我们必须传递两个参数,即context.resources和需要转换成位图的图像的 id。context.*resources* 为您的应用程序返回一个资源实例。在这个方法中,我得到了 drawable 对象,创建了位图画布对象。我们现在在vectorDrawable中有了我们的图像,我们将把这个对象转换成bitmap,然后在画布上绘制位图,设置它的边界并返回。

提示:如果你不熟悉画布,那么你可以把它想象成一张白纸,你可以在上面画出任何形状。

showReminderInMap()

**showReminderInMap()**方法将上下文、 GoogleMap 对象和 MyReminder 类对象作为参数,并检查myReminder是否有值,然后继续执行,否则不执行任何操作。在***if***语句中,我从 myReminder 对象中获取纬度和经度,并将其转换为LatLng,在第二个***if***语句中,我在指定位置周围画圆。一般来说,这种方法用于在指定的位置上画圆。

发送通知()

sendNotification()方法非常简单,它除了创建通知之外什么也不做。从服务中调用此方法来显示通知。

MyBroadcastReceiver.kt 类

现在在您的根目录中创建这个类,并将这个广播接收器添加到您的**AndroidManifest.xml**文件中。

<**receiver
    android:name=".MyBroadcastReceiver"
    android:enabled="true"
    android:exported="true"** />

这个类扩展了抽象类BroadcastReceiver()并实现了它的onReceive()方法。当 onReceive()方法被执行时,它将执行AppConstants.kt.enqueueWork()方法

当您添加提醒时,该接收器将通过PendingIntent注册。

GeofenceTransitionService.kt

创建这个类并在AndroidManifest.xml文件中声明它

<**service
    android:name=".GeofenceTransitionService"
    android:exported="true"
    android:permission="android.permission.BIND_JOB_SERVICE"** />

**注意:**如果在清单中声明了一个作业服务,但没有使用 BIND_JOB_SERVICE 权限保护,该服务将被系统忽略。

这是一个扩展了JobIntentService类的服务,IntentService 或 JobIntentService 是一个在后台独立线程上运行的服务,它在任务完成时被销毁。要使用这个类,我们必须实现它所需的方法**onHandleWork()**

**class** GeofenceTransitionService : JobIntentService() {**override fun** onHandleWork(intent: Intent) {

    **val** geofencingEvent = GeofencingEvent.fromIntent(intent)
    **repositoryMy** = MyReminderRepository(**this**)

    **if** (geofencingEvent.hasError()) {
        Log.e(AppConstants.**LOG_TAG**, **"An error occured"**)
        **return** }
    handleEvent(geofencingEvent)
}
}

在这个方法中,我们可以完成我们的任务。我在这个方法中获取**GeofencingEvent**类的对象,如果有问题,我们显示错误消息,否则我们调用handleEvent()方法。

**private fun** handleEvent(event: GeofencingEvent) {

    **if** (event.*geofenceTransition* == Geofence.*GEOFENCE_TRANSITION_ENTER*) {

        **val** reminder = getFirstReminder(event.*triggeringGeofences*)
        **val** message = reminder?.**message
        val** latLng = reminder?.**latLng
        if** (message != **null** && latLng != **null**) {
            *sendNotification*(**this**, message, latLng)
        }
    }
}

在此方法中,我们首先使用 GeofencingEvent 对象,检查用户是否进入了指定区域,如果是,则从存储库中获取 reminder 对象,检索其数据并发送通知。

**private fun** getFirstReminder(triggeringGeofences: List<Geofence>): MyReminder? {
    **val** firstGeofence = triggeringGeofences[0]
    **return repositoryMy**.get(firstGeofence.*requestId*)
}

**getFirstReminder()**方法从MyRepository类返回MyReminder类的对象。

下面是这个服务类的完整代码

提示: **lateinit** 是 Kotlin 中的一个关键字,可以让你在声明的时候不用初始化它,但是在使用之前应该初始化它,否则你会遇到 NullPointerException

MyReminderRepository.kt

您可以将该类视为本地服务器,这意味着它为我们提供了在地图上执行任何操作所需的一切,例如添加提醒或删除提醒以及维护所有提醒。

以下代码片段创建了一个 GeofencingClient 对象,帮助您操作地理围栏。

**private val geofencingClient** = LocationServices.getGeofencingClient(**context**)

这里我们得到了 SharedPreference 对象和 Gson 对象

**private val preferences** = **context**.getSharedPreferences(*PREFS_NAME*, Context.*MODE_PRIVATE*)
**private val gson** = Gson()

SharedPreference中,我们将存储提醒,并使用Gson对象将 json 字符串转换成 Java 对象,反之亦然。既然 SharedPreference 只能保存键值对形式的原始值,但是我们想要存储我们的类对象,那么这怎么可能呢?。显然,我们可以将我们的对象转换成 json 字符串并存储在 SharedPreference 中,每当我们需要使用它时,我们都会将其转换成一个真实的对象。

buildGeofence():

纬度、经度和半径的组合称为地理围栏。它是地图上特定位置的圆形区域。每当用户进入这个区域,应用程序就会触发特定的行为。

**private fun** buildGeofence(myReminder: MyReminder): Geofence? {
    **val** latitude = myReminder.**latLng**?.**latitude
    val** longitude = myReminder.**latLng**?.**longitude
    val** radius = myReminder.**radius

    if** (latitude != **null** && longitude != **null** && radius != **null**) {
        **return** Geofence.Builder()
            .setRequestId(myReminder.**id**)
            .setCircularRegion(
                latitude,
                longitude,
                radius.toFloat()
            )
            .setTransitionTypes(Geofence.*GEOFENCE_TRANSITION_ENTER*)
            .setExpirationDuration(Geofence.*NEVER_EXPIRE*)
            .build()
    }

    **return null** }

看看代码,我们正在使用 Geofence 构建地理围栏。构建器()

在方法 setRequestId() 中,我传递了唯一的 Id,因为每个 geofence 都是唯一的,您将从您的模型类中获得这个 id。

setCircularRegion() 中,我正在设置我的纬度、经度和半径,所有这些参数一起在一个特定的位置上形成一个圆

setTransitionTypes() 定义了过渡类型,可以使用*GEOFENCE_TRANSITION_ENTER* 在用户进入定义区域时触发事件。

**setExpirationDuration()**方法设置 geofence 的到期时间,我们使用的是*NEVER_EXPIRE* ,这意味着它永远不会到期,直到用户删除它。

buildFencingRequest():

您将使用此方法构建 geofence 请求。

**private fun** buildGeofencingRequest(geofence: Geofence): GeofencingRequest {
    **return** GeofencingRequest.Builder()
        .setInitialTrigger(0)
        .addGeofences(*listOf*(geofence))
        .build()
}

这里,您将**setinitiatrigger()**设置为 0,这意味着您不希望您的应用程序在已经位于 geofence 内部时触发事件。下一个 addGeofences()方法将请求添加到 geofence。

建筑地理围栏待定内容:

正如您在代码片段中看到的,我们在 intent 对象中传递 MyBroadcastReceiver,这意味着当调用此待定 intent 时,它将调用 BroadcastReceiver,而此 Receiver 将调用geofencetransationservice。

**private val geofencePendingIntent**: PendingIntent **by** *lazy* **{
    val** intent = Intent(**context**, MyBroadcastReceiver::**class**.*java*)
    PendingIntent.getBroadcast(
        **context**,
        0,
        intent,
        PendingIntent.*FLAG_UPDATE_CURRENT* )
**}**

该代码只有在*GEOFENCE_TRANSITION_ENTER*事件被触发时才会被执行。

提示:通过 *惰性属性,*该值仅在第一次使用时计算。

添加():

这个add()方法将提醒添加到 SharedPreference 中,并将请求添加到 geofence 中。

**fun** add(myReminder: MyReminder, success: () -> Unit, failure: (error: String) -> Unit)
{
    **val** geofence = buildGeofence(myReminder)
    **if** (geofence != **null** && ContextCompat.checkSelfPermission(**context**, android.Manifest.permission.*ACCESS_FINE_LOCATION*) == PackageManager.*PERMISSION_GRANTED*) {
        **geofencingClient**.addGeofences(buildGeofencingRequest(geofence), **geofencePendingIntent**)
            .addOnSuccessListener **{** saveAll(getAll() + myReminder)
                success()
            **}** .addOnFailureListener **{** failure(**"An error occured"**)
            **}** }
}

首先,我们检查权限是否被授予,然后我们继续,接下来,我们使用 geofencingClient 创建地理围栏。如果成功,那么只有我们将保存在共享首选项中,否则报告错误消息。

移除():

这个方法只是从列表中删除提醒。

**fun** remove(myReminder: MyReminder) {
    **val** list = getAll() - myReminder
    saveAll(list)
}

saveAll():

此方法会将列表中的所有提醒事项添加到共享偏好设置中。

**private fun** saveAll(list: List<MyReminder>) {
    **preferences** .edit()
        .putString(*REMINDERS*, **gson**.toJson(list))
        .apply()
}

注意,我们将列表转换为 Json 字符串,然后存储它。

getAll():

该方法返回我们存储在共享首选项中的所有提醒列表。

**fun** getAll(): List<MyReminder> {
    **if** (**preferences**.contains(*REMINDERS*)) {
        **val** remindersString = **preferences**.getString(*REMINDERS*, **null**)
        **val** arrayOfReminders = **gson**.fromJson(
            remindersString,
            Array<MyReminder>::**class**.*java* )
        **if** (arrayOfReminders != **null**) {
            **return** arrayOfReminders.*toList*()
        }
    }
    **return** *listOf*()
}

getLast():

这个函数返回添加到列表中的提醒。

**fun** getLast() = getAll().*lastOrNull*()

注意这个函数首先调用返回列表的getAll()方法,我们调用lastOrNull()函数,它是List类的一个方法,这个方法返回最后一个值。

get():

如果列表不是null.,该方法从列表中返回第一个提醒

**fun** get(requestId: String?) = getAll().*firstOrNull* **{ it**.**id** == requestId **}**

{it.id == requestId }是执行空值检查的 lambda 表达式。当 lambda 表达式中只有一个参数时,可以使用**it**

MyReminderRepository.kt 的完整代码

ReminderActivity.kt:

这个活动允许我们在指定的位置添加新的提醒。

首先,在本活动中创建这些对象。

**private lateinit var map**: GoogleMap

**lateinit var myRepository**: MyReminderRepository

**private var reminder** = MyReminder(latLng = **null**, radius = **null**, message = **null**)

现在将下面的代码粘贴到您的onCreate()方法中

**override fun** onCreate(savedInstanceState: Bundle?) {
  **super**.onCreate(savedInstanceState)
  setContentView(R.layout.*activity_new_reminder*)
  **myRepository** = MyReminderRepository(**this**)
  **val** mapFragment = *supportFragmentManager* .findFragmentById(R.id.*map*) **as** SupportMapFragment
  mapFragment.getMapAsync(**this**)

  instructionTitle.*visibility* = View.*GONE* radiusDescription.*visibility* = View.*GONE* message.*visibility* = View.*GONE* }

在这个 onCreate()方法中,我初始化了我的存储库实例变量并设置了映射。最初,我隐藏了所有的文本视图。

中央摄像机():

在这种方法中,我从意图中获取缩放值,并将相机设置在位置的中心。

**private fun** centerCamera() {
  **val** latLng = *intent*.*extras*?.get(AppConstants.**LAT_LNG**) **as** LatLng
  **val** zoom = *intent*.*extras*?.get(AppConstants.**ZOOM**) **as** Float
  **map**.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoom))
}

showReminderUpdate():

此方法更新提醒。在调用这个方法的时候,它会显示出这个位置周围的圆圈。

**private fun** showReminderUpdate() {
  **map**.clear()
  *showReminderInMap*(**this**, **map**, **reminder**)
}

showConfigurationLocationStep():

这种方法只是指导您如何在地图上导航。

**private fun** showConfigureLocationStep() {
  marker.*visibility* = View.*VISIBLE* instructionTitle.*visibility* = View.*VISIBLE* radiusDescription.*visibility* = View.*GONE* message.*visibility* = View.*GONE* instructionTitle.*text* = **"Drag map to set location"** next.setOnClickListener **{
    reminder**.**latLng** = **map**.*cameraPosition*.**target** showConfigureRadiusStep()
  **}** showReminderUpdate()
}

showConfigurationMessageStep():

这个方法和上面的方法一样,它指导你如何添加消息。当您收到通知时,会显示此消息。

**private fun** showConfigureMessageStep() {
  marker.*visibility* = View.*GONE* instructionTitle.*visibility* = View.*VISIBLE* message.*visibility* = View.*VISIBLE* instructionTitle.*text* = **"Enter a message"** next.setOnClickListener **{

    reminder**.**message** = message.*text*.toString()

    **if** (**reminder**.**message**.*isNullOrEmpty*()) {
      message.*error* = **"Message Error"** } **else** {
      addReminder(**reminder**)
    }
  **}** showReminderUpdate()
}

addReminder():

该方法从 MyReminderRepository 类调用 add 方法并添加提醒。如果提醒添加成功,那么它会在底部显示一条消息“提醒已添加”,否则会显示一条失败消息。

**private fun** addReminder(myReminder: MyReminder) {

  **myRepository**.add(myReminder,
      success = **{** setResult(Activity.*RESULT_OK*)
        finish()
      **}**,
      failure = **{** Snackbar.make(main, **it**, Snackbar.*LENGTH_LONG*).show()
      **}**)
}

最后修改您的 onMapReady()

**override fun** onMapReady(googleMap: GoogleMap) {
  **map** = googleMap
  **map**.*uiSettings*.*isMapToolbarEnabled* = **false** centerCamera()

  showConfigureLocationStep()
}

在这个方法中,我们将GoogleMap对象分配给我们的地图,然后配置设置。

此活动的完整代码

此活动的布局文件

在标签中你唯一能注意到的是"**Android:name = " com . Google . Android . GMS . maps . supportmapfragment "**行,这一行帮助你在屏幕上添加谷歌地图。这是添加地图最简单的方法。这个文件中的其余代码对您来说一定非常熟悉。

主活动. kt:

最后,这是我们代码的最后一部分。这是我们的家庭活动,当你第一次与应用程序互动时,你会看到它。

首先,在您的活动中添加这些侦听器,并实现它的所有方法。

**class** MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener

现在创建这些实例变量。

**lateinit var myRepository**: MyReminderRepository

 **private var map**: GoogleMap? = **null

 private lateinit var locationManager**: LocationManager

onCreate():

在这个方法中,我们首先初始化实例变量,然后从 activity_main.xml 文件的一个片段和动作按钮上的侦听器中获取一个映射。最初,这些按钮将不可见,直到用户给予 ACCESS_FINE_LOCATION 权限**。**当用户授予权限时,我们在**onMapAndPermissionReady()**方法中使按钮对用户可见。

看看下面的代码,我在按钮上附加了一个监听器,在 lambda 表达式中,我调用了 Google map 类的**run**方法。这个run也是一个λ函数。当用户按下 (+) 动作按钮时,我们将用户带到 ReminderActivity.kt 类。

actionNewReminder.setOnClickListener **{
    map**?.*run* **{
        val** intent = AppConstants.newIntentForReminderActivity(
            **this**@MainActivity,
            *cameraPosition*.**target**,
            *cameraPosition*.**zoom** )
        startActivityForResult(intent, AppConstants.**REMINDER_REQUEST_CODE**)
    **}
}**

onMapAndPermissionReady():

当许可被授予并且地图不等于null时,那么我们使我们的按钮可见并且添加一个监听器到actionCurrentLocation按钮。现在,如果用户按下actionCurrentLocation按钮,我们将获得位置提供者和用户的最后已知位置。如果这个不为空,那么我们将把摄像机移动到最后一个已知的位置。

onMarkerClick():

当用户点击标记时,会弹出一个警告对话框,询问用户是否想删除提醒,如果他按“是”,那么该提醒会从列表中删除。

**override fun** onMarkerClick(marker: Marker): Boolean {
    **val** reminder = **myRepository**.get(marker.*tag* **as** String)

    **if** (reminder != **null**) {
        showReminderRemoveAlert(reminder)
    }

    **return true** }

showreminderemovealert():

这个方法只创建一个有两个选项的警告对话框,即 OKCancel ,如果用户点击“OK”,那么我们调用 removeReminder()来删除它。

**private fun** showReminderRemoveAlert(myReminder: MyReminder) {
    **val** alertDialog = AlertDialog.Builder(**this**).create()
    alertDialog.*run* **{** setMessage(**"Reminder Removed"**)
        setButton(
            AlertDialog.*BUTTON_POSITIVE*,
            **"OK"** ) **{** dialog, _ **->** removeReminder(myReminder)
            dialog.dismiss()
        **}** setButton(
            AlertDialog.*BUTTON_NEGATIVE*,
            **"Cancel"** ) **{** dialog, _ **->** dialog.dismiss()
        **}** show()
    **}** }

ActivityMain.kt 文件的完整代码

现在,我们终于有了 activity_main.xml 文件

运行和测试

  • 运行应用程序并给予许可。
  • 如果你在模拟器上运行应用程序,然后点击三个点,你会看到下面的屏幕。现在设置任何你想要的位置(我设置了新德里的康诺特广场)。并保存该位置。

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

  • 现在,单击应用程序上的第一个操作按钮,您会看到您的地图将导航到指定的位置。
  • 现在通过点击(+)操作按钮添加提醒,拖动地图设置您的位置,点击继续,现在输入您的消息,再次点击继续。现在您的提醒已保存。

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

  • 现在再次点击三个点,改变位置到模拟器的其他地方,并保存它。
  • 点击应用程序的第一个按钮,你将到达你在模拟器上设置的位置。
  • 最后,在模拟器中,设置保存提醒时的位置。等几秒钟,你会收到通知。

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

如果你的项目有任何困难,那么你可以从我的 Github 项目中寻求帮助。

我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。

订阅 我的邮件列表,以便在您的收件箱中直接获得我的文章,并且不要忘记关注我自己在 Medium 上发表的文章The Code Monster来完善您的技术知识。

结论

我们已经了解了如何在自己的项目中使用谷歌地图和 Geofence APIs,还了解了服务和广播接收器。除此之外,我们还学习了一个技巧,使用 Gson 在 SharedPreference 中存储自定义 java 对象,并使用相同的方法检索它们。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值