TowardsDataScience 博客中文翻译 2019(四百三十六)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

搜集和探索整个英语有声目录

原文:https://towardsdatascience.com/scraping-and-exploring-the-entire-english-audible-catalog-ea279cd09f88?source=collection_archive---------20-----------------------

上周,我使用 Python 的 HTML-Requests 包编写了一个脚本,从 Audible 获取有关他们当前的“买一送一”信用销售中的程序的信息。在对线程超参数进行试验后,我设法让我的脚本的性能达到每秒 55 个条目,这意味着 Audible 的大约 40 万个程序的整个英文目录可以在大约两个小时内完成。几个小时后,我有了一个完整的数据集。

(注:如果你喜欢代码,可以直接跳转到刮痧笔记本分析笔记本。你也可以下载整个数据集。)

在加载和处理数据以消除重复并转换对象类型后,我有 352,686 个条目,包含以下字段:标题、作者、类别(流派)、长度、讲述者、价格、评级(在源处四舍五入到 0.5 的增量)、评级计数、发布日期和 URL。

#我们的数据中音频的总长度是多少?

230 年,235 天,15 小时

#哪几个类别的时长和收视率最高?

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

有趣的是,总时长排名前三的类别与收视率排名前三的类别正好相反。换句话说,“科幻&奇幻”节目每小时的参与度(即评论数量)比“小说”节目高得多。看看能不能进一步量化。由于每小时的评论数量可能比每个标题的评论数量更不公平(我们假设一些流派的标题平均比其他流派的长),让我们采用后者。让我们看看这一替代指标是否会延续敬业度趋势。

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

Categories sorted from left to right by mean number of reviews.

这里有几个有趣的发现:首先,按每个标题的平均评论数计算,“科幻与幻想”仍然是最高的参与度。然而,现在当考虑评论的中位数而不是平均数时,它输给了“浪漫”(22 对 23)。此外,“浪漫”评论的分布比任何其他类别都要紧密。综合这些发现,表明“浪漫”比其他类别有更多的双峰分布。我们应该预料到更多的书很少或者没有评论,这就降低了平均值。然而,受欢迎的书籍都有很多评论,提高了中位数。换句话说,“浪漫”的听众在节目选择上没有其他类型的听众大胆。

各书的评分是如何分布的?

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

注意:第一个情节不是错误。没有非线性轴的绘图意味着评论很少的标题数量主导了绘图。事实上,在我们大约 35 万个程序中,超过 20 万个程序的总评论数不到 10 条。第二个图是一个直的 x 轴和对数标度的 y 轴,展示了超过 100,000 条评论的极少数图书——准确地说是 7 个。第三个图向我们展示了评级计数的对数与评级计数频率的对数大致呈线性变化。换句话说,高层是孤独的。

#那些少有的> 100k 收视率的书是什么?

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

Ready Player One dominates the field in number of ratings.

需要注意的是:所有这些产品都比整个 Audible 目录 15.42 美元的平均价格要贵得多。所代表的类别比我们想象的更加多样化:两部科幻小说,一部奇幻小说,一部惊悚小说,两部自传和一部自助小说。除了一本,所有的书都是在过去十年发行的。一号玩家有大量的评论。事实上,这个标题的评论比其他 174,000 个节目加起来还多。

#哪些作者的评分最高?

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

斯蒂芬·金以超过 600,000 条评论赢得了他的书籍评论总数的冠军。令人印象深刻的是他收视率最高的头衔只有 51695。他在排行榜的首位证明了他的多产。他在 Audible 上有 130 个单独的录音,没有被我们的重复搜索筛选掉。

在第二个图中,我们考虑了 Audible 上有两个以上标题的作者的每个节目的平均评论数(以便筛选“一炮走红的奇迹”及其样本/替代版本)。在《火星人》的鼓舞下,安迪·威尔摘得桂冠,后者是第二大冠军。

#哪些作者的书名数量和记录小时数最多?

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

继续调查我们数据集中最多产的作者,我们查看音频的总持续时间和录音数量(不包括期刊)。查尔斯·狄更斯(Charles Dickens)在 audible 上以近 5000 小时的总时长独占鳌头。他和排名第二的阿瑟·柯南·道尔似乎都从 Audible 上对他们作品的多次翻译中受益匪浅。单单《双城记》就有 37 个不同的版本!詹姆斯·帕特森没有这种优势,他在当代作家中胜出。

多伊尔和狄更斯切蛋糕的方式略有不同,他们交换了 Audible 的唱片数量。亚瑟·柯南·道尔的作品在 Audible 上总共有 706 个版本。

#哪些解说员收视率最高?

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

斯科特·布里克叙述的标题(658 个!)获得了最高的收视率,几乎是原来的两倍。同上,我们看右边有最大“影响”的叙述者(至少按每部作品的评分数)。罗伊·多特里斯(拥有 10 本书)因其讲述《冰与火之歌》的作品而位居榜首,该书是我们七本收视率突破 10 万的书中的第一本。

#哪些解说员在 Audible 上的时间最长?

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

我们看到了同一枚硬币的两面——Scott Brick 在小时数方面的产量最高,有 658 本书,平均每本书 12 小时 39 分钟。凯茜·多布森以 1161 个冠军的数量获胜,平均每个冠军 2 小时 53 分钟。

#标题发布日期与评论数量有什么关系

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

We can see a spike for each of the 7 outliers above.

虽然我们可能会认为老书会因为有更多时间积累评分而受益,但我们看到了相反的趋势。最近的节目更有可能获得更多的评论。

从 1996 年开始,每个季度发行了多少本书?

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

Histogram of release dates with one bin per quarter.

请注意,在 1999 年的最后一个季度,有一个大箱子。事实上,在我们的数据集中最常见的日期是 1999 年 12 月 16 日。虽然你可能认为这个日期是追溯到所有早期的标题,但事实并非如此。数据集中的最小日期是 1995 年 12 月 1 日,也就是 Audible 成立的那一年。此外,谷歌没有从 1999 年 12 月有关 Audible 的新闻中得到多少回报,尽管该公司经历了多事之秋。最初的创始人在 10 月份刚刚去世。

另一个有趣的发现是,2015 年期间,电影发行量连续三个季度下降。事实上,这种下降使得 2015 年成为自 2002 年以来唯一一个发行数量少于前一年的年份。

旁注:数据集中最新的日期是莎伦·波顿的《投毒者》2020 年 12 月 10 日。

#所有节目的价格分布是怎样的?

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

所有书籍的平均价格是 15.43 美元,顺便提一下,这个价格接近某些每月有声计划的 15 美元的价格。在每 5 美元的区间都有峰值,正如我们所预期的给出 4.99 和 9.99 美元的定价方案。

#不同类别的价格有何不同?

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

Categories ranked, left to right, by mean price

《神秘与恐怖小说》的平均价格最高,为 20.53 美元。报纸和杂志的平均价格最低,为 2.81 美元。这里没有什么大的惊喜。
我们可以从我们的箱线图中看到我们可能最昂贵的标题——“语言教学”类别中的异常值。让我们找出它是什么。

# Audible 上最贵的节目是什么?

173.27 美元可以买到所有音频中最贵的英文标题,以及 982 分钟(约 16 小时)的西班牙语教学。

#每小时最贵的节目是什么?

每单位长度最贵的节目是杰夫·哈撒韦的 1 分钟长的节目杰作,价格为 10.49 美元——或每小时 629.40 美元。

#节目时长分布(不含期刊)?

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

报纸和杂志、广播和电视、现场活动和戏剧和诗歌不包括在这个图表中,因为它们主要是非常短的节目。我们可以看到还是有不少短的(<5 hour) programs, but there’s a nice bell curve of longer titles with median right around 7 hours.

We might infer that this length plot is a composite result of combining a long-form distribution with a shorter-form distribution. Let’s look at categories and see if we can pinpoint which are which.

#按类别划分长度分布是怎样的?

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

Categories ordered, left to right, by decreasing mean length

事实上,我们发现不同类别的长度分布不同。“报纸和杂志”的平均节目长度最短,为 28 分钟。《科幻与幻想》的平均时长最长,刚刚超过 9 小时。

我们马上会在箱线图中看到一些长度异常值,我们稍后会探讨这些异常值,但首先,让我们验证一下关于复合分布的理论。

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

#最后,Audible 上最长的书有哪些?

df.nlargest(5, 'length')

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

Audible 上 5 个最长的节目中有 4 个属于“宗教与精神”类别。最长 154 小时。唯一不属于这一类别的书名是爱德华·吉本 19 世纪的长篇历史《罗马帝国的衰亡》(The Decline and Fall of The Roman Empire),多年来,这本书都让我在夜里不时地眼皮上下起伏。

所以我们有它。我们已经依次查看了我们的大部分功能:作者、叙述者、收视率、发布日期、长度、价格和类别。我们查看了业内最多产的名字以及最长、最贵的图书。我们还没有探索的唯一功能是“评级”栏本身,这是因为当我们抓取时,我们只能从我们可以访问的 html 中捕捉 0.5 的等级。有了这些链接,我们就可以自由地从页面中为单个节目重新抓取更精细的评级,但我们会把它留到以后再做。

150 万条有声评论的情感分析

原文:https://towardsdatascience.com/scraping-and-sentiment-analysis-of-1-5-million-audible-reviews-3502fa6bc86c?source=collection_archive---------41-----------------------

在这个项目的第一部分中,我们收集并探索了一个数据集,其中包括 Audible 上每个英语节目的 ASIN 、收视率和一个单独的 URL。

我们将通过使用这些字段收集尽可能多的文本评论和附带评级来继续我们离开的地方。然后,我们将训练一些 LSTM 神经网络,将评论分为正面或负面。

(注:此处可跳转至刮痧笔记本,此处可跳转至数据预处理及情绪分析笔记本。你可以在这里下载所有评论的数据集。)

1.抓取 150 万条评论

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

我们之前检查了 Audible 目录中的收视率分布,注意到收视率极度集中于顶部的少数几个节目(标题排名的对数和评论数量的对数之间大致呈线性关系)。

今天,我们将利用这种偏态优势,最大限度地提高每小时的评论数量。假设每个标题的书面评论数量与每个标题的评级数量成比例,我们可以预计大约 50%的网站评论将集中在我们数据集中前 1%的节目中

从 HTML 中抓取这些评论有一个障碍。大多数最受欢迎图书的评论都隐藏在“查看更多”按钮下。

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

单击该按钮时检查网络请求为我们提供了一个解决方案。我们找到一个链接,指向一个只有评论文本和评级的备用页面。通过增加这个 URL 中的页码,直到 HTML 变成空白,我们可以收集给定节目的所有评论和评级。因为这个页面加载速度非常快(并且因为我们使用了 16 个线程),我们能够在合理的时间内从排名前 6% (26,101)的节目中抓取排名前 80%的评论。

刮的时候,我们会趁机做一些家务。我们将使用我们的文本来训练一个机器学习模型,以将评论分为好评(4 或 5 星)或不好评(1、2 或 3 星)。如果我们把标题和作者留在我们的评论文本中,我们的模型很可能会“记住”,比如说,一个给定的程序有非常高的评论分数。这对于归纳来说不是一个好兆头,所以我们想从评论中删除作者和标题。我们现在就做,这样我们就不需要为每次审核存储这些信息了。

用一个普通的词来替换单词的过程叫做“解块”。除了取消每个评论的作者和标题,我们还会用单词“stop”替换句号,去掉所有其他标点符号,并将所有文本改为小写。这减少了数据集中的“单词”数量。

几个小时后,我们有超过 150 万的评论和评级。

2.准备数据

虽然我们实际上为每个评论刮出了三个评级,“总体”、“故事”和“表现”,但我们将专注于“总体”列作为我们的标签。我们将为将来的分析保存另外两列。

为了使我们的数据和目标形成我们需要训练的模型,我们需要完成以下预处理任务:

  1. 根据频率从我们数据中的顶部 vocab_size 单词创建一个词汇表,其中 vocab_size 是一个超参数。
  2. ‘unk’—即替换为“”—我们的数据集中不在我们的词汇表中的所有单词。
  3. 将所有条目填充或截断到某个公共长度, seq_length ,也是一个超参数。
  4. 对数据集中的所有单词进行标记化(即转换为整数)。
  5. 从“总体”列创建二进制标签。

我们的目标是将数据 X 作为形状为 (num_reviews,seq_length) 的整数数组,将标签 y 作为长度为 num_reviews 的整数向量。

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

The first few entries. Text normalized and authors and titles removed.

为了决定上限和下限,让我们来看看分布。

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

Distributions of text character and word length over all reviews.

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

上面的长度与频率图显示了类似指数衰减的现象。96.4%的评论短于 250 字。我们将选择它作为我们的 seq_length

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

下限呢?左边的图表向我们展示了我们的数据集中剩下的绝大多数(> 98%)评论(在之前删除了带有空白文本的条目之后)都超过了 10 个单词。我们将用它作为下限。

按字符长度排序,我们看到最短的评论超过 10 个“单词”。

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

Shortest reviews with >10 ‘words’. Note that ‘words’ are everything between spaces.

我们的下一个任务是从我们所有的评论中构建一个词汇表。我们将使用一个计数器字典来统计每个单词在我们的数据集中的频率,然后我们将选择最频繁的 10,000 个单词来构建我们的词汇表,我们将使用这些单词来“取消”我们的数据集,即使用单词“unk”替换我们词汇表中没有的每个单词。我们还想将数据集转换成一个数字数组,即“令牌”,我们可以在同一个函数中完成这两项任务。

在一些示例文本上测试我们的功能:

sample = ‘i really loved dfalkjf especially the introduction’
...
print(tokenize_text(sample))Unk ID: 24
[4, 56, 79, 24, 301, 1, 1190]

请注意,无法识别的“dfalkjf”被赋予了“unk”标记 24。其余的标记对应于单词在我们的词汇表中的排名。

在填充、截断和标记之后,我们的数据看起来是这样的:

array([[  24,    0,    0, ...,   24,   24,   24],
       [  24,    9,   11, ...,   24,   24,   24],
       [ 149,  149,  149, ...,   24,   24,   24],
       ...,
       [ 131,   32,  873, ...,   24,   24,   24],
       [   5, 3312,  368, ...,   24,   24,   24],
       [ 172,  195,    1, ...,   24,   24,   24]])

注意每行末尾的 24 列,这是用“unk”标记填充的。

最后,我们根据“总体”评级创建二元标签,发现大约 79%的评论获得 4 星或 5 星评级。这个数字值得注意,因为它意味着即使是最简单的模型(总是预测 1)也能获得 79%的准确率。那是要打破的数字。

3.训练模型

既然准备步骤已经完成,我们就可以训练我们的模型了。使用 TensorFlow 和 Keras 层,我们可以使用不同数量的参数尝试许多不同的架构。我们所有的模型都将有一个嵌入作为第一层,它将每个单词转化为一定长度的向量,一个超参数。

我们所有的模型也将至少有一个 RNN 层(特别是长短期记忆或 LSTM 层)。该层将在向前和向后传递中使用。在每种情况下,LSTM 层将输入具有 relu 激活函数的密集层和具有 sigmoid 激活函数的输出层,这将产生 0 和 1 之间的值,该值将被阈值化以提供类别预测。

我们将添加的其他层将是丢弃层,以减少过度拟合,包括嵌入层后的一种特殊类型的丢弃层,它会丢弃整个一维特征图而不是单个单词,以及一个一维卷积层,它将学习一组过滤器,这些过滤器将从相邻单词之间的关系中提取特征。我们还将尝试堆叠两层 LSTMs。

我们将利用二进制交叉熵损失、Adam 优化器,并且我们将采用早期停止回调,这将在验证损失开始增加时停止训练。

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

The training set and validation set accuracy for one of our best models. The validation loss bottomed out on the 5th epoch and training was stopped early.

3.结果

表现最好的模型确实是最复杂的——在大约 155,000 条评论的未知测试集上有 93.8%的准确率。然而,值得注意的是,最不复杂的模型达到了 93.1%的准确率。

我们最简单的模型只包含三个隐藏层:一个嵌入长度只有 8 的嵌入层,一个只有 8 个单元的 LSTM,一个只有 16 个单元的全连接层。它共有 81k 个参数,训练时间为 53 分钟。

model = tf.keras.Sequential([ **tf.keras.layers.Embedding(vocab_size, 8),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(8)),
    tf.keras.layers.Dense(16, activation='relu'),** tf.keras.layers.Dense(1, activation='sigmoid')
])

我们最复杂的模型,也是最终的赢家,除了 LSTM 层之外,还有 7 个隐藏层,包括下降层、卷积层和池层:

model = tf.keras.Sequential([ **tf.keras.layers.Embedding(vocab_size, 128),
    tf.keras.layers.SpatialDropout1D(rate=0.4),
    tf.keras.layers.Conv1D(filters=32, kernel_size=3,                                                           padding='same', activation='relu'),
    tf.keras.layers.MaxPooling1D(pool_size=2),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dropout(rate=0.4),** tf.keras.layers.Dense(1, activation='sigmoid')
])

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

对于二元分类,受试者工作特征(ROC)曲线给出了模型区分能力的良好概念。它反映了这样一个事实,即随着您降低最终概率输出的阈值,您会捕获更多的真阳性,但也会捕获更多的假阳性。一个完美的模型会给正面例子分配比负面例子更高的概率,因此降低阈值会捕获更多的正面例子,而不会捕获更多的负面例子。因此,曲线将切割靠近左上角。AUC 相当于测量 ROC 曲线下的面积(越接近 1 越好)。这里,我们的模型对测试数据的 AUC 为 0.975。

让我们通过例子来测试我们的模型,并输入几段文本。我们给它四个序列。小于 0.5,模型预测为负;大于 0.5,为正。

“这本可怕的书太可怕了。真的,太可怕了。”

我们的模型对此的正确评分为 0.01,即尽可能接近 0。

“我喜欢这本书。这太棒了,太搞笑了,太神奇了。”

这个得了 0.99 分。不如来点暧昧的:

“这本书还可以,但它让我感到难过。”

0.52.我们的模型被撕破了。把积极的和消极的词语结合起来怎么样:

“故事确实很棒,但叙述者很恐怖。”

0.72.该模型更重视“非常棒”部分,而不是“讲述者很可怕”部分。也许如果我们在“性能”评级上重新训练我们的模型,我们会得到不同的结果。

最后,让我们看看我们的模型已经学习的单词嵌入。我们词汇表中每个单词的学习向量应该反映关于该单词的一些有用信息,用于预测正面或负面评论。为了可视化这些表示之间的空间关系,我们需要将单词向量减少到更易于人类理解的维数。

主成分分析(PCA)是一种转换数据的方法,使其信息最丰富的维度(即包含最大方差的维度)与轴对齐(即结果的第一维)。我们将使用 PCA 将我们的 128 维嵌入向量减少到 2 维,以便我们可以可视化单词之间的关系:

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

A 2D representation (the first two principal components) of our learned word embeddings for 61 key words.

我们词汇中 61 个常用词的前两个主要成分形成了上面这张惊人的图表。在这里,一般不喜欢的词,如“迷人的”和“优秀的”,被渲染成蓝色;喜欢的词,如“可怕的”和“无聊的”,用红色呈现,中性的词,如“表演”和“书”用黑色呈现。

我们的嵌入清楚地反映了这些术语的极性。甚至更好的关系似乎被表现出来。例如,我们凭直觉知道“单调”和“单调”、“更差”和“最差”、“乏味”和“无聊”、“声音”和“讲述者”、“音频”和“质量”等密切关系。

通过在整个词汇上重复这个过程,并且只看第一个主成分,我们可以识别出具有最积极和最消极显著性的单词。价值最高的词是‘well spent’[原文如此]。

价值最低的那个词?

“退款”。

最后,让我们对整个训练集进行预测,找出“最差”和“最好”的评论。

“最差”评论:

浪费,浪费,浪费。保存您的信用。

怎样才能让<标题>更好?

没有什么能让这本书变得更好。被警告。这本书甚至没有一些人说的那么有趣。为什么要买一本很搞笑的僵尸恐怖书?

你认为你的下一首歌会是什么?

不确定,除了说一些关于僵尸或天启的事情,但肯定不是这本书的作者或叙述者。

你不喜欢<解说员>的什么表现?

她的声音。这样她让所有的角色听起来都一样。

你不喜欢这本书……但它有什么可取之处吗?

零…更糟糕的是,因为它是数字媒体而不是实体书,我甚至不能烧它来预热。我读过比这本书更好听/读的服务条款。

还有其他意见吗?

我是任何形式的僵尸和启示录故事的忠实粉丝…这本书虽然不应该被写,更不用说…

“最佳”评论:

牛逼

牛逼牛逼牛逼牛逼牛逼牛逼 牛逼牛逼牛逼牛逼 牛逼牛逼牛逼牛逼牛逼

抓取继续:用 Python 下载图像

原文:https://towardsdatascience.com/scraping-continued-download-images-with-python-6122623f3d82?source=collection_archive---------12-----------------------

大约一周前,我写了一篇关于网络抓取的文章。本文介绍了使用 Python 的 BeautifulSoup 库获取数据的一些基本概念,并进一步利用 Pandas 的能力将获取的数据存储到 DataFrame 对象中。

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

Photo by Max Duzij on Unsplash

浏览那篇文章是这篇文章的先决条件,因为你还能如何获得数据?如果你已经熟悉网页抓取的概念,你就不必通读了,只需在底部抓取代码即可。总之,这里有一个链接:

[## 没有数据集?没问题。自己刮一个。

使用 Python 和 BeautifulSoup 的强大功能来收集对您重要的数据。

towardsdatascience.com](/no-dataset-no-problem-scrape-one-yourself-57806dea3cac)

如果你是阅读这篇文章的人之一,你可能会想知道整个故事就是这样( 剧透警告: 它不是)。
快速浏览一下获取的数据,你会看到雄伟的 缩略图 栏,如果你把它和这篇文章的标题联系起来,就不难得出这个故事还没有结束的结论。

今天的文章将处理从缩略图 URL 下载图像的过程。

但是我为什么要下载图片呢?

我很高兴你问了。也许你的数据科学项目需要通过卷积神经网络进行图像分类。让我们想象下面的场景——你想开发一个算法,它能够根据一些缩略图的特征来预测图书的主题(流派)(嗯,实际上是一个很好的项目想法!)。

我绝不是 CNN 的专家,但我将向您展示如何根据流派将图像下载到各自的子目录中。事不宜迟,我们开始吧!

一点点准备工作

你真的认为没有这个你就能逃脱吗?

虽然,我会尽量把这部分说得简短一些。归结起来就是做一些库导入和导入以前抓取的数据。代码如下:

Imports — https://gist.github.com/dradecic/04cbe16e067ea66bf750eb8fdd1c4845

简单回顾一下——下面是 df 的样子:

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

Head of df

所有的准备工作现在都完成了——没那么糟糕,对吧?

下载图像

你期待已久的部分来了。但是在给你代码之前,这里是应该发生的事情的概要:

  1. 一个变量 BASE_DIR 被声明,为图像存储根目录的名称
  2. 声明了一个变量 SUB_DIRS ,它是一个数组,保存了数据集中所有不同的主题
  3. 将创建根目录以及所有子目录(如果它们不存在的话)
  4. 基于不同的主题值过滤数据集
  5. 使用 urllib 下载缩略图
  6. 执行一些替换以使文件名格式正确

呀呀。听起来工作量很大。但它实际上不是——拜托,我们谈论的是 Python!

下面是代码片段。我强烈建议您阅读每一行,甚至可以一行一行地执行,以便更好地理解为什么所有东西都是这样工作的。

Image Downloader — https://gist.github.com/dradecic/d26d44d7e145577a1ded06f28559499b

运行此代码单元将创建以下目录结构:

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

Directory Structure

此外,在每个子目录中,您会看到一堆图像:

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

Music Subdirectory

结论

这是一篇相当短的文章——但我更喜欢那些较长的文章。使您不想立即关闭浏览器窗口。

像往常一样,在您选择的数据集上使用这项新获得的技能。看看有没有可能不下载图片而下载别的东西(有可能)。如果你对 CNN 和深度学习感兴趣,但在网上找不到相关的数据集,那就自己做一个。没有限制。

感谢阅读…

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

[## 通过我的推荐链接加入 Medium-Dario rade ci

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

medium.com](https://medium.com/@radecicdario/membership)

使用 Python 抓取飞行数据

原文:https://towardsdatascience.com/scraping-flight-data-using-python-e71b97e859d3?source=collection_archive---------11-----------------------

计划下一次周末旅行的无聊方式

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

假设我们想计划下一次周末旅行。计划是去米兰或马德里。关键是我们真的不在乎,我们只是在寻找最好的选择。这有点像德国足球运动员安迪·穆勒曾经说过:米兰或者马德里,只要是意大利

第一步,我们只是像往常一样寻找航班。对于这个例子我们使用 皮艇 。一旦我们输入了搜索标准,并设置了一些额外的过滤器,如 Nonstop ,我们可以看到,有趣的是,我们浏览器中的 URL 也相应地进行了调整

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

我们实际上可以将这个 URL 分解成不同的部分:起点、终点、开始日期、结束日期和一个后缀,告诉 Kayak 只寻找直接连接,并按价格对结果进行排序。

现在,总的想法是从网站的底层 html 代码中获取我们想要的信息(例如,价格、出发和到达时间)。要做到这一点,我们主要依靠两个包。第一个是,基本控制你的浏览器,自动打开网站。第二个是美汤,它帮助我们将杂乱的 HTML 代码重塑为更有结构性和可读性的格式。从这道汤里,我们可以很容易地得到我们想要的美味。

所以让我们开始吧。首先我们需要设置元素。为此,我们需要下载一个浏览器驱动程序,例如 ChromeDriver (确保它对应于您安装的 Chrome 版本),我们必须将它放在与我们的 Python 代码相同的文件夹中。现在我们加载几个包,告诉 selenium 我们要使用 ChromeDriver ,让它从上面打开我们的 URL。

一旦网站加载完毕,我们需要找出如何获取与我们相关的信息。以出发时间为例,使用浏览器的 inspect 特性,我们可以看到 8:55pm 出发时间包含在一个名为出发时间基准时间的 span 中。

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

如果我们现在将网站的 html 代码传递给 BeautifulSoup ,我们可以专门搜索我们感兴趣的课程。然后可以用一个简单的循环提取结果。因为对于每个搜索结果,我们得到一组两个出发时间,我们还需要将结果重新整形为逻辑出发-到达时间对。

我们用类似的方法计算价格。但是,在检查价格元素时,我们可以看到 Kayak 喜欢为其价格信息使用不同的类。因此,我们必须使用正则表达式来捕捉所有情况。此外,价格本身被进一步包装,这就是为什么我们需要使用一些额外的步骤来达到它。

现在,我们将所有内容放入一个漂亮的数据框架中,得到

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

差不多就是这样了。我们已经将最初飞行中的 html 代码中混乱的所有信息整理成形。繁重的工作完成了。

为了方便起见,我们现在可以将上面的代码封装到一个函数中,并在三天的旅程中使用不同的目的地和起始日组合来调用该函数。当发送几个请求时, Kayak 可能会不时地认为我们是一个机器人(谁能责怪他们呢),解决这个问题的最好方法是不断地改变浏览器的用户代理,并且在请求之间等待一段时间。我们的整个代码将会是这样的:

一旦我们指定了所有的组合并收集了各自的数据,我们就可以使用来自 seaborn 的热图很好地可视化我们的结果

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

就这样决定了。下一站:马德里!仅 108 美元,这是我们在 9 月份挑选的三个周末中最便宜的选择。期待着吃一些美味的小吃。

用 Python 和 BeautifulSoup 刮汉萨德

原文:https://towardsdatascience.com/scraping-hansard-with-python-and-beautifulsoup-f2887f0bc937?source=collection_archive---------31-----------------------

使用网络搜集脚本自动收集议会官方报告中的数据

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

Photo by Deniz Fuchidzhiev on Unsplash

请注意:我是以个人身份写这篇文章的。表达的任何观点都不是我雇主的观点。

我最近有一个问题:众议院议长约翰·伯科主持了多少次首相问答时间(PMQs)?

通常我会通过搜索议会资料来回答这样的问题,这种搜索服务包含议员的贡献、议会会议记录、议会文件以及下议院和上议院图书馆的简报。

这项服务可在https://search-material . parliament . uk获得

对于这个特定的问题,我无法从搜索议会材料中得到这个问题的直接答案,原因可以填充几个中等职位。相反,我决定通过网上搜集英国议会议事录来寻找答案。

我从一个粗略的计划开始,我认为我可以找到 John Bercow 主持了多少次 PMQs 会议:

  1. https://hansard . parliament . uk上查找约翰·伯科任职期间发生的所有 pmq 记录
  2. 对于这些 PMQs 记录中的每一条,浏览页面并找到一位演讲者所做的贡献。
  3. 对于演讲者所做的每项贡献,提取该人的具体姓名、他们做出贡献的日期以及当天 PMQ 记录的 URL。
  4. 计算返回的唯一 PMQs 记录的数量,并从该数量中减去主持会议的发言人不是 John Bercow(即副发言人)的记录。

在我进入代码之前,我应该指出,在开始这个项目之前,我知道一些事情,它们在我的道路上帮助了我:

  • 我知道英国议会议事录中所有的 PMQs 记录都有一个标准的名字:他们都被称为“首相”。
  • 我知道演讲者所做的贡献总是被列为演讲者,而不是成员的名字。
  • 我知道当‘partial = True’放在 URL 的末尾时,Hansard HTML 更容易阅读。

查询议会信息通常需要知道数据是如何格式化的,以及信息是如何存储的。我发现 Twitter 是一个询问有关议会数据问题的好地方,那里有很多知识渊博的人很乐意提供帮助。

对,对代码。

需要的包

这些是我用过的包

import csv
from bs4 import BeautifulSoup
import pandas as pd
import requests
  • csv 允许您操作和创建 csv 文件。
  • BeautifulSoup 是网络抓取库。
  • 熊猫将被用来创建一个数据框架,把我们的结果放到一个表中。
  • Requests 用于发送 HTTP 请求;查找网页并返回其内容。

找到所有的 PMQ 记录

首先,我需要为英国首相收集所有的英国议会议事录辩论记录。

hansardurls = []
for i in range(1,20):
    url = '[https://hansard.parliament.uk/search/Debates?endDate=2019-10-28&house=Commons&searchTerm=%22Prime+Minister%22&startDate=2009-06-23&page={}&partial=true'.format(i)](https://hansard.parliament.uk/search/Debates?endDate=2019-10-28&house=Commons&searchTerm=%22Prime+Minister%22&startDate=2009-06-23&page={}&partial=true'.format(i))
    rall = requests.get(url)
    r = rall.content
    soup = BeautifulSoup(r,"lxml")
    titles = soup.find_all('a',class_="no-underline")
    for t in titles:
        if t['title'].lower() == "prime minister [house of commons]":
            hurl = '[https://hansard.parliament.uk'+t['href'](https://hansard.parliament.uk/'+t['href')]
            hansardurls.append(hurl)
print(len(hansardurls))

我打开 https://hansard . parliament . uk,点击查找辩论,然后搜索约翰·伯科担任议长期间的“首相”。这给了我一组长达 19 页的搜索结果。

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

Results set from Hansard

代码首先创建一个名为‘hansardurls’的空列表,我将 PMQ 记录的 URL 放在那里。

然后代码在 19 页的搜索结果中循环。每个循环执行以下操作:

  • 请求 URL 并返回所请求页面的内容
  • 使用 BeautifulSoup 处理 HTML
  • 在 HTML 中搜索以找到该页面上每个辩论的链接,并给它们分配变量“title”。我进入结果页面,右键单击一个辩论标题,然后选择“Inspect ”,找到了这些链接。这会将您带到控制台,向您显示该页面元素的 HTML。
  • 查找仅包含“首相”的头衔。有一些辩论题目提到了首相,但不是 pmq,所以这些被排除了
  • 有些标题是大写的,所以所有的标题都用小写。降低()以捕捉所有相关的辩论标题
  • “if”语句是说,如果辩论的标题是“首相”,则为该辩论创建一个完整的 URL,并将其添加到名为“hansardurls”的列表中。title 元素中的链接前面没有“hansard.parliament.uk ”,这需要首先添加以使它们成为可用的链接。
  • 最后一行打印出找到并添加到列表中的 PMQ URL 的数量: 327

导出 PMQ 网址

我想在一个单独的 CSV PMQ 的网址,原因我将进入稍后。

with open(‘hansardurls.csv’, ‘w’, newline=’’) as myfile:
 wr = csv.writer(myfile, quoting=csv.QUOTE_ALL)
 wr.writerow(hansardurls)

这段代码创建了一个名为“hansardurls.csv”的文件,并将列表“hansardurls”中的每个对象写入该 csv 中的单独一行。

查找每次 PMQ 会议的演讲者贡献

现在我有了约翰·伯科担任议长期间所有 PMQ 会议的链接,我想看看每个 pmq,看看是否有一位议长做出了贡献,如果有,是约翰·伯科还是他的副手。

Speakercontrib = []
Speakingtime = []
urloftime = []

首先,我创建了 3 个单独的列表:一个保存在提问时间发言的人的名字,一个保存成员发言的日期,一个保存他们发言的 pmq 的 URL。

for h in hansardurls:
    rall = requests.get(h)
    r = rall.content
    soup = BeautifulSoup(r,"lxml")
    time = soup.find('div',class_="col-xs-12 debate-date").text
    contributors = soup.find_all('h2',class_="memberLink") 

然后,我编写了一个 for 循环来请求每个 PMQs URL,返回该页面的 HTML,然后使用 BeautifulSoup 来查找页面上对应于发言成员姓名的元素。我通过打开一个 PMQs 页面,右键单击一个成员的名字,找到这个名字是什么类型的元素以及这个元素的类,找到了这个问题。

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

The screen you see after clicking inspect on a page element.

for c in contributors:
        link = c.find('a')
        try:
            member = link.text
        except: 
            print(c)
        if "Speaker" in member:
            Speakercontrib.append(member)
            Speakingtime.append(time)
            urloftime.append(h)

然后,我对 PMQs 页面上每个成员的名字运行了一个 for 循环。该循环执行以下操作:

  • 在成员的名称页面元素中查找链接。包含成员姓名文本和 ID 的实际链接嵌套在 h2 标记中,如上面的屏幕截图所示。
  • 尝试找到包含成员姓名的链接文本。我在这里使用了一个 try 语句,因为在《英国议会议事录》中有一些投稿没有附上姓名。这些通常是当几个成员同时站起来或叫出来。如果没有 try 语句,这个脚本在遇到“Hon. Members rose”投稿时就会停止。该语句打印出成员名称,让我知道这是不是“Member rose”的贡献,或者是否有其他错误发生。

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

An example of the phantom contribution, “Members rose — “

  • 运行一个 if 语句,如果成员的名字包含“Speaker ”,将名字、投稿日期和页面的 URL 添加到各自的列表中。

创建表格

所有数据都是从英国议会议事录中收集来的,但这些数据是在 3 个不同的列表中。我使用 Pandas 创建了一个数据框架,并将 3 个列表添加到数据框架中,以创建一个可用的结果表。

speakersdf = pd.DataFrame(
    {'Date': Speakingtime,
     'Speaker': Speakercontrib,
     'url': urloftime
    })

这段代码创建了一个名为“speakersdf”的数据帧,并将三个列表作为列添加进来。

然后,我将数据帧导出为 CSV 文件:

speakersdf.to_csv('speakerspmqdf.csv')

我浏览了一下电子表格,发现约翰·伯科在 319 个下午问中讲过话,副议长林赛·霍伊尔在 1 个下午问中讲过话。

但是等等…有 327 个 PMQs 网址…少了 7 个 PMQs】。

这就是我在单独的文档中导出 PMQs URLs 的原因。我从 speakersdf 表中取出 URL,并将其与 hansardurls 列表中的 URL 进行比较(我在 Excel 中完成了此操作),找到了 7 个缺失的 PMQs URLs。

当我进入这 7 个网页时,我发现演讲者在这 7 次 PMQ 会议中没有做出贡献,所以他们没有被脚本选中。在这种情况下,我会手动查看议事录,找出在 PMQs 开始前是哪位发言者在主持会议。

最终结果:
约翰·伯科在任内主持了 326 场 PMQ 会议(至 2019 年 10 月 28 日)*

  • 这取决于我从英国议会议事录中收集的数据是否完整和正确,以及我收集这些数据的工作流程是否准确。请不要把它当作真理。

如何改进这段代码?

这段代码并不完美。

我想改进的代码部分:

  • 引入一个函数,该函数在查找演讲者贡献时自动对照收集的 URL 列表检查 hansardurls 列表,以找到丢失的 URL。
  • 我想用演讲者的贡献创建数据框架,而不需要先创建和填充 3 个单独的列表。

如果你认为这段代码可以在其他方面改进,请告诉我。我仍然在学习,非常感谢任何建议或指导。

抓取 Reddit 数据

原文:https://towardsdatascience.com/scraping-reddit-data-1c0af3040768?source=collection_archive---------1-----------------------

如何使用 Python Reddit API 包装器从 Reddit 抓取数据(PRAW)

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

Photo by Fabian Grohs on Unsplash

顾名思义,PRAW 是 Reddit API 的 Python 包装器,它使你能够从子编辑中抓取数据,创建机器人等等。

在这篇文章中,我们将学习如何使用 PRAW 从不同的子编辑中抓取帖子,以及如何从特定的帖子中获取评论。

入门指南

PRAW 可以使用 pip 或 conda 安装:

现在 PRAW 可以通过写:

import praw

在它被用来收集数据之前,我们需要验证自己的身份。为此,我们需要创建一个 Reddit 实例,并为其提供一个client_idclient_secret和一个user_agent

为了获得认证信息,我们需要创建一个 reddit 应用程序,方法是导航到此页面并单击创建应用程序创建另一个应用程序。

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

Figure 1: Reddit Application

这将打开一个表单,您需要在其中填写名称、描述和重定向 uri。对于重定向 uri,你应该选择http://localhost:8080,正如优秀的 PRAW 文档中所描述的。

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

Figure 2: Create new Reddit Application

按下创建应用程序后,一个新的应用程序将会出现。在这里,您可以找到创建praw.Reddit 实例所需的认证信息。

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

Figure 3: Authentication information

获取子编辑数据

现在我们有了一个praw.Reddit实例,我们可以访问所有可用的函数并使用它,例如从机器学习子编辑中获取 10 个“最热门”的帖子。

输出:

[D] What is the best ML paper you read in 2018 and why?
[D] Machine Learning - WAYR (What Are You Reading) - Week 53
[R] A Geometric Theory of Higher-Order Automatic Differentiation
UC Berkeley and Berkeley AI Research published all materials of CS 188: Introduction to Artificial Intelligence, Fall 2018
[Research] Accurate, Data-Efficient, Unconstrained Text Recognition with Convolutional Neural Networks
...

我们还可以通过指定“all”作为子编辑的名称,得到所有子编辑的 10 个“最热门”的帖子。

输出:

I've been lying to my wife about film plots for years.
I don’t care if this gets downvoted into oblivion! I DID IT REDDIT!!
I’ve had enough of your shit, Karen
Stranger Things 3: Coming July 4th, 2019
...

这个变量可以被迭代,包括文章标题、id 和 url 在内的特征可以被提取并保存到一个.csv文件中。

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

Figure 4: Hottest ML posts

使用 subreddit 对象上的.description函数可以获得 subreddit 的一般信息。

输出:

**[Rules For Posts](https://www.reddit.com/r/MachineLearning/about/rules/)**
--------
+[Research](https://www.reddit.com/r/MachineLearning/search?sort=new&restrict_sr=on&q=flair%3AResearch)
--------
+[Discussion](https://www.reddit.com/r/MachineLearning/search?sort=new&restrict_sr=on&q=flair%3ADiscussion)
--------
+[Project](https://www.reddit.com/r/MachineLearning/search?sort=new&restrict_sr=on&q=flair%3AProject)
--------
+[News](https://www.reddit.com/r/MachineLearning/search?sort=new&restrict_sr=on&q=flair%3ANews)
--------
...

从特定帖子中获取评论

您可以通过创建/获取一个Submission对象并遍历comments属性来获取帖子/提交的评论。要获得 post/submission,我们可以遍历 subreddit 的提交,或者使用reddit.submission指定一个特定的提交,并向其传递提交 url 或 id。

要获得顶级注释,我们只需迭代submission.comments即可。

这对于某些提交是有效的,但是对于其他有更多评论的提交,这段代码将抛出一个 AttributeError,表示:

AttributeError: 'MoreComments' object has no attribute 'body'

这些MoreComments 对象代表在网站上遇到的“加载更多评论”和“继续此主题”链接,在评论文档中有更详细的描述。

去掉了MoreComments对象,我们可以在打印正文之前检查每个注释的数据类型。

但是 Praw 已经提供了一个名为replace_more的方法,它取代或移除了MoreComments。该方法采用一个名为 limit 的参数,当该参数设置为 0 时,将删除所有的MoreComments

以上两个代码块都成功地迭代了所有的顶级注释,并打印了它们的正文。输出如下所示。

Source: [https://www.facebook.com/VoyageursWolfProject/](https://www.facebook.com/VoyageursWolfProject/)
I thought this was a shit post made in paint before I read the title
Wow, that’s very cool.  To think how keen their senses must be to recognize and avoid each other and their territories.  Plus, I like to think that there’s one from the white colored clan who just goes way into the other territories because, well, he’s a badass.
That’s really cool. The edges are surprisingly defined.
...

然而,评论部分可以任意深入,大多数时候我们当然也想得到评论的评论。CommentForest提供了.list 方法,可以用来获取评论区内的所有评论。

上面的代码将首先输出所有的顶级注释,然后是二级注释,依此类推,直到没有剩余的注释。

推荐阅读

[## 使用硒和美丽素的网刮

如何使用 Selenium 在页面之间导航,并使用它来废弃加载了 JavaScript 的 HTML。

towardsdatascience.com](/web-scraping-using-selenium-and-beautifulsoup-99195cd70a58)

结论

Praw 是 Reddit API 的 Python 包装器,它使我们能够通过一个干净的 Python 接口使用 Reddit API。该 API 可用于网络抓取,创建一个机器人以及许多其他。

本文介绍了身份验证、从子编辑获取帖子以及获取评论。要了解更多关于 API 的信息,我建议看一看他们的优秀文档

如果你喜欢这篇文章,可以考虑订阅我的 Youtube 频道,在社交媒体上关注我。

本文涵盖的代码可以从 Github 资源库获得。

如果你有任何问题、建议或批评,可以通过 Twitter 或评论区联系我。

一步一步地刮财富 500 强公司的招聘板

原文:https://towardsdatascience.com/scraping-the-fortune-500-company-job-boards-step-by-step-a124cf8bc364?source=collection_archive---------18-----------------------

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

Photo by: Matthew Henry

我想你同意我的观点,Linkedin 在整合工作岗位和激励专业人士方面非常成功。招聘人员更有可能在 Linkedin 上搜索候选人,比所有其他招聘平台加起来还要多。

事实上,这是一个真实的事实:谁拥有求职者市场,谁就拥有数十亿美元的市场。的确,怪物,招募者知道这一点。甚至谷歌也在 2017 年开始分享就业市场的馅饼。

公司不断花钱寻找适合合适工作的候选人。因此,我们仍然有很大的潜力去开拓就业市场。

在这篇文章中,我将带你从零开始构建一个财富 500 强招聘网站的整个过程。此外,我将解析 Linkedin 的商业模式来推动你的业务。

一个招聘网站就像一个媒体代理,为合适的候选人和潜在的公司牵线搭桥。雇主付费在网站上发布职位列表,求职者向感兴趣的公司发送简历和求职信。因此,工作列表的质量和数量对你的网站生存至关重要。有两种方法可以增加招聘网站上的招聘数量:

1。从公司网站的职业栏中搜集工作列表

2。从工作列表搜索引擎上刮下来的,像确然和 Monster.com

第一种方法:

因为每个公司都有自己的网站,我们需要为他们所有人建立一个蜘蛛。一个传统的方法是用美汤写 python。这导致高的初始成本和维护成本。由于每个网站都有独特的布局,我们需要为每个公司写一个单独的脚本。此外,该网站可能会改变其网页结构。因此,我们必须重写脚本,并建立一个新的蜘蛛抓取网站。此外,有这么多的网站,只有由一群技术专家来完成,才能使你的网站持续下去。多增加一个劳动力的高边际成本对企业来说是站不住脚的。

网页抓取工具作为成本低得多的最有效的替代工具派上了用场。它允许我们自动化整个抓取过程,而无需编写脚本。Octoparse 脱颖而出,成为最好的网页抓取工具。它将使首次入门者和有经验的技术专家都能通过点击式可视化界面提取数据。

由于有 500 个网站,我将在本文中以脸书就业委员会为例。(这是财富 500 强企业网站排行榜,欢迎充分利用 )。)

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

可以看到,该网页包含十个列表,分布在多个页面上。我们将点击每个工作列表,提取每个职位位置职责最低和首选要求。对于像这样带有嵌套列表 ( 列表包含额外列表)的网页,我们可以预处理 URL 列表,这样我们就可以获得所有列表页面的 URL,而无需分页。

1。一个 URL 遵循一个一致的模式,在末尾有一个固定的主机名和一个页面标签。当您分页时,编号会相应地改变。因此,我们将第一个页面的 URL 复制到一个电子表格中,向下拖动以获得网站 URL 的列表。

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

2。然后我们用 Octoparse 建立了一个带有这个列表 URL 的爬虫。

现在你可以开始提取了

通过内置的浏览器,我们可以用给定的命令提取网页上的目标元素。在这种情况下,我们单击页面中的一个工作列表,并选择“全选”来创建一个包含所有列表的循环。

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

3。然后选择循环点击每个元素来浏览每个详细页面。

你应该可以得到一个工作列表,像这样提取出来。同样,从详细页面中选择提取元素,包括演示 _ 脸书 _ 职业 _ 列表 职位地点职责最低和首选要求

遵循同样的想法,我们可以用 Octoparse 创建任意多的爬虫。因此,高维护成本的风险被最小化。您可以设置擦除计划,并通过 API 将最新作业列表传送到您的数据库。

第二种方法:

像 Monster.com 的**这样的工作搜索引擎提供了大量的工作列表。我们可以用一个爬虫从大公司和小公司获得这些工作信息。另一方面,如果你从求职引擎上找工作,这不会给你带来竞争优势。最平易近人的解决办法就是找个小众。我们可以缩小到特定的群体,而不是一个范围很广的网站。它可以根据供求关系进行创作。在这种情况下,我收集了 10000 份工作列表和相关的位置,并将它们与地图进行比较,以查看数据科学职位在地理上是如何分布的。**

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

数据科学职位主要集中在沿海地区,西雅图和纽约的需求最高。考虑到这一点,这将是一个绝佳的机会,可以帮助更多的科技公司通过当地的数据科学家社区找到合适的候选人。

我有一个类似的视频,指导如何搜集工作列表。

Linkedin 为什么成功?

从一个昙花一现的人物到一个巨人,Linkedin 在商业策略上非常老练。以下是受它们启发的四个因素,它们将在许多层面上有益于您的企业:

  • 找到合适的传播者:首先要邀请“冠军”和行业领袖来宣传你的网站。这些冠军具有魅力效应,将转化为精英聚集。
  • 社交网络社区:用户一旦聚集在一起,就承载了更多的商业价值。社区产生 UGC(用户生成内容),吸引更多优质用户分享自己的想法。这些是提高竞争力的资产。
  • 可信度:求职网站的目标是帮助他们获得职业发展。说“帮助别人就是帮助自己”有点老生常谈,但如果你在追求一个成功的企业,这是正确的心态。

最后的想法?

我相信边做边学。就像布兰森说的,“你不是靠遵守规则学会走路的。你在实践中学习,在跌倒中学习。”这就是你成功的方法。

引用:

https://www . statista . com/statistics/976194/annual-revenue-of-LinkedIn/

https://towards data science . com/influencer-marketing-using-web-scraping-568 ef4c 072 C3

原载于 2019 年 8 月 16 日【https://www.octoparse.com】*。*

NLP 第 1 部分|使用 BeautifulSoup 和 Python 抓取 Web

原文:https://towardsdatascience.com/scraping-the-web-using-beautifulsoup-and-python-5df8e63d9de3?source=collection_archive---------3-----------------------

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

Photo by Patrick Selin on Unsplash

数据是任何数据科学项目的核心,然而我们常常认为数据的可用性是理所当然的,尤其是当它整齐地出现在 SQL 数据库中或者更好地出现在我们的收件箱中时。

也就是说,由于其特定的性质,有时您正在寻找的数据并不容易获得。这个问题的一个可能的解决方案是网络抓取的想法,或者通过仔细阅读特定网站的 HTML 从该网站提取信息。例如,让我们说你正在计划一个假期,你在寻找机票何时开始销售。是的,你可以每小时浏览同一个旅游网站,希望价格会下降,但更有效的方法是每小时浏览旅游网站,并有一个输出文件为你提供最新的票价。

放弃

许多网站不希望自己的数据被窃取,尤其是当这些数据包含可识别的用户信息时(如脸书、Linkedin 等)。).请考虑您选择刮取哪些数据以及刮取的频率。

NLP 系列

这个简短的教程是关于自然语言处理(NLP)的 3 部分系列的第一部分。在这个系列中,我们将探索抓取网站数据的技术,预处理并准备好数据以供分析,最后从我们的 NLP 数据中收集见解。

NLP 第二部

NLP 第三部

从 Indeed.com 搜集公司评论

在这个例子中,让我们尝试抓取 indeed.com,但具体来说是公司评论。让我们针对员工的评分、评估职称、评估描述以及优点和缺点。

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

HTML 基础

在我们真正开始收集信息之前,我们需要熟悉 HTML 的基本结构,因为我们实际上将使用 HTML 标签来标识我们希望收集的信息。

我们可以通过在您当前的浏览器中打开开发者工具来访问网站的 HTML。比如 Firefox(选项→ Web Developer → Inspector)。所有这些蓝色的“div”标签、箭头、类和 id 就是你当前所在网站的 HTML。

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

在我们研究 indeed.com 的 HTML 之前,让我们用下面的例子回顾一下它的基本结构。

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

HTML 描述了网站的底层结构。换句话说,它标识了网站将有一个标题,多个段落,一个嵌入的视频,一个结束页脚,等等。HTML 不会描述这些组件将如何排列,它们的样式、大小、颜色等。

HTML 代码本质上是分层的,缩进的标签(即。

Python 代码

首先,我们需要导入所需的库。

from bs4 import BeautifulSoup
import lxml
import requests
import pandas as pd
import numpy as np

导入的“request”库有一个 get()函数,它将向 indeed.com 服务器请求 URL 的内容,并将服务器的响应存储在“base_url”变量中。如果我们打印“base_url”变量,我们将实际看到页面的整个 HTML。

base_url = requests.get('[https://www.indeed.com/cmp/Google/reviews?fcountry=ALL&start='](https://www.indeed.com/cmp/Google/reviews?fcountry=ALL&start='), timeout=5)print(base_url.text)

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

让我们首先定义一个名为“parse”的函数,它需要一个参数,这个参数将是我们试图解析/抓取的页面的实际 URL。接下来,我们将使用 BeautifulSoup 类创建器来解析所提供网站的内容(HTML 代码)。我们将使用“lxml”解析器,以防 HTML 格式不完美。更多关于 BeautifulSoup 可用的不同解析器的信息,请访问这个链接

请记住,网站通常会通过更改容器中项目的名称或父/子关系来调整其 HTML 脚本。这些变化需要你调整你的职能。该函数于 6 月 20 日进行了调整,以修复新实现的 indeed.com HTML 脚本

def parse(full_url):
    **page_content = BeautifulSoup(full_url.content, 'lxml')**
    containers = page_content.findAll('div', 
                 {'class':'cmp-Review-container'})
    df = pd.DataFrame(columns = 
         ['rating', 'rating_title',  'rating_description',
                         'rating_pros', 'rating_cons'])

    for item in containers:
        try:
            rating = item.find('div', 
                     {'class': 'cmp-ReviewRating-text'})
                     .text.replace('\n', '')
        except:
            rating = None
        try:
            rating_title = item.find('div', 
                           {'class': 'cmp-Review-title'})
                           .text.replace('\n', '')
        except:
            rating_title = None
        try:
            rating_description = item.find('span', 
                                 {'itemprop': 'reviewBody'})
                                 .text.replace('\r', '. ')
        except:
            rating_description = None
        try:
            rating_pros = item.find('div', 
                          {'class': 'cmp-ReviewProsCons-prosText'})
                          .text.replace('\n', '')
        except:
            rating_pros = None
        try:
            rating_cons = item.find('div', 
                          {'class': 'cmp-ReviewProsCons-consText'})
                          .text.replace('\n', '')
        except:
            rating_cons = None
        df = df.append({'rating': rating, 
             'rating_title': rating_title, 
             'rating_description': rating_description,
             'rating_pros': rating_pros, 
             'rating_cons': rating_cons}, ignore_index=True)
    return df

接下来,我们需要更仔细地检查 HTML 以识别哪个根容器(即哪个父标签)容纳包含我们要抓取的信息的子/嵌套标签。让我们导航到“https://www.indeed.com/cmp/Google/reviews?fcountry=ALL&start =”,因为我们将收集员工对谷歌的评论。打开您的开发工具,观察网站的 HTML。请注意,一旦您访问了网站的 HTML,在网站上移动鼠标光标会导致特定区域高亮显示,并且 HTML 似乎会随着您移动光标而改变。当您移动光标时,您的开发者工具会自动将您带到网页上突出显示部分的 HTML 部分。这非常有帮助,因为我们可以快速识别 HTML 代码中需要更详细检查的部分。

如果您还记得,在本教程中,我们感兴趣的是收集员工的总体评分、考核标题、考核描述、优点和缺点。我们需要识别哪个 HTML 标签是所有这些信息的容器或家。通过将鼠标光标移动到适当的位置,我们看到所有我们想要抓取的信息都整齐地包含在一个根元素中。通过检查下面的 HTML,我们可以看到“

”现在移动到一个不同的评论,您将看到名为“cmp-Review-container”的同一个类属性,它存储该评论的数据。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我们将使用“findall()”方法提取所有具有“cmp-Review-container”类属性的“div”容器。接下来,我们将创建一个名为“df”的空 pandas 数据帧,我们将把抓取的数据追加到该数据帧中。

def parse(full_url):
    page_content = BeautifulSoup(full_url.content, 'lxml')
    **containers = page_content.findAll('div', 
                 {'class':'cmp-Review-container'})
    df = pd.DataFrame(columns = 
         ['rating', 'rating_title',  'rating_description',
                         'rating_pros', 'rating_cons'])**

    for item in containers:        
        try:
            rating = item.find('div', 
                     {'class': 'cmp-ReviewRating-text'})
                     .text.replace('\n', '')
        except:
            rating = None
        try:
            rating_title = item.find('div', 
                           {'class': 'cmp-Review-title'})
                           .text.replace('\n', '')
        except:
            rating_title = None
        try:
            rating_description = item.find('span', 
                                 {'itemprop': 'reviewBody'})
                                 .text.replace('\r', '. ')
        except:
            rating_description = None
        try:
            rating_pros = item.find('div', 
                          {'class': 'cmp-ReviewProsCons-prosText'})
                          .text.replace('\n', '')
        except:
            rating_pros = None
        try:
            rating_cons = item.find('div', 
                          {'class': 'cmp-ReviewProsCons-consText'})
                          .text.replace('\n', '')
        except:
            rating_cons = None
        df = df.append({'rating': rating, 
             'rating_title': rating_title, 
             'rating_description': rating_description,
             'rating_pros': rating_pros, 
             'rating_cons': rating_cons}, ignore_index=True)
    return df

既然我们已经为所有想要抓取的数据确定了容器,那么让我们更深入地研究 HTML,以确定包含实际数据的元素。首先,评论评级再次位于“cmp-Review-container”容器中,但是向下钻几层,我们会发现“< div”标记具有“cmp-ReviewRating-text”类属性,它实际上存储了“5.0”评级。让我们记下存储这些数据的标签和类属性,因为下面的 python 脚本需要这些信息。我们对希望提取的所有剩余数据重复该过程。

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

这是对你的挑战。为什么我们要用(“)替换回车(” \r "))为我们评分 _ 描述?提示:标题为“工作环境棒”和“工作环境好”的评论有什么区别?在下面的评论中发表你的答案吧!😉

一旦我们为数据确定了合适的标签,让我们把注意力转回到 python 代码上。我们可以在 for 循环中使用 try/except 块,通过 find()方法在容器中搜索已识别的标签。我们使用 find()而不是 findall(),因为我们只希望返回第一个匹配,因为我们使用了 for 循环。最后,我们将抓取的数据追加回之前创建的空数据帧中。

def parse(full_url):
    page_content = BeautifulSoup(full_url.content, 'lxml')
    containers = page_content.findAll('div', 
                 {'class':'cmp-Review-container'})
    df = pd.DataFrame(columns = 
         ['rating', 'rating_title',  'rating_description',
                         'rating_pros', 'rating_cons']**)**

    for item in containers:        
        try:
            **rating = item.find('div', 
                     {'class': 'cmp-ReviewRating-text'})
                     .text.replace('\n', '')**
        except:
            rating = None
        try:
            **rating_title = item.find('div', 
                           {'class': 'cmp-Review-title'})
                           .text.replace('\n', '')**
        except:
            rating_title = None
        try:
            **rating_description = item.find('span', 
                                 {'itemprop': 'reviewBody'})
                                 .text.replace('\r', '. ')**
        except:
            rating_description = None
        try:
            **rating_pros = item.find('div', 
                          {'class': 'cmp-ReviewProsCons-prosText'})
                          .text.replace('\n', '')**
        except:
            rating_pros = None
        try:
            **rating_cons = item.find('div', 
                          {'class': 'cmp-ReviewProsCons-consText'})
                          .text.replace('\n', '')**
        **except:
            rating_cons = None
        df = df.append({'rating': rating, 
             'rating_title': rating_title, 
             'rating_description': rating_description,
             'rating_pros': rating_pros, 
             'rating_cons': rating_cons}, ignore_index=True)**
    return df

我们还没有完成,因为如果您要执行“parse()”函数,您将获得一个只有 20 条记录的数据帧,这是因为我们只抓取了一页。

为了抓取所有剩余的评论页面,我们首先创建一个新的空数据框架,在遍历所有页面时收集所有的评论。接下来,我们初始化一个计数器变量 20,因为每页有 20 条评论。接下来,我们创建一个 while-loop,它将迭代直到评论的数量等于或大于 4000。为什么是 4000?所有页面上有将近 4000 条个人评论(在撰写本文时)。接下来,当 while 循环遍历每个页面时,我们在 base_url 的末尾添加增量 20。在 base_url 的末尾增加 20 将为您希望访问的页面创建一个新的 url。例如,“https://www.indeed.com/cmp/Google/reviews?fcountry=ALL&start =40”将把我们带到评论的第二页。接下来,我们再次请求并获取 while 循环正在迭代的页面的整个 HTML。我们在页面上应用 parse()函数,将新抓取的评论添加到我们的数据帧中。最后,我们将计数器增加 20,以便 while 循环在下一页上迭代。

base_url = '[https://www.indeed.com/cmp/Google/reviews?fcountry=ALL&start='](https://www.indeed.com/cmp/Google/reviews?fcountry=ALL&start=')all_reviews_df = pd.DataFrame(columns = ['rating', 'rating_title', 
'rating_description','rating_pros', 'rating_cons'])num_reviews = 20# you can adjust this number on how many reviews you which to scrape
while num_reviews < 3000:  

    full_url = base_url + str(num_reviews)

    get_url = requests.get(full_url, timeout=5)  

    partial_reviews_df = parse(get_url)       all_reviews_df = all_reviews_df.append(
                     partial_reviews_df, ignore_index=True) 

    num_reviews += 20

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

剩下要做的就是将我们的数据帧移动到一个 csv 文件中。

all_reviews_df.to_csv('indeed_scrape.csv')

我希望你喜欢这个教程。我喜欢你的反馈,欢迎在下面评论。

别忘了挑战!

谢谢!

通过 CenPy 的美国人口普查数据

原文:https://towardsdatascience.com/scraping-us-census-data-via-cenpy-9aeab12c877e?source=collection_archive---------17-----------------------

用 API 争论人口普查数据

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

Source: Census.gov (The United States Census logo® is a Federally registered trademark of the U.S. Census Bureau, U.S. Department of Commerce.)

在承担了一个团队项目来检查自然灾害对特定地区的经济影响(通过工资损失)后,我们的假设是使用季节性自动回归综合移动平均(SARIMA) 预测来确定没有自然灾害时的历史就业、失业和工资水平。通过将我们的预测与灾后的实际影响进行比较,我们可以估算出该事件的经济影响。

考虑到时间紧迫,我立即惊慌地想到了这样一种可能性,即必须导航一个笨重的 SQL 界面,或者为每个州手动下载不同的 CSV 来获取我需要的信息。此外,我甚至不太确定我最终需要的指标或时间框架。有了这些不确定性,我认为我最好的选择是利用 API 或 webscraper 来获取我需要的数据。然而,我的 API & BeautifulSoup 技能仍然需要一些练习,所以我开始寻找一种替代方法。

幸运的是,Python 社区中充满了富有创造力的开发人员,他们创建了库和包装器来更容易地与 CenPy 这样的数据进行交互。根据创作者的说法,“CenPy ( sen - pie)是一个包,它公开了美国人口普查局的 API,并使其易于下载和处理熊猫的人口普查数据。”虽然我将探索我使用的几个特性,但我鼓励大家查看他们的 GitHub入门笔记本以获取更多信息。

入门指南

我在这个练习中使用的唯一进口商品是熊猫和熊猫:

import pandas as pd
import cenpy as cen

通过调用explorer.available,您可以访问cenpy知道的所有 API 的标识符列表。幸运的是,我知道我想要访问哪个 API/数据系列,但是如果您想要查看所有可用的 API,您可以通过字典输出或者转换为 Pandas 数据帧来实现,以便于阅读。

# Call list of available datasets, verbose = True to include dataset title
datasets = list(cen.explorer.available(verbose=**True**).items())# Convert dictionary to dataframe
datasets = pd.DataFrame(datasets, columns = ['code', 'dataset name'])

code在这里很重要,因为这将允许您指定您想要建立连接的数据库。connection类允许您构造一个查询字符串,并从人口普查服务器发出请求。然后将结果解析成 JSON 并返回。我从季度工作指标 (QWI)开始我最初的查询,这是一组 32 个经济指标,包括就业、就业创造/破坏、工资、雇佣和其他就业流动的衡量标准。

qwi_connection = cen.base.Connection('QWISA')

构造查询

根据 Census API 文档,在调用它们的 API 时,有几个参数/变量需求必须使用:端点、指示器、地理和时间。如果没有指定,则采用其他分类变量的默认值。以下是完整基本查询的最低要求示例:

api.census.gov/data/timeseries/qwi/sa?get=Emp&for=state:02&year=2012&quarter=1&key=[userkey]

我从设置一些默认参数开始:

# Specify all counties
g_unit = 'county:*'# Start with one state (chosen arbitrarily) 
g_filter = {'state': '01'}# Specify time period
time = 'from 2003-Q1 to 2018-Q1'# Uses .varslike to pull in all indicator names
cols = qwi_connection.varslike('Emp')    # Employment
hir = qwi_connection.varslike('HirA')    # Hiring
earns = qwi_connection.varslike('Earn')  # Earning
payroll = qwi_connection.varslike('Pay') # Payroll
firm = qwi_connection.varslike('Frm')    # Firm Job Stats
sep = qwi_connection.varslike('sep')     # Seperations # Extend cols to add additional variables
cols.extend(hir)
cols.extend(earns)
cols.extend(payroll)
cols.extend(firm)
cols.extend(sep)

循环所有状态

我想构建一个循环,允许我遍历所有状态,并将所有结果连接到一个主数据帧中。我首先创建了一个“主”数据帧,可以在其上附加其他状态,而不是创建一个空白数据帧,以确保列顺序匹配,并且新的查询可以适当地连接。

# Create the first query / dataframe (with state 01)
master = qwi_connection.query(cols = cols, time = time, geo_filter = g_filter, geo_unit = g_unit)

然后,我利用这个州 FIPS 代码字典创建一个州代码列表,并对其进行迭代:

state_codes = {
    'WA': '53', 'DE': '10', 'DC': '11', 'WI': '55', 'WV': '54', 'HI': '15',
    'FL': '12', 'WY': '56', 'NJ': '34', 'NM': '35', 'TX': '48',
    'LA': '22', 'NC': '37', 'ND': '38', 'NE': '31', 'TN': '47', 'NY': '36',
    'PA': '42', 'AK': '02', 'NV': '32', 'NH': '33', 'VA': '51', 'CO': '08',
    'CA': '06', 'AL': '01', 'AR': '05', 'VT': '50', 'IL': '17', 'GA': '13',
    'IN': '18', 'IA': '19', 'MA': '25', 'AZ': '04', 'ID': '16', 'CT': '09',
    'ME': '23', 'MD': '24', 'OK': '40', 'OH': '39', 'UT': '49', 'MO': '29',
    'MN': '27', 'MI': '26', 'RI': '44', 'KS': '20', 'MT': '30', 'MS': '28',
    'SC': '45', 'KY': '21', 'OR': '41', 'SD': '46'
} # Extract numerical state codes
states = list(state_codes.values())

最后,我创建了一个for循环来迭代所有的州代码,并将结果与我现有的master数据帧结合起来:

**for** s **in** states:  

    print(f'Scraping **{s}**')

    **try**:
         # Iterate over states 's' g_filter = {'state': s}    

         df = qwi_connection.query(cols=cols, time=time, geo_filer=g_filter, geo_unit = g_unit)                  

        # Concat new df with master df master = pd.concat([master, df]) 

    **except** requests.exceptions.HTTPError:
         **pass**

请注意,您可以在单个 API 查询中包含多达 50 个变量,并且每天可以对每个 IP 地址进行多达 500 次查询。每天每个 IP 地址超过 500 次查询需要您注册一个普查密钥。该密钥将是您在建立connection时指定的数据请求 URL 字符串的一部分。

我发现我对所有州的所有县的查询超过了总的 IP 限制,即使在使用了我的 API 密钥之后也是如此,并且它使我在系统之外超时。这迫使我在两天内将我的查询分成两部分。

我希望这是对你有帮助的入门,并且你能够探索 CenPy 的所有可能性!

seaborn——让绘图变得有趣

原文:https://towardsdatascience.com/seaborn-lets-make-plotting-fun-4951b89a0c07?source=collection_archive---------5-----------------------

Python 中的 Seaborn 库简介

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

Photo by Isaac Smith on Unsplash

如果您曾经在 Python 中使用过诸如线图、条形图和其他图,您一定会遇到名为 matplotlib 的库。当我开始使用 Python 进行可视化时,我甚至写了一篇关于如何使用 matplotlib 使数据可视化变得有趣的激动人心的文章,您可以在下面阅读:

[## matplotlib——让数据可视化变得有趣

使用 Matplotlib 创建世界各地人口密度的美丽可视化。

towardsdatascience.com](/matplotlib-making-data-visualization-interesting-8bac1eb3d25c)

虽然 matplotlib 库相当复杂,但情节并不那么精炼,不会是任何人出版的首选。这就是 seaborn 参与进来的原因。

Seaborn 是一个基于 matplotlib 的 Python 数据可视化库。它提供了一个高层次的界面来绘制有吸引力的和信息丰富的统计图形。— seaborn

这个库是可视化的下一步。我们可以制作美丽的情节,甚至只需一个命令就可以制作多个情节。让我们开始探索 seaborn 吧。随附的 GitHub 存储库如下:

[## kb22/理解-Seaborn

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/kb22/Understanding-Seaborn)

输入数据

为了理解不同的情节,我从 Kaggle 中选择了一个关于加州房价的数据集。因此,我的第一步是导入允许我读取 CSV 文件的pandas库,然后使用head(5)打印行数、列名和前 5 行。

我们的数据集有 20640 行和 10 列,它们的名称在上面的要点中有描述。我们也来看看前 5 排是什么样子的。

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

dataset.csv

海生的

先从进口matplotlib说起吧。请注意,我使用的是 matplotlib 版本 3.0.3,而不是最新版本,因为有一个错误会破坏热图,使其变得无用。然后,我导入了seaborn。最后,为了确保 Jupyter 中的情节显示在笔记本中,我们使用了命令%matplotlib inline

让我们开始探索剧情吧!

散点图

当我们想要显示两个要素或一个要素与标注之间的关系时,散点图非常有用。这很有用,因为我们还可以描述每个数据点的大小,给它们涂上不同的颜色,并使用不同的标记。让我们看看 seaborn 中的基本命令是做什么的。

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

Scatter plot in seaborn

上图描述了total_roomstotal_bedrooms之间的关系。仅仅一个命令就完成了这一切,但是等等,还有更多。

使用 figsize,我将大小增加到 12x8。然后,我用基于median_house_value的每个数据点的大小、基于ocean_proximity的使用hue的颜色和基于ocean_proximity的使用style的标记更新了散点图命令。此外,没有适当的标题和轴标签的图是不完整的,所以我也添加了它们。

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

Updated scatter plot using seaborn

正如你所看到的,这个情节看起来比上一个好得多,还包括一个很好的图例,所以任何人都可以看到和理解这个情节——这是它应该的方式。

计数图

计数图根据某个分类列自动计算数据点,并将数据显示为条形图。这在分类问题中非常有用,我们想看看不同的类是否有相同的大小。然而,由于这不是一个分类数据,并且只有一个分类列,所以我决定使用它。

seaborn 中的情节允许我们使用annotatetext添加到每个条形中。仔细观察数据集,我们会发现许多元数据信息都丢失了。例如,列ocean_proximity的值<1H OCEAN在任何地方都没有描述过。人们应该始终收集元数据信息,并使用具有适当信息的数据集。因为这只是理解图的参考数据集,所以没什么大不了的。

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

Count plot using seaborn

在上面的图中,我们可以看到该列的数据高度倾斜。让文本和条形在一起真的很有用,因为仅从图上看,最后一个类型ISLAND看起来像是零值。

直方图

直方图是显示连续数据点并查看其分布情况的有效方法。我们可以看到,大部分值都在较低的一侧,或较高的一侧,或均匀分布。

seaborn 的dist图根据数据生成直方图和密度线。我定义了总共 10 个容器,这样整个median_house_value被分配到 10 个不同的桶中。

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

Histogram (with density) in seaborn

正如我们所看到的,分布似乎很正常,在较高的一侧有一个轻微的峰值。上图中的蓝线定义了密度的分布。

小提琴情节

在与 seaborn 合作之前,我总是在各种文章中看到这些看起来很奇怪的情节,并想知道它们是什么。然后,我阅读了它们,发现它们是小提琴图,非常类似于箱线图,根据密度描绘宽度,以反映数据分布。在 seaborn,创建一个小提琴情节只是一个命令。

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

Violin plot in seaborn

在我们继续之前,让我们看看如何理解这些情节。考虑绿色地块INLAND。从零延伸到大约 250000 的黑线是 95%的置信区间。内部的黑色厚块是四分位数范围,意味着大约 50%的数据位于该范围内。图的宽度基于数据的密度。我们可以理解为这个特定数据集的直方图,以黑线为 x 轴,被完全平滑并旋转 90 度。

热图

相关矩阵有助于我们了解所有特性和标签之间的相互关系以及依赖程度。pandas数据帧有一个名为corr()的函数,它会生成一个关联矩阵,当我们将它输入到 seaborn 热图时,我们会得到一个漂亮的热图。将annot设置为 True 可确保相关性也用数字定义。

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

Heatmap in seaborn

虽然整个情节很有用,但我们可以从最后一栏开始,注意每个特征如何与标签median_house_value相关联。median_income与值为0.69的标签最相关。

联合地块

联合图是散点图与密度图(直方图)的组合,用于我们尝试绘制的两种特征。seaborn 的联合图允许我们甚至使用kind作为reg单独绘制一个线性回归。我用height作为8定义了正方形的尺寸,用green定义了颜色。

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

Joint plot in seaborn

绿线表示基于数据点的线性回归。

带群图的箱线图

箱线图将信息分成独立的四分位数和中位数。当与swarm图重叠时,数据点分布在其位置上,因此完全没有重叠。

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

Box plot (and swarm plot) in seaborn

从上面的印迹中,我们可以看到ocean_proximity中五个类别的箱线图是如何描述的。数据点揭示了数据是如何分布的。

配对图

成对绘图会在每对要素和标注之间产生大量绘图。对于要素/标注的每个组合,此图显示一个散点图,对于每个组合本身,它显示一个直方图。该图本身对于获取手头数据的精华非常有用。

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

Pait plot in seaborn

上面的图包含了大量的信息,而且只需要一个命令。

结论

在本文中,我们探索了一些无限的 seaborn 情节,可以更好地理解该库如何工作以及它能够做什么。不断练习和试验,因为选择几乎是无限的。

这里还有一些你可能会喜欢的文章:

[## matplotlib——让数据可视化变得有趣

使用 Matplotlib 创建世界各地人口密度的美丽可视化。

towardsdatascience.com](/matplotlib-making-data-visualization-interesting-8bac1eb3d25c) [## 使用 Python 中的多个绘图库在地图上绘制商业位置

比较地图打印库

towardsdatascience.com](/plotting-business-locations-on-maps-using-multiple-plotting-libraries-in-python-45a00ea770af) [## numpy——Python 变得高效

文章列举了几个提高 Python 效率和速度的 Numpy 函数,并介绍了 4 个 Numpy 函数

towardsdatascience.com](/numpy-python-made-efficient-f82a2d84b6f7) [## Google Colab——您在云上的 Python 工作空间

了解 Google Colab

towardsdatascience.com](/google-colab-your-python-workspace-on-cloud-c3aed424de0d)

欢迎分享你的想法、想法和建议。我很乐意收到你的来信。也可以通过 LinkedIn 联系我。

将深度学习环境与 Terraform、Google cloud、Gitlab 和 Docker 无缝集成

原文:https://towardsdatascience.com/seamlessly-integrated-deep-learning-environment-with-terraform-google-cloud-gitlab-and-docker-faee4b351e94?source=collection_archive---------13-----------------------

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

当你开始一些严肃的深度学习项目时,你通常会遇到需要一个合适的 GPU 的问题。购买适合深度学习工作负载的合理工作站很容易变得非常昂贵。幸运的是,云中有一些选项。我尝试的一个方法是使用美妙的谷歌计算引擎。在 GCE 中,GPU 可以作为实例的外部加速器。目前,有这些 GPU 可用(价格为美国中心 1)。

  • 英伟达特斯拉 P4:每月每 GPU 1267.28 美元
  • NVIDIA Tesla V100:每个 GPU 每月 306.60 美元
  • 英伟达特斯拉 P100:每月每 GPU 746.06 美元
  • NVIDIA Tesla K80:每月每 GPU 229.95 美元

手动配置通常不是您可以轻松扩展的事情,所以我做了一些调查,看是否有方法可以尽可能无缝地扩展我的环境,并以同样的方式破坏它。因此,我找到了一个解决方案,使用 terraform 在谷歌计算平台上建立基础设施。源代码从 Git 部署,Docker 容器自动启动,安装了所有必要的依赖项,如 tensorflow、keras 和 jupyter。在这篇博文中,我将指导你如何轻松设置环境的各个步骤。一些工作基于这个 git 库:https://github.com/Cheukting/GCP-GPU-Jupyter

你会从博文中学到什么?

  • 以自动化的方式使用 GPU 设置 GCE 实例
  • 如何与 GCP 一起使用 terraform
  • 如何将 Gitlab 存储库的代码部署到 GCE 实例中

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

What we will build in this blog post.

如上图所示,我将向您展示如何编写一个 terraform 脚本,该脚本自动启动一个 Google compute engine 虚拟机,在其上安装 CUDA、Docker 等,并最终使用来自另一个外部 Git 存储库(在我们的例子中是来自 gitlab)的代码启动一个 Docker 容器。这个 Docker 容器运行的是 jupyter 笔记本服务器,可以通过浏览器从外部访问。此外,我将向您展示如何使用 Docker 在虚拟机中运行笔记本之外的更长时间的任务。

TL;速度三角形定位法(dead reckoning)

只是想试试,那就去看看 TL;DR 部分,列出了所有必要的命令。

为您的 python 代码创建 Gitlab 库

如果你已经有一个想要给我们的知识库,请随意。否则,现在应该创建一个新的存储库。https://gitlab.com/。你所有的 ml 和 data exploration 代码都将进入这个库。我们将把它与基础设施代码分开。

同样,在这个 Gitlab 存储库中,您现在可以添加所有代码,例如,创建一个简单的 train.py python 文件来训练神经网络,并在最后保存训练好的权重。这里可以找到一个非常简单的例子:https://gitlab.com/dice89/deep-learning-experiments

这个 repo 只包含一个用于数据探索的 Jupyter 笔记本和一个用于训练 RNN·LSTM 文本生成模型的 train.py

创建您自己的 Docker 图像/使用现有的图像

您可以采用现有的 Docker 图像,也可以创建自己的图像。为了简单起见,我们将使用预构建的 Docker 映像,其中安装了 python 3.5 环境,并包含了合理的深度学习用例所需的所有库。它应该包含第一次尝试所需的一切:python 3。x,tensorflow-gpu,numpy,pandas,sklearn,keras

如果你感兴趣,你可以看看这张图片:https://hub.docker.com/r/dice89/ubuntu-gpu-python-dl/

使用 Terraform 配置和启动实例

Terraform 是一个“基础设施即代码”工具包,允许您通过编写代码来定义、创建和销毁基础设施。这带来了很多好处:例如,你不需要像在 GCP 控制台中那样通过 UI 来配置任何东西。此外,默认情况下,您的整个基础设施配置都有文档记录,因为它是可读的代码,在 git 存储库中有理想的版本。我一年前才发现 Terraform,已经无法想象没有它如何建立基础设施。Hashicorp 发现了可编码和可版本化基础设施的完美结合,这可以用编码者的心态来理解。您只需要一些 terraform 文件和 terraform CLI 来创建您的基础架构。

添加新的 Google compute 实例很容易创建,如下所示:

Example of a gcloud compute instance creation with terraform.

在创建实例之前,您需要预先执行一些步骤:

在下面的代码片段中,您将创建一个 gcloud 项目,将执行上下文设置为您的当前帐户,创建一个能够创建新实例的服务帐户,最后下载私钥以在 terraform 中使用该服务帐户。

在您的 CLI 中执行这些命令,并用您想要的项目名称替换。这可能需要一些时间,主要是因为激活了计算 API。

在我们开始实例之前,让我们看一下关于如何配置一个支持 GPU 的 Google compute 实例的细节。不幸的是,你必须为 GPU 申请一个配额(直到 2018 年 12 月没有它,它一直为我工作)。请听从本 Stackoverflow 文章 的建议。处理此请求可能需要 2 个工作日。

Google Compute Instance with a GPU

正如您在第 19 行中看到的,我们向该实例添加了一个 Tesla K80 GPU,启动时我们在一个脚本中执行一些操作( start-up-script.sh )。如下所示:

Setting up Ubuntu VM to run with CUDA

在这个脚本中,我们安装所有需要的库,为用户添加一个 ssh 密钥,并运行 Docker 容器,将端口 80 暴露给外界。因此,我们可以到达 jupyter 笔记本服务器。请注意,创建此实例后,任何知道 IP 的人都可以访问您的笔记本。即使对于像这样的短命环境,这也应该改变。现在创建一个新的 ssh 密钥,以便能够将我们的代码从 Gitlab 部署到实例中。

ssh-keygen -t rsa -b 4096 -C “your_email@example.com”

为了使它工作,您必须将占位符“在此添加您的 SSH 密钥”替换为您生成的私有(!!!)在启动脚本. sh 中输入 ssh 密钥。请注意:不要与任何人分享您的密钥!从这个存储库中克隆完整的配置,然后更改您的 ssh 密钥:https://gitlab.com/dice89/google-cloud-gpu-vm.(不要将您的私钥提交给任何 git 存储库)另外,确保您的 credentials.json 在这个文件夹的根目录中(不要提交 credentials.json)。您还必须将这个 ssh 密钥添加到您的 gitlab 帐户,以便可以部署来自 Gitlab 存储库的代码。

现在我们准备创建机器。这仅用 3 个 bash 命令就可以实现!🚀

填写您的 GCP 项目 id 类型“是”,您的实例将被创建(还要考虑这将导致的成本,免费层不包括 GPU)。创建实例后,您将看到一个发布到命令行的 IP 地址。这是您的公共 IP,您的 jupyter 实例将在该 IP 下可用。可能需要几分钟的时间,直到启动脚本. sh 完成并安装好所有东西。

在脚本完成之前,让我们花点时间来研究一下这个实例。为了做到这一点,你必须 ssh 到它。幸运的是,谷歌为我们提供了一个命令。

gcloud compute --project “<your>_dl” ssh --zone “europe-west1-d” “gpu-vm”

start-up-script.sh 作为根用户运行,因此,您必须切换到您的根控制台来查看发生了什么。

sudo su
cd /var/log
tail -f syslog | grep startup-script

现在我们在实例上,可以检查,例如,是否安装并使用了 GPU。

nvidia-smi -l 1

我们还可以安装 htop,因为它可以方便地监控正在运行的进程的内存消耗:

sudo apt-get install htop

过一会儿,您可以检查是否已经有 Docker 容器在运行:

docker ps

如果你在这个概览上看到你的 Docker 容器,你已经准备好在显示的 IP 下登录你的 jupyter 笔记本。

此外,如果你进入路径*~/data science/deep-learning-experiments*,你会看到它会自动挂载到你的 Docker 容器中的 /root/project 下,并包含你的 gitlab 库的内容,如 train.py

在 jupyter 外执行训练任务

Jupyter 非常适合一些数据探索或实验代码。然而,训练深度学习模型需要很多时间,你不能承受 jupyter 会话崩溃和丢失所有训练进度。幸运的是,有一种补救方法。通过运行 Docker 容器作为守护进程,运行 python 脚本来训练您的模型,您可以非常容易地训练一个新模型。例如,在我们的示例类型中,您需要做的所有事情都是。

docker run --runtime=nvidia -d -v ~/datascience:/root/project dice89/ubuntu-gpu-python-dl python3 /root/project/deep-learning-experiments/train.py

如果您现在检查 docker ps ,您会看到类似这样的内容:

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

要查看训练任务的日志,只需输入:

docker logs <your_container_id_from docker ps>

最后,您将使用 Git 存储库中的代码以可重复的方式训练您的模型。当您完成并保存和储存了您的体重后,您只需输入:

terraform destroy \
  -var 'project_id=<your>-dl' \
  -var 'region=europe-west1-d'

如果您需要相同的环境,只需输入:

terraform apply \
  -var 'project_id=<your>-dl' \
  -var 'region=europe-west1-d'

所以,这就是这个关于如何使用云资源创建深度学习环境的小演练。祝你试用愉快,如果你有任何改善环境的建议,请给我一些反馈。

TL;速度三角形定位法(dead reckoning)

下面是使用预定义的 Docker 容器创建环境的说明。用你喜欢的前缀替换。

1.创建 gcloud 帐户

2.安装 g cloud CLI:https://cloud.google.com/sdk/docs/downloads-interactive

curl [https://sdk.cloud.google.com](https://sdk.cloud.google.com) | bash
exec -l $SHELL
gcloud init

3.创建 Gcloud 帐户和项目(替换)

gcloud projects create <your>-dl --enable-cloud-apis
gcloud config set project  <your>-dl
gcloud services enable compute.googleapis.com

4.安装地形:https://www.terraform.io/intro/getting-started/install.html

brew install terraform

6.(如果你想给它部署一些代码的话是可选的)Fork 和 git 克隆深度学习实验

https://git lab . com/dice 89/deep-learning-experiments/forks/new

git clone [git@gitlab.com](mailto:git@gitlab.com):<your_user>/deep-learning-experiments.git

7.Git 克隆代码,用 GPU 定义 Google 计算引擎 VM

git clone git@gitlab.com:dice89/google-cloud-gpu-vm.git
cd google-cloud-gpu-vm

6.创建 ssh 密钥

ssh-keygen -t rsa -b 4096 -C “your_email@example.com”

7.将私有 ssh 密钥添加到 Google 云基础架构“start_up_script.sh”

8.将公共 ssh 密钥添加到您的 Gitlab 帐户

9.创建一个 GCP 服务帐户并获取凭据

gcloud iam service-accounts create gcp-terraform-dl --display-name gcp-terraform-dlgcloud projects add-iam-policy-binding  <your>-dl \
            --member='serviceAccount:gcp-terraform-dl@<your>-dl.iam.gserviceaccount.com' --role='roles/owner'gcloud iam service-accounts keys create 'credentials.json' --iam-account='gcp-terraform-dl@<your>-dl.iam.gserviceaccount.com'

10.初始化你的地形环境

terraform init

11.启动环境

terraform apply \
  -var 'project_id=<your>-dl' \
  -var 'region=europe-west1-d'

稍等片刻(大约 5-10 分钟),查看您的 jupyter 笔记本电脑服务器的 IP 地址:

terraform show | grep assigned_nat_ip

要 ssh 到您计算实例:

gcloud compute — project “<your>-dl” ssh — zone “europe-west1-d” “gpu-vm”

12.破坏环境

terraform destroy \
  -var 'project_id=<your>-dl' \
  -var 'region=europe-west1-d'

如果你发现教程有什么问题,请向我反映!我非常希望它保持最新。

搜索算法介绍-广度优先搜索

原文:https://towardsdatascience.com/search-algorithm-introduction-1-a71e4a1911b3?source=collection_archive---------21-----------------------

搜索

从头开始 Python 实现

搜索是关于规划的,广泛应用于自动驾驶汽车和路线规划。在本帖中,我们来谈谈在给定的静态环境下寻找可行路径的问题。结构遵循这里的课程,我们直接进入一个问题,基于问题设置引入 first search。

设置

假设我们生活在 2D 网格世界中:

网格大小为 5 乘 6,0 表示可用,1 表示堵塞。我们从点[0, 0]开始,目标位于右下角[4, 5]。有 4 个动作可用left, right, up and down对应delta_name中列出的 4 个符号。现在的问题是找到一条从起点到目标的可行路径。

横向优先搜索

有许多搜索算法,让我们从广度优先搜索开始。与深度优先搜索相反,广度优先搜索开始水平扩展搜索路径:

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

refer to wiki

如图所示,从节点1开始,在深入到下一层之前,它会探索它的所有邻居。与我们的问题设置相同,我们的 2d 网格世界也可以被视为一个可扩展的树,我们从初始点[0, 0]开始,从这里,我们只能向下到[1, 0]或直接到[0, 1],只有在迭代完所有这些邻居之后,我们才会探索下一级的节点。

因此,让我们考虑一下实现广度优先搜索需要哪些组件。首先,我们肯定需要维护一个当前扩展路径的队列或列表,并且能够弹出下一个最低级别的节点。其次,我们应该维护另一个列表来跟踪所有被访问过的节点,这样我们就不会两次访问一个节点。

所以这里的close是我们追踪所有访问过的节点并用值1表示它们的列表,而open_list是扩展路径,其中每个元素记录了节点的级别和节点位置→ [g_value, x, y]

搜索路径不断扩展,直到:

  1. 没有可浏览的节点:len(open_list) == 0
  2. 找到目标:[cur_x, cur_y] == goal

sortreverse确保每次弹出最小级别的节点。如果在那个层次上没有找到目标,它将通过迭代所有可能的动作来扩展节点,同时通过nxt_g = cur_g + cost添加层次。

最后,我们打印出结果:

[11, 4, 5]

所以需要 11 步才能达到我们的目标[4, 5]。你可以算出 11 实际上是我们的最优值。

上面的代码贯穿了整个过程,但是好像缺了点什么……我们并没有真正找到我们的路径并打印出来!

要打印出我们的路径,我们需要在扩展时跟踪每个单元格中采取的每个动作,然后以相反的方式从目标到初始点,以获得我们的最终路径。

列表action在展开过程中跟踪每个动作,就像在 for 循环中一样:

action[nxt_x][nxt_y] = delta.index(d)

扩展后的行动列表包括:

[-1, 3, -1, 0, 3, 3]
[2, 2, -1, 0, 3, 3]
[2, 2, 3, 3, -1, 2]
[2, 2, -1, -1, -1, 2]
[2, 2, 3, 3, -1, 2]

策略列表从目标状态开始,以相反的方式打印出代表动作的符号,最后,我们得到最终结果:

['>', 'v', ' ', ' ', ' ', ' ']
[' ', 'v', ' ', '>', '>', 'v']
[' ', '>', '>', '^', ' ', 'v']
[' ', ' ', ' ', ' ', ' ', 'v']
[' ', ' ', ' ', ' ', ' ', '*']

您可能会注意到有多条最佳路径,但是该算法只能根据扩展顺序打印一条路径。我们将在下一篇文章中介绍另一个实现,它给出了所有的最优路径。

最后,广度优先搜索可以找到给定条件下的最优结果:

如果路径代价是节点深度的非减函数,则广度优先搜索是最优的。最常见的情况是所有的行为都有相同的成本。

一个直观的理解是,由于广度优先搜索仅在当前级别的所有节点都已被访问时探索下一级别,所以它总是在可能的最低级别中找到目标。

参考:

  1. https://classroom . uda city . com/courses/cs 373/lessons/48646841/concepts/112 e9f 79-63cd-44ee-883 f-652677 e64d 31

在 GCP 上用人工智能搜索外星人

原文:https://towardsdatascience.com/searching-for-et-using-ai-on-gcp-b45b07ba5b6?source=collection_archive---------26-----------------------

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

Photo by Austin Johnson on Unsplash

一个使用 SETI 开放数据的项目

他们说学习数据科学的最好方法是创造一些东西。

一旦你使用教科书、博客文章,当然还有 MOOCs(大规模开放在线课程)覆盖了数据处理、编码和统计的基础知识,接下来要做的就是做一个你感兴趣的项目。通过这种方式,您可以使用您所学的各种工具和技术,另外,您可以以一种现实而有意义的方式进行数据科学研究,因为您必须实际找到数据,为分析做好准备,最重要的是,您必须提出要问的问题。

对我来说,这就是我最近花了几年时间学习无数在线课程的地方。我已经达到了 MOOC 疲劳的暂时状态,我想做一些长期而深入的工作。我开始四处寻找有趣的数据,最终偶然发现了来自 SETI 研究所(搜寻外星智能)的各种文件和 GitHub 知识库。

最初,看起来我需要的所有数据和代码都是可用的,包括 IBM 托管的一些大数据集,加上一些分析代码。然后我意识到,相当一部分是来自已经停止的项目,留给我许多零碎的东西,但没有具体的东西。

令人欣慰的是,在收到几封邮件后,SETI 的人热心地帮助我,并明确表示,让“公民科学家”参与进来是他们希望在未来做得更多的事情。我加入了他们的 Slack 频道,打了一个 Skype 电话,并获得了几个额外数据集的链接。这是一种惊人的、令人耳目一新的接触数据爱好者的方法,我以前从未遇到过。

满怀热情的我接着回顾了 SETI 过去和现在的所有公众参与项目,以便找到我的项目想法。

SETI 和公民科学

2016 年 1 月,伯克利大学伯克利 SETI 研究中心启动了一项名为突破倾听的计划,被描述为“迄今为止最全面的外星通讯搜索”。无线电数据目前由西弗吉尼亚州的格林班克天文台和新南威尔士的巴夏礼天文台收集,光学数据由加利福尼亚州的自动行星探测器收集。

为了吸引公众,Breakthrough listen 的主要方法是一种名为 SETI@Home 的东西,可以下载并安装一个程序,空闲时可以用你的电脑下载数据包并对其进行各种分析。

除此之外,他们还共享了一些启动脚本和一些数据。脚本可以在 GitHub 这里找到,数据存档可以在这里找到(虽然大部分是“基带”格式,与我一直使用的“滤波器库”格式相比,这是一种更原始的格式)。请注意,来自自动行星探测器的光学数据也是一种不同的格式,称为“FITS”文件。

SETI 让公众参与的第二个举措是 2016 年 9 月启动的 SETI@IBMCloud 项目。这为公众提供了通过 IBM 云平台访问大量数据的机会。这个项目也附带了一个优秀的启动脚本集合,这个集合仍然可以在 GitHub 这里找到。不幸的是,在编写本报告时,该项目被搁置,数据无法访问。

SETI 对深度学习的运用

SETI 还有一些其他的在线数据来源。2017 年夏天,他们举办了一场机器学习挑战,向参与者提供了各种大小的模拟数据集以及盲测集。获胜团队使用卷积神经网络实现了 94.7%的分类准确率。这项挑战的目的是尝试一种新的信号检测方法,即超越传统的信号分析方法,在将信号转换为光谱图后,将问题转化为图像分类任务。

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

A squiggly signal in simualated SETI data

主要训练数据已被删除,但数据的“基本”、“小型”和“中型”版本仍在 GitHub 页面上。这些信号的详细性质以及更详细的挑战描述可以在这里找到。

注意,许多由 SETI 编写的托管在 Github 上的脚本使用了一个名为 ibmseti 的非标准 Python 包。

SETI 在机器学习方面的工作最近成为头条新闻,当时一种深度学习算法被应用于来自绿色银行望远镜的大量数据,这些数据与一个名为 FRB 121102 的射电源有关。被认为来自 30 亿光年外的一个矮星系,一些神秘的信号被发现,让媒体陷入外星人引发的狂热。然而,这些信号的一些细节,如它们的极化,表明它们已经通过了一个极其强大的磁场,导致了它们来自一颗中子星的假设,也许是在一个大质量黑洞附近。

进一步的细节可以在张,格里等人的论文“快速无线电突发 121102 脉冲检测和周期性:机器学习方法”中找到天体物理学报 866.2 (2018): 149。

这项工作的所有数据和相应论文的链接可以在这里找到。

ABACAD 寻找 ET 的方法

一些 SETI 数据寻找非常快速的信号,即在很宽的频率范围内存在很短时间的信号。来自上述快速射电爆发论文的数据使用了这样的数据。另一种 SETI 数据被用来做相反的事情,即信号在很窄的频率窗口在较长的时间帧。这类数据从一开始就让我更感兴趣,因为它似乎更有可能包含任何有目的的外星信号。

然后我看到了下面这篇文章:对智慧生命的突破性监听搜索:对 692 颗邻近恒星的 1.1-1.9 GHz 观测。天体物理学报 849.2 (2017): 104。

在其中,使用非机器学习技术分析了来自 692 颗恒星的此类数据,其中许多基础数据是共享的(从现在起我将把这项工作称为“突破 692”论文/项目)。使用的一种技术被称为“ABACAD”方法。思路如下:从目标恒星(第一个“A”)收集数据,然后将望远镜移动到不同的目标(“B”)。然后,回到 A,然后是另一个目标 C,再次回到 A,然后是最后一个不同的目标 d。这个想法是,如果信号来自主要目标恒星,它将出现在所有 3 A 扫描中。然而,如果一个信号来自陆地,它可能会在所有 6 次扫描中出现。

这篇论文让我产生了将这些数据用于机器学习的想法。我找到了我的项目!

我的项目

最初,我开始玩 2017 年 SETI 夏季机器学习挑战的模拟数据。起初我在本地这样做(即使用我的家用台式电脑),然后很快转移到在 Kaggle 上工作,这要感谢他们免费的数据托管和 GPU 支持。我创建了一个笔记本(在 Kaggle 上称为“内核”),介绍了典型的 SETI 数据和 filterbank 文件格式,随后是一个使用深度学习区分不同类型模拟数据的笔记本(根据夏季挑战)。

然后,我转向突破性的 692 数据,并决定尝试使用云计算来实现这一点,因为我知道,在 ABACAD 搜索中生成的大量 filterbank 文件中翻腾将受益于云平台提供的缩放能力。不幸的是,我对这个主题知之甚少,所以我暂停了这个项目,直到我在 Coursera 上完成了出色的谷歌云平台专业化数据工程。

一旦完成,我就开始在 GCP 数据实验室(谷歌的云版 Jupyter 笔记本)上整理代码。我把这个问题分成 4 个部分,

  1. 创建谱图图像——将滤波器组数据转换成谱图图像文件
  2. 模拟数据 —这必须与 ABACAD 搜索得出的数据类型相似,与夏季挑战赛的模拟数据截然不同
  3. 构建深度学习模型 —使用模拟数据创建和评估模型
  4. 从 ABACAD filterbank 文件中进行预测 —使用模型发现信号,不仅仅是每个图像,还包括 ABACAD 扫描技术的背景

模拟数据

为了模拟数据,我想出了一些类别(部分基于夏季挑战类别,部分基于我在突破 692 结果中看到的)。这些是:噪音、线条、断续线、抖动曲线。我试图确保信号水平和背景噪声水平的种类,以及像素的数量,与突破 692 项目中看到的数据种类相匹配。下面是每种方法的一些例子,

线

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

维布尔

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

斩线

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

曲线

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

噪音

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

该代码有一个变量列表,它可以取某个范围内的任何值。这些包括数值变量,例如背景噪声水平、信号强度、线的梯度、截断线中间隙的大小等。这些是每次随机选择的,允许批量创建每个图像类别的变体。还有一些更多的实验变量,如一个叫做“幽灵”的变量,其中信号的副本被复制到主信号的左侧和右侧。然而,我还没有探究它的影响。

构建深度学习模型

对于这个项目的深度学习方面,我最初使用了 VGG19 和 InceptionNet 等预构建模型。然而,我后来得出结论,对于这个应用程序来说,这些可能过于复杂,所以最终使用 Keras 框架来构建一个简单的模型架构(受我读过的一些 Keras 博客帖子的启发)。我使用的模型总结如下:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 188, 188, 32)      9632      
_________________________________________________________________
activation_1 (Activation)    (None, 188, 188, 32)      0         
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 94, 94, 32)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 94, 94, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 90, 90, 32)        25632     
_________________________________________________________________
activation_2 (Activation)    (None, 90, 90, 32)        0         
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 45, 45, 32)        0         
_________________________________________________________________
dropout_2 (Dropout)          (None, 45, 45, 32)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 43, 43, 64)        18496     
_________________________________________________________________
activation_3 (Activation)    (None, 43, 43, 64)        0         
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 21, 21, 64)        0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 21, 21, 64)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 28224)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 197)               5560325   
_________________________________________________________________
dropout_4 (Dropout)          (None, 197)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 5)                 990

然后,我使用 RMSprop 优化器对其进行了 100 多个时期的训练,给出了以下准确度和损耗图。

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

这实现了大约 84%的测试数据准确性。下面是测试阶段的混淆矩阵,

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

单幅图像预测

一旦模型被创建,我需要一些图像来测试。我从突破 692 论文里取了一些,看看模型给出了什么。下面是几张来自 HIP4436 的图片,第一张是噪点,

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

这是模型的预测,

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

如你所见,它正确地将其归类为噪声。接下来,有信号存在的图像,

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

这个预测,

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

“line”类是明显的赢家。

根据 ABACAD 过滤器库文件进行预测

从 filterbank 文件中提取一部分并对其进行分类是很好的,但正如前面提到的,SETI 使用 ABACAD 搜索技术来决定目标是否是感兴趣的。为此,我编写了一个“评分函数”(我在代码中实际上称之为“外星人探测器”,因为这样听起来更酷),对 6 幅图像进行预测,对应于 6 个 filterbank 文件的相同频率范围,然后应用一些简单的规则,通过分配点数来决定整体模式是否令人感兴趣。评分工作如下,

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

例如,6 个“噪音”结果得零分。事实上,6 手任何一个相同的信号都不得分(例如线-线-线-线-线)。一个或多个 A 扫描中的一种信号类型与 B、C 和 D 扫描中的不同类型得到一些点,最大值分配给 3 A 扫描中的一种信号类型,B、C 和 D 为噪声(例如wibble-noise-wibble-noise-wibble-noise)。

这似乎工作得很好。例如,在 Breakthrough 692 论文中,使用非 ML 信号检测方法将以下标记为可能的候选者(HIP65352 ),

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

使用深度学习,这得到一个很低的分数,因为 B 扫描(倒数第二个),你可以仅仅用眼睛看到有一条微弱的线存在,检测到。

回到前面使用的 HIP4436 示例,我们有以下图像集合,

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

用深度学习模型给出以下预测:‘线’,‘噪声’,‘线’,‘噪声’,‘线’,‘噪声’,,如预期。这得到最高分。

新建数据

上面的例子使用了从突破 692 论文中共享的小数据文件。然而,SETI 向公众公开了大量未经处理的数据。分析单个目标所需的 x6 filterbank 文件高达令人瞠目的 100GB。我将这些文件用于 HIP4436,并将这 6 个文件中的前 3000 个频率片转换成 PNG 图像文件。

这些然后通过深度学习模型发送,然后通过评分功能,最后结果被保存到 csv 文件中。对于如此少量的频率,这将产生一个微小且易于使用的文件,但如果处理整个滤波器组范围,产生的 csv 文件将有 50 万个条目(尽管您总是只能保存“命中”,这将少得多)。

为了将结果转换成更加“云友好”的格式,我测试了从结果中创建一个 BigQuery 数据库。这非常简单,并且可以作为处理管道的一部分来完成,然后允许感兴趣的团体搜索高分目标。举个例子,

SELECT *
FROM SETI _ results . SETI _ results _ table
其中分数> 0
按分数排序 DESC

给出最高得分目标。

整体流程

下图显示了如何将此流程放入一个简单的管道中,不同的流交错排列,以显示扫描完成后可以立即开始处理 A1 数据(在所有流完成并输入评分功能后,结果将与其他 5 个流合并)。

这些独立的流甚至可以由 6 个不同的 GCP 实例来处理,从而大大加快速度。

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

时间就是金钱

据我所知,SETI 数据的一个问题似乎是它的剪切比例。单个 filterbank 文件是 17GB,我们每个目标处理 6 个。在每个单独图像的频率分辨率下,每个滤波器组生成 500,000 个图像。因此,每个目标都需要通过深度学习模型处理惊人的 300 万张图像。

我在一个 GCP 实例上测试了一些东西,具体来说,是一个带有 1 个 NVIDIA Tesla K80 GPU 的 n1-standard-8 (8 个 vCPUs,30 GB 内存)。利用这一点,我能够对每次扫描的 2999 幅图像做出预测,或者在大约 187 秒内总共 17,994 幅图像。基于此,300 万次预测需要大约 8 个小时。

我们可以从下面的成本计算器中看到,如果我永久保留这个实例,成本将是每天 15 美元左右。因此,使用这种方法可以处理全套 6 个滤波器组文件,费用约为 5 美元。

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

因为这是云计算,如果可以承受更高的成本水平,这个价格可以降低以节省资金,或者分析一组 filterbank 文件的时间可以减少(甚至大大减少)。

下一步去哪里?

由于我不是 SETI 的专业研究人员,毫无疑问,上述所有方面都可以改进,包括:

  1. 数据模拟 —改善信号类别(信号形状、信号电平、信号噪声等)。在给定图像中包括不止一个信号(例如几行而不是一行)。我最初生成了一个名为“brightpixel”的图像类,用于夏季 ML 挑战赛。然而,这似乎破坏了我的模型。这可以进一步调查
  2. 深度学习模型 —尝试不同的模型配置、不同的超参数、不同的预处理方法等。针对更多数据和更多时期进行训练
  3. ABACAD 评分 —为决定 ABACAD 功能的结果设计一个更好的评分系统
  4. 大规模模型部署 —使用 GCP 允许过滤器库集合(来自 ABACAD 搜索的 x6)通过部署的模型自动运行
  5. 代码优化 —通过提高代码效率来加速预测

欢迎专业人士的任何意见或建议。

这个项目的代码可以在这里找到。

感谢 Emilio Enriquez、Andrew Siemion 和 Steve Croft 在这方面的帮助,以及 SETI 对让公民科学家研究这些迷人数据的普遍开放态度。

在洛杉矶县寻找食物沙漠

原文:https://towardsdatascience.com/searching-for-food-deserts-in-los-angeles-county-b573467a55b?source=collection_archive---------16-----------------------

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

img source: robrogers.com

在最近的一个数据科学项目中,我与其他几位 Lambda 学校的学生合作,在洛杉矶县寻找食物沙漠。食物沙漠的一般定义是在一英里范围内没有杂货店/市场提供新鲜、健康的食物,如水果、蔬菜、肉类等。我们想验证低收入社区更有可能生活在食物沙漠的理论。

第一步:获取数据

我们需要为这个项目找到 3 种主要类型的数据:洛杉矶县的杂货店/市场的数据,洛杉矶县每个邮政编码的地理数据,最后是每个邮政编码的收入数据。

对于杂货店,我们从 SNAP retailers 数据库获取所有加州的数据,然后过滤洛杉矶县的数据。不幸的是,这一数据过于宽泛,因为它包含了许多便利店、酒类商店和其他随机机构,这些机构不应该有资格提供健康和新鲜的食物。使用 regex,我们试图尽可能多地消除这些不合格的机构。最后,我们使用每个市场的纬度和经度,并使用 Shapely 库生成一个点对象。

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

接下来,我们能够找到洛杉矶县每个邮政编码的地理信息。然而,由于地球的曲率,我们需要将这些数据转换成二维表示,或投影,以便我们可以用图形显示它。我们将数据读入 geopandas 数据帧,然后调用 to_crs() 方法,并传入“epsg=4326 ”,该方法将地理数据从 GIS 坐标转换为纬度/经度,并格式化邮政编码多边形对象,以便在二维可视化中正确显示。

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

我们获得的最终数据是洛杉矶县每个邮政编码的家庭收入中值,我们将其存储在 pandas 数据框架中,以便在项目的后期与 food desert 和杂货密度合并。

.

第二步:在整个洛杉矶县进行网格搜索

我们希望对整个洛杉矶县进行彻底的网格搜索,以确定任何食品沙漠的位置,并为每个邮政编码汇编一些汇总统计数据,如 1 英里内杂货店的平均数量,以计算每个邮政编码的杂货店密度指标。然后,我们将这些数据与每个邮政编码的平均收入进行比较,并检查结果。

首先,对于每个邮政编码,我们在邮政编码的地理边界周围创建了一个矩形,然后从西北角开始,以四分之一英里的步长水平(向东)迭代,然后再次从西边开始,但向下走四分之一英里,再次水平迭代,重复这个过程,直到整个区域都被遍历完。在每一步,我们都生成一个 Shapely Point 对象,并附加到该邮政编码的列表中。我们将每个邮政编码内的点列表添加到地理数据框架的新列中。对于整个洛杉矶县来说,这导致了将近 58,000 点。

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

接下来,对于每个邮政编码,我们遍历点列表中的每个点。对于每个点,我们使用 buffer() 方法在每个点周围画一个半径为一英里的圆,并将该圆保存为一个形状良好的多边形对象。因为每个形状良好的多边形对象都包含组成该对象形状外部的所有坐标的值,所以我们可以为该多边形调用 contains() 方法并传入一个坐标。无论给定坐标是否位于多边形对象的内部,该方法都会返回一个布尔值 True 或 False。

为了测试食物沙漠,我们可以检查所有市场的坐标,看看它们是否存在于那个圆圈的边界内。然而,数据框包含几千个市场,对洛杉矶县的每个市场的每个生成点执行这种检查在计算上是非常昂贵的。不用遍历数据框架中的每个市场,我们可以通过首先过滤坐标在我们的圆的正方形边界内的市场来大大加快这个过程,我们可以使用圆的 bounds 属性(一个形状良好的多边形对象)来访问它。现在,我们要测试的市场列表大大减少了。对于正方形内的每个市场,如果圆圈对象包含市场坐标,我们增加圆圈内食品杂货的数量。

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

最后,为了创建一个食品杂货密度统计数据以及一个包含所有食物沙漠点的主列表(在该点的一英里半径范围内没有找到市场),我们创建了一个函数来迭代每个邮政编码,然后迭代点列表中的每个点。我们为每个邮政编码初始化了一个计数器,记录在点列表中找到的食物沙漠的数量。我们还初始化了两个空列表,一个记录找到的食物沙漠的位置,另一个记录列表中每个点的圆圈内的食品数量。

遍历每个邮政编码后,我们对结果进行解包,以创建一个所有找到的食物沙漠的主列表,并按邮政编码计算汇总统计数据,如杂货密度(找到的附近杂货的平均数量,以及每个邮政编码的食物沙漠的总数)。

步骤 3:分析结果并创建可视化:

在解开详尽的网格搜索结果后,我们首先想要创建一个食物沙漠发现位置的可视化。如你所见,大部分位于洛杉矶县的北半部和西部。这些地区通常要么是国家公园,山区,甚至是真正的沙漠,所以这些食物沙漠的存在是完全有道理的。

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

接下来,我们根据邮政编码绘制了杂货店的密度。如你所见,城市中心的密度最高。

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

最后,我们想检查相对于收入的食品杂货密度,所以我们将收入分成 4 个范围,并绘制每个范围的平均食品杂货密度。

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

有趣的是,我们的结果似乎表明,低收入地区实际上比高收入地区有更高的杂货店密度。

结论/要点:

在考虑了为什么我们的结果与我们的预期相矛盾之后,我们学到了一些很好的数据科学经验。首先,任何数据科学项目的结果通常最终取决于底层数据的质量。在我们的例子中,市场和杂货店列表的基础数据过于宽泛。即使我们尽了最大努力使用 regex 手动过滤掉不合格的机构,仍然有数百到数千个“市场”可能不符合资格。不幸的是,我们没有足够的工时来逐一检查每一家店,并确定是否有健康/新鲜的食物。

此外,由于洛杉矶独特的地形,许多富人区位于蔓延的丘陵/山腰和海滨地区。这些较富裕的家庭大多拥有汽车,开车去一英里以外的杂货店没有问题。相反,收入较低的家庭往往位于人口更稠密的城市地区。这些家庭的成员通常没有汽车,但可以通过步行、骑自行车或使用公共交通工具出行。由于这些地区的总体密度较高,并且因为我们的市场数据框架包括许多街角商店、杂货店或民族商店/零售机构,这些地区的杂货密度最终高于高收入邮政编码地区。

总的来说,我认为这是一个伟大和令人愉快的项目。我在一些新的 python 库方面获得了一些经验,包括 GeoPandas、Shapely、pyproj,制作了一些简洁的地理数据可视化,并在此过程中学习了一些有价值的数据科学课程。

如果有兴趣看看我们的一些代码,这里有一个链接指向这个博客的笔记本,这里有一个链接指向我们的团队回购

西雅图 AirBnB 市场价格分析

原文:https://towardsdatascience.com/seattle-airbnb-market-price-analytics-97196545da3a?source=collection_archive---------16-----------------------

查看 AirBnB 西雅图公共数据集进行价格预测,并了解预测变量

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

Fig.1.Dr. Frasier Crane looks out on Seattle in the series finale, on March 23, 2004. FROM NBC/NBCU PHOTO BANK/GETTY IMAGES.

每当我看到“西雅图”时,这句名言就在我脑海中回响:“下午好,西雅图。这是弗雷泽·克雷恩博士。”。看过这部电视剧的人都知道克雷恩博士有一套华丽的公寓。我敢肯定,如果他的公寓在 airbnb 上,就不会一直空着。我告诉你,我们会看看能有多少。

我们的数据集来自 Kaggle。1

文档和笔记本也可以在https://github.com/elifinspace/udacity_crispdm找到。

快速浏览一下数据,让我们从一些问题开始:

  1. 西雅图的高峰日是什么时候?
  2. 哪些地区比其他地区更贵?
  3. 决定价格的主要因素是什么?

数据探索

让我们开始探索数据集。我们有 3 个数据文件:

  • 日历. csv
  • listings.csv
  • 点评. csv

日历数据集有每个列表的日期和价格。通过简单地计算工作日和月份的平均值,结果是周六是最昂贵的工作日,平均值为86.69 美元,而七月是最昂贵的月份,平均值为94.76 美元。

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

Fig.2

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

Fig.3

我们的第二个数据集 listings.csv 包含位置、便利设施、运营成本和价格等信息。

通过邮政编码或邻近地区计算平均值将有助于我们了解哪些地区价格更高。我选择使用邻居,因为它比邮政编码或邻居组更细粒度。我已经用叶子来绘图了。[3]

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

Fig.4. Mean Price by Neighbourhood Group Cleansed

近距离观察西雅图市中心:

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

Fig.5 Mean Price by Neighbourhood Group Cleansed (zoomed)

原来安妮女王是最贵的地区。随着我们远离中心,平均价格越来越低。

不仅是西雅图市中心,西雅图南部的一些地方似乎也很贵,比如西西雅图的滨水区和贝尔镇(西雅图的西北部),如下图所示。

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

Fig.6 Mean Price by Neighbourhood Group Cleansed (zoomed)

克兰博士虚构的公寓位于安妮女王区,有 3 间卧室,3.5 间浴室。快速搜索位置,卧室和浴室的数量,整个公寓的价格将在 750 美元左右。

我们的第三个数据集 reviews.csv 包含评论、按日期排列的评论和列表。经过一些数据争论,我决定使用评论极性。我看到评论不仅是英语的,还有法语、德语和其他几种语言的。我只使用了英语评论,并按邻里群体划分了评论的极性。

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

Comment Polarity by Neighbourhood Group

评论极性图与邻近地区的平均价格一致。虽然安妮女王有更大的极性(更积极),但大学区与其他街区相比极性较低。类似地,其他昂贵的地区如西西雅图也有很高的评论极性。这个结果告诉我们,邻居在定价中起着重要的作用,这是合理的。

特征选择

让我们看一下列表数据,看看哪些字段与邻居相关。除了“邻里”和“邻里 _ 团体 _ 净化”之外,我们还有一些自由文本栏“邻里概况”、“描述”和“交通”。为了简单起见,我选择只使用这些列的极性,而不是更复杂的语义分析。

我添加了几个特性,如“time_to_first_review”和“time_since_last_review”作为流行度的指标,并删除了原来的日期列。计算结果见。

我使用的另一个分类特征是列表中的“便利设施”。我们知道,就顾客满意度和价格而言,便利设施非常重要。后来我们会发现我们没有错。

回归

我选择了 RandomForestRegressor 来预测价格[4]。在这种情况下,Ridge 和 SVR 不起作用。

为了评估我们回归器的性能,我们可以看看 mse(均方误差)和 r2 得分,这是数据与拟合回归线接近程度的统计度量。

r2 得分为 0.63,这很好,下图也显示了回归变量的预测效果。回归器对于大值的表现不是很好,可能是因为缺少训练样本。

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

Measured vs Predicted Values (Scaled Prices with a factor 100)

那么我们的回归变量用于预测的特征是什么呢?

首先,请记住,在基于树的算法中,每个特征的贡献不是单个预定值,而是取决于特征向量的其余部分,该特征向量确定了遍历树的决策路径,从而确定了沿途经过的保护/贡献。[5]

绝对特征重要性与特征:

 index               feature  coefficient
0     12              bedrooms     0.413892
1     15          cleaning_fee     0.053380
2    101          private room     0.039854
3     81  description_polarity     0.036671
4     14      security_deposit     0.030007
5     10          accommodates     0.029731
6      7              latitude     0.027472
7     36     reviews_per_month     0.027280
8     11             bathrooms     0.025478
9     23      availability_365     0.023142

当我们看上面的特征重要性表时,“卧室”是最重要的因素。此外,一些相关的功能,如“容纳”和“浴室”也在最重要的 10 个功能。我们知道容纳更多人的房间总是更贵。此外,我们可以看到,房间类型“私人房间”在定价中也起着重要作用。

“清洁费”似乎与“保证金”和“可用性 _365”一样对定价有显著影响。这些是与运营成本相关的变量。成本越高,价格就越高。

供求定律是一种经济理论,它解释了供给和需求之间的关系,以及这种关系如何影响商品和服务的价格。这是一个基本的经济原则,当商品或服务供大于求时,价格就会下跌。当需求超过供给时,价格就会上涨。[6]这解释了为什么我们在这个表格中有“每月评论”。

“纬度”和“描述极性”是与邻里关系相关的变量。如果你看一下列表,你会看到“描述”有时包括关于便利设施和邻居的信息。所以描述越正面,价格越高。而“纬度”,即一个点相对于赤道的南北位置,被证明是一个重要的因素。因为西雅图有一个狭长的形状,这意味着纬度比经度变化更大,因此纬度而不是经度对价格的影响更大。

结论

我们看到,位置、运营成本、便利设施和受欢迎程度是决定价格的主要因素。

从而通过谈判降低运营成本,在中心位置有一个宽敞的地方,提供私人房间而不是共享或整个公寓。而且,公开露面有助于空中旅馆赚更多的钱。此外,为什么不通过提供折扣或其他活动在高峰期吸引更多的客人呢?

感谢您的阅读!

参考资料:

【2】:【https://pypi.org/project/folium/

[3]:https://www . thrillist . com/home/photo-tour-of-frasi er-crane-s-Seattle-apartment

[4]:https://sci kit-learn . org/stable/tutorial/machine _ learning _ map/index . html

【5】:https://blog.datadive.net/interpreting-random-forests/

[6]:https://www . investopedia . com/ask/answers/033115/how-law-supply-and-demand-affect-prices . ASP

帮助我学习机器学习的秘密元素!

原文:https://towardsdatascience.com/secret-element-that-helped-me-learn-machine-learning-e6a29abb7b3b?source=collection_archive---------31-----------------------

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

Photo by Emily Morter on Unsplash

这段旅程开始于将近一年半前。

我还记得我以前看那些机器学习需要的复杂的数学概念的时候会觉得不舒服。更糟糕的是,我非常焦虑和不知所措,因为有大量的概念需要学习。

我曾经怀疑,质疑自己。

我能做到吗?

你数学不是很好,你还能做到吗?

有那么多东西要学,你还能做到吗?

但在所有这些问题中,有一件事没有让我停下来。这更加激励了我的学习。秘密且通常被遗忘的元素。

我过去常常问这么多问题。好奇心达到顶峰。我很想知道:这些东西是如何工作的?为什么只有这种方式?为什么不是这条路?,如果我们把这块换成这块呢?

"尽可能以最散漫、最不敬和最原始的方式努力学习你最感兴趣的东西."理查德·费曼

伴随着好奇心而来的是学习的意愿。它不再是一个负担。现在你学习不是因为外在的官能,你想学习是因为你想知道和理解事物,你想知道你早就问过自己的问题的答案,你只是不想成为一个旁观者,你想成为这个领域的参与者。

如果你不够好奇,你只是在强迫自己去学东西。近期是不行了。你要么放弃,要么把自己打得太狠!两者都将导致不幸。

相信我,一旦你足够好奇,你会把学习放在首位,像孩子一样开始提问。

机器学习主要是关于实验的。这是一个反复的过程。你有一种直觉,认为这东西可以工作,你尝试了。它不起作用。你试了另一个。它也不起作用。别担心,让我们试试这个,看看是否可行。而且它起作用了,现在你可以愉快地记录下你已经尝试了什么,什么起作用了,什么不起作用了,并给出最好的理由。你将这些知识融入到你的下一个项目中。

这就是我学习机器学习的方式。

甚至在理解任何新概念的时候,都是从好奇心开始的。如此多的问题开始出现。这个概念带来了什么不同的东西或想法?和以前的方法有什么不同?它在其他环境中也会起作用吗?从中可以学到什么?

明白重点了吗?都是问题。

在这个快节奏的世界里,时间紧迫,压力重重,我们常常会忘记真正的要点。这篇文章是对你的一个善意的提醒。

“重要的是不要停止提问。好奇心有它存在的理由。”
阿尔伯特·爱因斯坦

你不需要为难自己。你只需要更加好奇。

如果您发现它在任何方面都有帮助,请确保帮助我传播这个消息,并关注我以获得更多这样的文章。和平与力量。

谷歌基于网络的 OCR 服务的秘密

原文:https://towardsdatascience.com/secret-of-google-web-based-ocr-service-fe30eecedd01?source=collection_archive---------15-----------------------

光学字符识别简介

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

Photo by Edward Ma on Unsplash

光学字符识别(OCR)是连接现实世界和虚拟世界的方法之一。第一个 OCR 系统是在 20 世纪 20 年代末推出的。OCR 的目标是从图像中识别文本。然而,由于许多因素,实现非常高的精度是非常具有挑战性的。在下面的故事中,我将介绍谷歌如何构建解决方案,这是谷歌云视觉 API 之一,以解决这个问题。

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

谈到 OCR, tesseract 是一个著名的开源库,每个人都可以利用它来执行 OCR。宇宙魔方由惠普发现,自 2006 年以来一直由谷歌赞助开发。Tesseract 3.x 模型是旧版本,而 4.x 版本是由深度学习(LSTM)构建的。如果你想了解 3.x 和 4.x 的区别,你可以访问分享了解更多细节。

由于 tesseract 是由 C++实现的,我们不能像调用其他 python 库一样调用它。事实上,我们可以在 python 中调用 C-API,但是它对用户不太友好。因此,python 包装器pytesserac的引入让我们的生活变得更加轻松。

定义

在介绍架构设计之前,需要介绍一些定义。

  • 脚本 vs 语言:脚本不同于语言。文字指的是书写系统,而语言大多数时候指的是口语。在下图中,“数据科学家”是罗马体的英语,而“书局科学家”是罗马体的中文。

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

Script vs Language

  • 边界框:与其他 OCR 系统不同,边界框包括单行检测文本,而不是单个字符或单个单词。
  • 模型考虑:除了准确性,成本,通用性和可维护性也被考虑来建立模型。

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

给出一幅图像,它要经过 5 个阶段才能在谷歌视觉 API 中得到最终结果。

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

Google Cloud Vision API Architecture (Walker et al., 2018)

文本检测

第一步是使用传统的基于神经网络(CNN)的模型来检测和定位文本行,并生成一组边界框。

方向识别

按边界框对方向进行分类。如果需要,一些边界框将被过滤掉,因为它被错误地检测为文本。

脚本识别

标识每个边界框的脚本。假设每个边界框有一个脚本,但允许每个图像有多个脚本。

文本识别

这是 OCR 的核心部分,它从图像中识别文本。它不仅包括基于字符的语言模型,还包括初始风格的光学模型和自定义解码算法。

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

Inception Model (Fujii et al., 2017)

布局分析

确定订单的阅读和区分标题、页眉等。

实验

如前所述,宇宙魔方是由谷歌赞助的。我想这也是作者拿结果和宇宙魔方比较的原因之一。模型比较采用字符差错率(CER)。它被定义为编辑距离除以参考长度并乘以 100。越低越好。

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

Tesseract vs Google Cloud Vision API (Walker et al. 2018)

拿走

持有一,为什么图像处理步骤不包括在管道中?论文没有提到原因。我相信这是由于神经网络模型已经能够捕捉那些特征。

除了性能,速度是另一个考虑因素。从我的经验来看,宇宙魔方在识别方面做得很好,但速度相当慢。

关于我

我是湾区的数据科学家。专注于数据科学、人工智能,尤其是 NLP 和平台相关领域的最新发展。你可以通过媒体博客LinkedInGithub 联系我。

参考

沃克 j,藤井 y,Popat A. C…2018.基于网络的文档 OCR 服务。

王晓东,王晓东,王晓东,王晓东,王晓东,王晓东。…2017.用于多语言 OCR 的序列到标签文字识别

成功的数据科学面试秘诀

原文:https://towardsdatascience.com/secrets-to-a-successful-data-science-interview-28fc902ed23f?source=collection_archive---------16-----------------------

所以,你被叫去参加一个数据科学面试…但是,你不清楚如何破解数据科学面试吗?你担心在数据科学面试中被拒绝吗?您是否对数据科学面试准备什么感到困惑?如果你有以上问题,那么你就来对地方了。我们有兴趣分享我们从作为被采访者和采访者的经历中学到的东西。

您正在阅读本文档,这反映了您作为一名成功的数据科学家的认真态度。在数据科学这个已经非常广泛且迅速发展的领域中面试,对你和面试官来说都是一个挑战。面试试图分几轮评估您的数据科学能力,每轮持续一小时,而您的学习将持续一生。这让面试官(我们去过!)处于一个不值得羡慕的位置,也就是说,如何评估像你这样充满热情、知识渊博的人?好的面试官会将你视为潜在的未来同事:他们需要你的帮助。我们相信,如果你是采访者,而我们是被采访者,你也许会要求我们按照下面提到的方式进行合作。

如何开始?

这一切都从你在简历中写了什么开始。期待你的简历中与数据科学相关的问题。这是可以理解的,他们在你的过去很深(>三年),但不要被他们困扰。我们相信你有能力给自己的简历上的任何东西一个很好的证明。请重访记忆巷。重新认识你的过去。*假设你过去用方法 X 解决了某个问题,如果你现在要解决它,你有更好的方法 Y 吗?*此外,问问你自己:我是否在简历上写了一些东西只是为了让面试官留下深刻印象,而不是根据这些来提问?如果你是面试官,你觉得公平吗?注意:这同样适用于技术博客,以及简历中的 github 链接。

解决问题:开放式和封闭式

您的数据科学深度将根据您解决问题的能力进行评估。这些问题可能是:

  1. 给定一个要求,如何解决。例如,你的企业正在失去客户,找到原因和解决方案。
  2. 给定一个解决方案,它能改进吗?例如,现有解决方案的“转换率”为 90%,您如何使其达到 91%。

你能重用你过去的方法,并使它们在一个新的但不同的环境中工作吗?你是否也能在最近所学的背景下反思你的旧方法?

这些是开放式问题,在面试范围内可能没有完整的解决方案。请记住,这些问题不是为了从你那里免费获得十亿美元的创业想法或可申请专利的想法;远非如此,这些纯粹是为了评估你解决问题的能力。事实上,他们中的一些人可能会对面试官试图解决的数据科学问题产生共鸣。

既然我们已经完成了开放式问题,这里有一个封闭式问题的样本:推导 CNN 的反向传播,解释梯度推进等。前者帮助面试官评估你对意外情况的反应,而后者建立了一个植根于标准理解的基线过滤器。尽你最大努力两者兼得。练习是关键。

机器学习

数据科学使用机器学习作为关键技术之一。是的,它可能还会用到神经科学、行为经济学、博弈论、统计力学、复杂性理论、非欧几何以及你所擅长的无数领域。然而,你被期望在工业环境中解决的问题需要作为主要技能的 ML 技术的健全知识;如果你是其他领域的专家,面试官会很高兴,但也请你是 ML 领域的专家。

这是他们将如何评估你的 ML 技能。

  1. 标准机器学习课程和书籍涵盖的主题:CS229(斯坦福),CS4780(康奈尔),6–034(麻省理工),PRML(克里斯·毕晓普),海量数据集的挖掘。

深度学习

你是个深度学习的忍者。尽管你有强烈的感觉,为什么我们仍然认为你也需要对‘传统的’机器学习有一个良好的理解?深度学习模型的“黑箱”性质(你可能完全不同意)使得面试官很难知道你做了什么和模型做了什么。然而,ML 的传统形式似乎是所有候选人的良好公分母。如果一个人建造了一个双向 LSTM,却不知道 SVM 是如何工作的,会是什么样子?请继续,用你的 DL 技能让面试官眼花缭乱,但是在你用你的 ML 印章证明一两点之后。

你应该知道为什么你选择了一个模型/算法/方法/架构。超参数在 DL 中起着关键作用。培养对模型调整的良好理解。一个好的模型和一个不太好的模型之间的区别可能在于你对超参数的选择。

我们建议在参加面试前阅读以下材料:

  1. deeplearning.ai 在 Coursera 提供的深度学习专业化(5 门课程)
  2. Geoff Hinton 的 Coursera 神经网络课程,以加深对概念的理解。
  3. 涵盖编码器的全面深度学习课程http://www.cse.iitm.ac.in/~miteshk/CS7015.html
  4. 从长远来看,你可以去看看 Yoshua Bengio 和 Ian Goodfellow 写的深度学习书。

注意:我们并没有说你必须经历所有这些。通过细读高质量的内容来熟悉主题会给你信心,这种信心可以在不同的情况下转移。

使用工具的经验

公司会优先考虑那些不仅能清晰表达良好的数据科学解决方案,而且能够使用正确的工具高效实施的候选人。如果你能让自己对业内可用的工具感到舒适,那就太好了。你至少应该从语言的角度了解 python 或者 R,ML 算法的 scikit learn 库,深度学习的 Keras / tensorflow / pytorch / caffe。不要忽视查询语言,如 HQL、SQL 和分布式框架,如 hadoop 和 spark。好的组织对所有这些技能都有培训计划。被选中的候选人经常参加这些培训课程。熟悉以上工具会给你的候选人带来急需的优势。在你被选中后,一定要接受培训。这将暴露组织解决的问题,并有机会与组织中的其他科学家互动。作为一名新员工,这些都是非常重要的经验。

模型的生产和部署

面试官希望你知道机器学习模型是如何使用的,它是如何生产和部署的,以及你的模型的整体端到端架构。很难雇佣一个不知道客户/服务/顾客/产品将如何使用他的模型的人。没有部署,您构建的只是一个概念验证。在最好的情况下,它可以在您的笔记本电脑上进行演示。为了使你的 ML 模型有用,它需要成为软件管道的一部分。知道如何与工程师互动来部署模型。准备好卷起袖子,在没有任何人提示的情况下行动起来。通过这样做,你提高了自己的价值和在团队中的地位。

模型的生命周期管理

作为一名伟大的数据科学家,你应该了解 ML 模型的整个生命周期。你应该知道当你的模型试图模拟的世界改变时,你的模型应该如何改变。(这种情况比你想象的更频繁,因为现实世界并不在你的控制之下。)您将决定您的模型需要多长时间重新培训一次,以保持新鲜感。你可能还想自动化再培训过程,以节省你的宝贵时间。将迭代模型构建中节省的时间重新用于提高性能。为自己创造机会,解释你是如何在一个模型的生命周期中管理它的。

调试 ML 模型

调试是软件行业非常重要的技能。如果一个软件不能被调试,它就有很高的风险。你应该深入了解你的模型。你应该知道你所使用的算法的内部原理。您应该知道如何进行根本原因分析和调试您的模型。面试官希望你知道当模型有高偏差或高方差时,你将如何改善结果,你将如何避免爆炸梯度和消失梯度,以及你将如何在训练期间优化记忆等。

为什么反转链表仍然是一个好主意?

虽然数据结构和算法确实被外包给了软件包,但是您从数据中进行推理的能力是由您对算法和数据结构的理解所帮助的。虽然面试官不会要求你实现跳过列表或平衡 k-d 树,但他们仍然希望你理解算法的顺序复杂性,熟悉基本的数据结构,如链表、堆栈、树、哈希表和堆,并熟悉排序、最短路径、字符串处理等算法。换句话说,来自数据结构和算法的卫生问题。你可能认为通过这个镜头来评判你是不合适的,但是请记住你是在为比赛制造方便。我们相信你能胜任。

了解公司

你被要求参加面试的事实表明,你之前的经历已经被 HR 和 HM(招聘经理)认为是潜在的合适人选。不要停在那里!你有责任研究并了解你被叫去面试的团队是做什么的,以及你如何能增加价值——这是一个很好的区分。

问什么问什么,你有什么问题吗?

注意,这不是讨好面试官的邀请。像你在整个面试过程中一样正式礼貌。

这是展示您对如何作为数据科学团队的一员发挥作用的理解的绝佳机会。这也是一个让面试官看到你在面试中没有的优势的机会。例如,你可能擅长代码审查。你可以询问代码审查是如何工作的。你可以突出你最喜欢的方法,比如说走查。您可能想知道使用的开发平台。您可能会问团队中是否有开源贡献者(如果您是开源贡献者,这是再次强调的时候了)。你可以问交货周期。如果团队分布在不同的时区,检查交互是如何工作的。如果适用,分享您在此类团队中的工作经验。

询问你是否可以从高层次了解面试官参与的项目。比起想了解 WFH 政策或数据科学家的日常生活,这些问题要好得多。小心地措辞这些问题,因为你不是面试官!当然,最好只问两个问题,因为接下来的对话不会给你更多的机会。所以要小心选择,并根据面试的环境来选择。最重要的是,做一个耐心的倾听者,这是难忘对话的关键。

注意:不要试图通过夸大事实来催眠面试官。一如既往,要有尊严。记住,没有什么工作比你的尊严更重要。

一些有用的提示

  1. 请澄清。如果你认为问题不明确,问面试官澄清问题。其实这是数据科学家的一个重要技能,即理解需求。你越早问这些问题越好,因为这样可以节省时间,反过来也有助于面试官更好地评估你。
  2. 不要将讨论从 ML 主题移开。假设你没有做好深入某个话题的准备,但是面试官在向你这个方向提问。相信我们,转移话题不是一个好主意,原因有三。
  • 这是不道德的。
  • 如果成功了,你被选中了,你可能会得到一些你在面试中没有准备深入的工作。
  • 大多数情况下是行不通的。面试官和你一样聪明,他们知道你何时试图回避或转移话题。你最终会给人留下不好的印象。

承认你对这个话题不太熟悉是值得尊敬的。如果可能的话,你可能仍然希望根据基本原则进行尝试,并问一些澄清性的问题。别担心,面试官会从你的简历转移到另一个话题。

处理拒绝

万一你这次没有成功,请放心,面试官不是为了好玩才这么做的。公司投入大量的时间和金钱来评估你,如果你没有做到,这仅仅意味着你需要更多的准备。你的申请没有被拒绝,只有你的 T2 申请被拒绝。此外,考虑这样一个事实,除了你之外,许多其他聪明能干的候选人也在面试你申请的职位。只选择几个候选人是这一过程的本质。如果不是你,对你来说也不是世界末日。(坦白地说,不要给任何人那种控制你的权力。)记下不带任何判断地问你的问题。但是先不要分析;让一两天时间让你恢复镇静。然后不带怨恨地思考下一次你如何成为更好的候选人。找出改进的机会,制定一个行动计划,并开始付诸行动,因为生活在过去或无所作为的状态中是没有用的。公司非常希望你在下一个周期再次申请(与人力资源部门核实一下时间)——我们中的许多人在被选中之前已经申请了不止一次。你的人力资源联系人也会努力提供反馈,但是请…请…请…不要开始反驳链。在这种情况下,表现得像一个数据科学家:如果数据(面试结果)与你的假设(你的准备)不一致,那么转向一个更好的假设,也就是说,下次做更好的准备。

好吧,这听起来可能有点哲学。然而,我们认为这是实用的,也是重要的,它能让你成为一个更好的候选人和一个更好的人。培养两种自我意识:内在的和外在的。前者是关于你有多了解自己;后者是关于你如何意识到别人对你的看法。应该清楚这两者是如何结合在一起为你创造一个富有成效的面试经历的。我们推荐你去看看塔莎·欧里奇的《洞察》。

什么是公平面试?

你可能在想,这看起来像是一个冗长的、充满了人造友好的长长的列表,以一种做作的方式对我提出了很多要求(这不是我的意图,也不是人造的友好;抱歉,我说的太多了)。你可能想知道作为公平面试过程的一部分,你会得到什么样的回报。以下是我们为候选人提供的内容。你可能期望从其他知名组织获得类似的经验。

公正的评价

  • 没有问题映射到:*我想出了一个 10 位数的随机数,猜对了。*不,我们不指望你给出我们心中的答案;只要是正确答案,你的答案和我们的一样好。
  • 我们不会把我们的个性投射到你身上。我觉得这很容易,因此你也应该觉得这很容易。
  • 问题一完成就期待答案。在你回答之前,我们听听你的沉默或大声思考是完全没问题的。
  • 没有技巧性问题或误导性问题。
  • 我们两天前才知道的,我们不指望你一辈子都知道。因此,尽管我们最近读了一堆伟大的媒体文章,这些文章真的让我们觉得自己是天才,但我们不会基于它们问任何问题(嗅!)
  • 一两个糟糕的答案会被良好的整体表现所补偿。

与你的经验和知识相关的问题

  • 如果你的简历足够丰富,足以让你被邀请参加面试,那么它肯定也足够丰富,足以让我们把问题限制在简历的内容上。简历中没有的东西足以证明它超出了你的专业范围。这不包括基本的 ML 概念。

礼貌的对待

  • 面试官自我介绍。
  • 面试官会告诉你面试是如何组织的。
  • 面试官会问你是否需要茶点。
  • 面试官关注你。
  • 面试官绝对不会做不道德的事情。例如,对你现在或过去的组织或教育机构进行粗鲁或讽刺的评论;鼓励你打破你的 NDA。
  • 面试官永远不会让你不舒服。他们不会吓唬你或取笑你的答案。
  • 面试官陪你去吃午饭,或者把你交给愿意带你去吃午饭的人。

祝你做好充分的准备。愿原力与你同在!!!

我们希望听到您的反馈。不要忘记与我们分享您的经验,告诉我们这些技巧如何帮助您成功通过数据科学面试。

联系我们:

  • Suresh(沃尔玛实验室首席数据科学家)——【suresh.venkatasubramanian@gmail.com T2
  • Himanshu(沃尔玛实验室数据科学家)——【himanshu6589@gmail.com

免责声明:以上观点是个人观点,不代表我们组织的立场。然而,我们确实希望任何专业组织都有一个公平的选择过程,反映上述各点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值