在 Google Cloud Composer 上用 Selenium 抓取网页(气流)
已经有很多不同的资源可以用来使用 Python 创建 web-scraper,这些资源通常基于众所周知的 Python 包urllib+beautiful soup 4或 *Selenium 的组合。*当您面临抓取大量 javascript 的网页的挑战,或者需要与内容进行一定程度的交互,而这仅仅通过发送 URL 请求是无法实现的,那么 Selenium 很可能是您的首选。我不想在这里详细介绍如何设置您的抓取脚本,以及如何以可靠的方式运行它的最佳实践。我只是想参考一下我找到的这个和这个资源特别有帮助。
在这篇文章中,我们想要解决的问题是:**作为一名数据分析师/数据科学家,我如何建立一个协调的、完全受管理的流程,以最少的开发操作来促进 Selenium scraper?**这种设置的主要用例是在云中运行所有抓取作业的托管和预定解决方案。
我们将使用的工具有:
- Google Cloud Composer 用于安排作业和协调工作流程
- 硒为框架刮网站
- Google Kubernetes 引擎在云中部署 Selenium 远程驱动程序作为容器化的应用程序
在houssinganywhere的时候,我们已经在使用*Google Cloud Composer完成许多不同的任务。Cloud Composer 是一个非常神奇的工具,可以轻松管理、调度和监控工作流,如有向无环图(Dag)。它基于开源框架Apache air flow并使用纯 Python,这使得它非常适合在数据领域工作的每个人。如果您不是来自 DevOps,那么自行部署气流的准入门槛相对较高,这导致一些云提供商提供托管气流部署,谷歌的 Cloud Composer 就是其中之一。*
当部署 Selenium 进行 web 抓取时,我们实际上使用了所谓的 Selenium Webdriver。这个 WebDriver 是一个框架,允许你使用代码( Java,。Net、PHP、Python、Perl、Ruby 。对于大多数用例,你只需下载一个可以直接与 WebDriver 框架交互的浏览器,例如 Mozilla Geckodriver 或 ChromeDriver 。抓取脚本将在您的本地上启动一个浏览器实例*,并执行指定的所有动作。在我们的用例中,事情有点复杂,因为我们希望在不使用任何本地资源的情况下,按照循环的时间表运行脚本。为了能够在云中部署和运行 web 抓取脚本,我们需要使用一个Selenium Remote web driver(又名 Selenium Grid )来代替 Selenium WebDriver。***
来源:https://www . browser stack . com/guide/difference-between-selenium-remote web driver-and-web driver
使用 Selenium Grid 运行远程 web 浏览器实例
Selenium Grid 背后的思想是提供一个框架,允许您通过在一台或多台机器上运行 web 浏览器来运行并行抓取实例。在这种情况下,我们可以利用提供的独立浏览器(请记住,每个可用的浏览器,Firefox、Chrome 和 Opera 都是不同的图像),它们已经被包装为 Docker 图像。
Cloud Composer 在 Google Kubernetes 引擎(GKE)集群上运行 Apache Airflow。此外,它与其他谷歌云产品完全集成。新的 Cloud Composer 环境的创建还附带了一个功能性 UI 和一个云存储桶。所有 Dag、插件、日志和其他所需文件都存储在这个桶中。
在 GKE 上部署并公开远程驱动程序
您可以使用下面的 selenium-firefox.yaml 文件为 Firefox 独立浏览器部署 docker 映像,并通过运行以下命令在您的资源上应用指定的配置:
*kubectl apply -f selenium-firefox.yaml*
配置文件描述了你想要创建的类型的对象,它是元数据以及规格。
我们可以在 Airflow 的管理界面中创建新的连接,然后在我们的插件中访问连接细节。连接细节要么在 yaml 文件中指定,要么可以在 Kubernetes 集群中找到。
气流连接
GCP 上的 Kubernetes 发动机
建立连接后,我们可以访问我们的抓取脚本(气流插件)中的连接,在那里我们连接到远程浏览器。
感谢Massimo Belloni为实现本项目和本文提供的技术咨询和建议。
使用 Python 抓取世界发展指标(WDI)数据库
本文提供了一个 python 函数的演示,该函数使用 xmltree 包从 WDI 数据库中一次抓取和处理多个序列的最新数据。
世界银行维护的世界发展指标数据库是最强大和最多样化的开放存取数据库之一。该数据库包含各种发展指标的数据集,对于任何有兴趣从数量或质量上研究发展问题的人来说,都是一个分析金矿。
就我个人而言,在我作为研究生的研究工作和作为科学家的职业生涯中,我经常依赖这个数据库。在本文中,我提供了一个脚本的演练,该脚本从 WDI 数据库中抓取数据集并将其保存到 dataframe 中。该脚本利用了 python 中的 xmltree 库,可以非常高效地从 WDI 数据库中抓取最新的可用数据。该脚本是我在过去一年中开发的更广泛的 python 数据处理项目的一部分。此处可进入项目。因此,不再浪费时间,让我们直接进入代码。
首先,让我们了解一下 WDI 网站的数据结构。我们将抓取为世界银行托管不同数据集的 WDI API。https://api.worldbank.org/v2/countries/all/indicators/ WDI API 包含以 xml 格式存储在 url- 的数据
任何用户都可以使用特定代码访问单个指示器。例如,代码‘AG。“CON.FERT.ZS”代表按国家和年份划分的施肥数据。因此,要访问这些数据,用户需要指定 URL—【https://api.worldbank.org/v2/countries/all/indicators/AG. CON.FERT.ZS 。
因此,第一步是将来自 WDI 的所有代码存储在一个列表中。为了简化,我在一个 csv 中存储了来自 WDI 的 300 个不同数据集的代码。csv 看起来如下所示,
为了使这变得更加容易,我将脚本构造为一个名为 WDIData 的函数。让我们首先从定义函数并在 python 中导入所需的库开始。如上所述,我们将需要 xml etree 包、numpy 和 pandas。我们还需要请求包,因为这是一个刮刀。
**def** WDIData():
**import** numpy **as** np
**import** requests
**import** pandas **as** pd
**import** xml.etree.cElementTree **as** et
现在让我们将上面提到的 WDICodes 文件读入一个名为 datadict 的对象。让我们创建一个空的数据帧来存储我们的数据集。
datadict = pd.read_csv(**'input\WDI\WDICodes.csv'**, encoding=**"ISO-8859-1"**)df=[]
对于 csv 中的每个代码,我们希望提取国家、代码、年份和值。因此,让我们为 WDI 代码创建一个 for 循环,其中我们为每个变量(国家、代码、年份和值)指定空列表。我们还必须创建一个 url 来从 API 请求数据。最后,我们将使用元素树中的“fromstring”函数从 url 中提取数据。
**for** row **in** datadict[**'WDI Code'**]:
#Specify empty lists for required variables
Countryname = []
SeriesCode = []
Year = []
Value = [] #Create URL for each
url = **'http://api.worldbank.org/v2/countries/all/indicators/'** + str(row) + **'/?format=xml&per_page=20000'** #Get data from url and store to a variable r = requests.get(url, stream=**True**)
root = et.fromstring(r.content)
对于每个代码中的数据,我们希望提取所有年份的所有值。这可以很容易地通过使用 for 循环来实现,这些循环使用 python 中 xml 树包内的‘ITER’函数。我们将为循环写 4 个,一个为系列 id、国家名称,一个为年份,一个为值。如果您跟踪上面提到的 URL,您会注意到数据存储在特定的属性中。
**for** child **in** root.iter(**"{http://www.worldbank.org}indicator"**):
SeriesCode.append(child.attrib[**'id'**])
**for** child **in** root.iter(**"{http://www.worldbank.org}country"**):
Countryname.append(child.text)
**for** child **in** root.iter(**"{http://www.worldbank.org}date"**):
Year.append(child.text)
**for** child **in** root.iter(**"{http://www.worldbank.org}value"**):
Value.append((child.text))
现在让我们为列表中的每个代码创建一个数据帧。这可以在 pandas 中使用 from_dict 函数来完成。我们还将转置数据集。我们将把代码数据存储到一个名为 test_df 的临时变量中。
test_df = pd.DataFrame.from_dict({**'SeriesName'**: SeriesCode,
**'Country'**: Countryname,
**'Year'**: Year,
**'Value'**: Value}, orient=**'index'**)test_df = test_df.transpose()
为了最小化数据集的大小,我们将消除值、国家和系列名称中的空值。
test_df = test_df[pd.notnull(test_df[**'Value'**])]
test_df = test_df[pd.notnull(test_df[**'Country'**])]
test_df = test_df[pd.notnull(test_df[**'Series'**])]
现在,让我们将该代码的数据追加到我们在开始时创建的第一个空数据帧中。让我们使用 pandas 中的 pivot_table 函数从合并的数据集创建一个表。使用 pivot_table 函数的想法是将系列名称存储在列中。
df=pd.concat(df)data = pd.pivot_table(df, index=[**'Country'**, **'Year'**], columns=[**'Series'**], values=[**'Value'**],
aggfunc=[np.sum])
合并数据集存储为数据透视表。让我们将这个表存储为记录。最后,让我们返回数据,完成函数。
#Create the pivot table
data = pd.pivot_table(df, index=[**'Country'**, **'Year'**], columns=[**'Series'**], values=[**'Value'**],
aggfunc=[np.sum])#Convert the dataframe to records from a pivot table
data = pd.DataFrame(data.to_records())#Complete the function
**return** (data)
你有它!最终的数据集如下所示。如上所述,WDI 数据函数可以在 DataUpdate.py 文件中找到。请随时分享您对该职能的任何反馈。提取一个系列大约需要 5 秒钟,您可以用多达 1000 个文件更新 csv,以提取多个系列的最新数据。
如何使用 Python 和 BeautifulSoup 刮电视节目爱普西迪 IMDb 收视率
简单易行——初学者指南。刮掉你最喜欢的电视节目的收视率!
有大量的教程教你如何从 IMDb 那里获得电影收视率,但我没有看到任何关于电视剧集收视率的教程。因此,对于任何想这样做的人,我专门为此创建了本教程。它主要是为了迎合网络抓取的初学者,因为这些步骤被分解了。如果你想要没有故障的代码,你可以在这里找到它。
Alex Olteanu 有一个精彩的 DataQuest 教程,深入解释了如何从 IMDb 收集 2000 多部电影,这是我学习如何收集这些集的参考。
由于他们的教程已经很好地解释了识别 URL 结构和理解单个页面的 HTML 结构的基础知识,我已经将这些部分链接起来,如果你还不熟悉的话,建议你阅读它们,因为我不会在这里解释它们。
在这个教程中,我将而不是赘述他们已经做了什么1;相反,我会做许多类似的步骤,但它们将专门针对剧集评级(对任何电视剧都一样),而不是电影评级。
这个教程最好在我的网站上看,那里代码更清晰。随意查看 这里 。
分解它
首先,你需要导航到你选择的第一季的页面,上面列出了这一季的所有剧集。我将使用的系列是社区。它应该是这样的:
作者图片
获取该页面的 url。它应该是这样的结构:
http://www.imdb.com/title/TT 1439629/剧集?季节=1
粗体部分是节目的 ID,如果您不使用社区,它会有所不同。
首先,我们将通过使用get()
向服务器请求网页的内容,并将服务器的响应存储在变量response
中,并查看前几行。我们可以看到,response
里面是网页的 html 代码。
from requests import geturl = 'https://www.imdb.com/title/tt1439629/episodes?season=1'response = get(url)print(response.text[:250])
使用 BeautifulSoup 解析 HTML 内容
接下来,我们将通过创建一个 BeautifulSoup 对象来解析response.text
,并将该对象分配给html_soup
。html.parser
参数表明我们希望使用 Python 内置的 HTML 解析器进行解析。
*from* bs4 *import* BeautifulSouphtml_soup = BeautifulSoup(response.text, 'html.parser')
从这部分开始,代码将与电影示例不同。
我们将得到的变量是:
- 剧集标题
- 剧集编号
- 广播日期
- IMDb 评级
- 总票数
- 剧集描述
让我们看看我们感兴趣的容器。正如您在下面看到的,我们需要的所有信息都在<div class="info" ...> </div>
中:
作者图片
在黄色的中是代码的标签/部分,我们将调用这些标签/部分来获取我们试图提取的数据,它们在绿色的中。
我们将从页面中抓取<div class="info" ...> </div>
的所有实例;每集都有一个。
episode_containers = html_soup.find_all('div', class_='info')
find_all()
返回了一个ResultSet
对象–episode_containers
–这是一个包含所有 25 个<div class="info" ...> </div>
的列表。
提取我们需要的每个变量
阅读 DataQuest 文章的这部分来理解如何调用标签。
在这里,我们将看到如何从每集的episode_containters
中提取数据。
episode_containters[0]
调用<div class="info" ...> </div>
的第一个实例,即第一集。在第一对变量之后,你将理解调用 html 容器内容的结构。
剧集标题
对于标题,我们需要从<a>
标签中调用title
属性。
episode_containers[0].a['title']
'Pilot'
剧集编号
在content
属性下的<meta>
标签中的剧集编号。
episode_containers[0].meta['content']
'1'
广播日期
Airdate 在带有类airdate
的<div>
标签中,我们可以通过text
属性获取它的内容,然后我们通过strip()
移除空白。
episode_containers[0].find('div', class_='airdate').text.strip()
'17 Sep. 2009'
IMDb 评级
等级在带有类ipl-rating-star__rating
的<div>
标签中,该标签也使用text
属性来获取。
episode_containers[0].find('div', class_='ipl-rating-star__rating').text
‘7.8’
总票数
总票数是一样的,只不过是在不同的类别下。
episode_containers[0].find('span', class_='ipl-rating-star__total-votes').text
'(3,187)'
剧集描述
对于描述,我们做了与播放日期相同的事情,只是改变了类。
episode_containers[0].find('div', class_='item_description').text.strip()
‘An ex-lawyer is forced to return to community college to get a degree. However, he tries to use the skills he learned as a lawyer to get the answers to all his tests and pick up on a sexy woman in his Spanish class.’
最终代码——将所有代码放在一起
现在我们知道如何获得每个变量,我们需要为每一集和每一季进行迭代。这将需要两个for
循环。对于每一季的循环,你必须根据你正在抓取的剧集有多少季来调整range()
。
输出将是一个列表,我们将成为一个熊猫数据框架。代码中的注释解释了每一步。
https://gist . github . com/isabellabenabaye/960 b 255705843485698875 DC 193 e 477 e
制作数据框
import pandas as pd
community_episodes = pd.DataFrame(community_episodes, columns = ['season', 'episode_number', 'title', 'airdate', 'rating', 'total_votes', 'desc'])community_episodes.head()
这是你的新数据框架的头部应该看起来的样子(作者图片)
看起来不错!现在我们只需要稍微清理一下数据。
数据清理
将总票数转换为数字
首先,我们创建一个函数,它使用replace()
从total_votes
中删除’,‘,’(‘和’)'字符串,这样我们就可以将它变成数字。
def remove_str(votes):
for r in ((',',''), ('(',''),(')','')):
votes = votes.replace(*r)
return votes
现在我们应用函数,取出字符串,然后使用astype()
将类型改为 int
community_episodes['total_votes'] = community_episodes.total_votes.apply(remove_str).astype(int)community_episodes.head()
使评分成为数字而不是字符串
community_episodes['rating'] = community_episodes.rating.astype(float)
将广播日期从字符串转换为日期时间
community_episodes['airdate'] = pd.to_datetime(community_episodes.airdate)community_episodes.info()
太好了!现在数据已经整理好,可以进行分析/可视化了。
让我们确保保存它:
community_episodes.to_csv('Community_Episodes_IMDb_Ratings.csv',index=False)
就这样,我希望这是有帮助的!如有任何编辑或问题,请随时评论或 DM me 。
链接:
这个项目的 Github 库
使用 python 为 NLP 抓取 Twitter 数据
了解如何用几行代码从 twitter 创建自己的数据集。
约书亚·阿拉贡在 Unsplash 上拍摄的照片
“我们相信上帝。所有其他人都必须带数据。”— 爱德华·戴明
如果您是从 NLP 这个不可思议的领域开始,那么您会想要接触真实的文本数据,您可以使用这些数据来研究您所学到的概念。Twitter 是这类数据的绝佳来源。在这篇文章中,我将展示一个刮刀,你可以用它来刮你感兴趣的话题的推文,一旦你获得了你的数据集,你就会变得很无聊。
我使用了这个神奇的库,你可以在这里找到。我将介绍如何安装和使用这个库,并建议一些使用并行化使整个过程更快的方法。
装置
可以使用 pip3 通过以下命令安装该库
pip3 install twitter_scraper
创建关键字列表
下一个任务是创建一个你想用来抓取 twitter 的关键词列表。你会在 twitter 上搜索这些关键词,因此重要的是你要列出一个你感兴趣的关键词的综合列表。
抓取一个关键词的推文
在我们运行程序提取所有关键字之前,我们将用一个关键字运行程序,并打印出我们可以从对象中提取的字段。在下面的代码中,我展示了如何迭代返回的对象并打印出您想要提取的字段。您可以看到,我们提取了以下字段
- 推文 ID
- 是转发还是不转发
- 推文时间
- 推文的文本
- 对推文的回复
- 转发总数
- 喜欢推特
- 推文中的条目
对所有关键字按顺序运行代码
既然我们已经决定了要从我们的对象中存储什么样的数据,我们将依次运行我们的程序来获取我们感兴趣的主题的 tweets。我们将使用我们熟悉的 for 循环来逐个检查每个关键字并存储成功的结果。
并行运行代码
从文档中,
多重处理是一个支持使用类似于线程模块的 API 生成进程的包。多处理包提供了本地和远程并发,通过使用子进程而不是线程有效地避开了全局解释器锁。因此,多处理模块允许程序员在一台给定的机器上充分利用多个处理器。
首先,我们将实现一个函数来抓取数据。
接下来,我们将创建子流程来并行运行我们的代码。
如您所见,我们将处理时间减少到了顺序执行的 1/4。您可以将此方法用于类似的任务,并使您的 python 代码更快。
使用 Python 抓取天气数据以在电子邮件中获得雨伞提醒
使用 Python 获得自动“雨伞提醒”电子邮件
Python 在脚本编写、自动化、web 抓取、数据分析等方面创造了奇迹,这样的例子不胜枚举。在本文中,我混合了 web 脚本和自动化——因此,如果城市的天气是阴雨天,Python 脚本将发送一封“雨伞提醒”电子邮件。
库导入
from bs4 import BeautifulSoup
import smtplib
import requests as rq
“美丽的汤”用于从网站、Html 和 XML 页面中提取数据。“Requests”库用于发送 HTTP 请求,而“smtplib”用于向任何机器发送电子邮件。
获取天气数据
url = rq.get('https://forecast.weather.gov/MapClick.php?lat=40.71455000000003&lon=-74.00713999999994')
soup = BeautifulSoup(url.text, 'html.parser')
weather = (soup.select('#current_conditions-summary p')[0]).text
temperature = (soup.select('#current_conditions-summary p')[1]).text
我使用过国家气象局网站搜集纽约的天气数据。requests.get()函数接受要下载的 URL 字符串。BeautifulSoup()函数需要用一个包含它将解析的 HTML 的字符串来调用。
现在,BeautifulSoup 对象已经创建,您可以使用一个 “select” 方法从 web 页面中检索天气和温度的 web 元素。
构建 smtplib 对象
server = smtplib.SMTP('smtp.gmail.com', 587)
print(server.ehlo())
print(server.starttls())
server.login('Enter your email', 'Password')
我创建了一个带有参数“smtp.gmail.com”和 587 的 SMTP 对象。创建 SMTP 对象后,您可以使用您的电子邮件地址和密码登录。
注意:smtplib 中的参数可能会更改。SMTP()取决于域的 SMTP 服务器。如果您有 Gmail 帐户,您必须为您的电子邮件地址生成特定于应用程序的密码。否则,当您的程序试图登录时,您将得到一个特定于应用程序的需要密码的错误消息。
您可以使用本指南创建您的 Gmail 应用程序专用密码
发送电子邮件
if weather == 'Raining' or weather == 'Overcast' :
subject = "Umbrella Reminder"
body = f"Take an umbrella with you. Weather condition for today is {weather} and temperature is {temperature} in New York."
msg = f"Subject:{subject}\n\n{body}\n\nRegards,\nVishal".encode('utf-8')
print(msg)
server.sendmail('Sender's Email', 'Recipient Email', msg)
print("Email Sent!")
server.quit()
else :
print("There is going to be {} and temperature is {} in New York".format(weather, temperature))
如果来自 web 元素的天气结果是下雨或阴天,发件人将向收件人发送一封带有“雨伞提醒”消息的电子邮件。
这就是你如何使用 Python 从一小步开始并自动化你生活中的小事情。开始您的脚本之旅。
请联系我,在维沙尔·夏尔马寻求反馈或讨论!
Scrapy:这就是如何轻松成功登录
李晟从没开过
揭秘用 Scrapy 登录的过程。
一旦你理解了 Scrapy 的基本知识,第一个复杂的问题就是必须处理登录。要做到这一点,了解登录是如何工作的以及如何在浏览器中观察这个过程是很有用的。我们将在这篇文章中讨论这一点以及 scrapy 如何处理登录过程。
在本文中,您将了解到
- 如何将 FormRequest 类用于简单的登录过程
- 如何使用
fromresponse
方法进行更复杂的登录过程 - 如何实现基本的 XPATH 选择器
- 了解会话和令牌浏览器身份验证之间的区别
- CSRF 是什么,为什么知道它很重要
- 如何处理 Scrapy 中的令牌认证
简单的登录程序
当你输入数据到网站表单域时,这些数据被打包。浏览器将在标题中加入这一内容来执行 POST 请求。登录时可能会有许多 POST 和 redirect 请求。
要在 Scrapy 中做最简单的登录程序,我们可以使用 Scrapy 的 FormRequest 类。实际上,最好使用 FormRequests 方法之一来处理表单数据,但稍后会详细介绍!
让我们先看看它是如何工作的,然后在此基础上进行构建。为了在我们的 scrapy spider 中使用它,我们必须首先导入它。
from scrapy.http import FormRequest
现在,我们不再在蜘蛛的开头使用start_url
,而是使用start_requests()
方法。这允许我们使用与表单填充相关的方法。
让我们看看 scrapy.spider 代码的底层,看看它是如何工作的。请记住,这是我们在启动蜘蛛时经常提到的地方。
当我们从那个类中调用start_requests()
方法时,这就是隐藏的内容。
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url)def make_requests_from_url(self, url):
return Request(url, dont_filter=True)
在这里,我们循环浏览start_urls
的每个网址。对于每个 url,我们使用scrapy.requests()
方法,并传递一个 URL 和一个名为 dont_filter 的关键字。现在dont_filter=True
意味着不过滤重复的请求。
通过使用 FormRequest 子类,我们扩展了scrapy.http.Request
类。FormRequest 为我们提供了从响应中预填充表单字段的功能。我们扩展了scrapy.http.Request
,可以访问它所有的关键字参数。
让我们看看这是什么样子。
from scrapy.http import FormRequests
import scrapydef start_requests(self):
return [
FormRequest("INSERT URL", formdata={"user":"user",
"pass":"pass"}, callback=self.parse)]def parse(self,response):
pass
笔记
1.默认的parse()
函数处理脚本的响应和规则,以获取您想要的数据。
2。对于函数start_request
,我们使用 FormRequest 类。我们向它提供一个 url 和关键字参数 formdata 以及我们的用户名和密码。
3.Scrapy 为我们处理饼干,而不需要我们在start_request
中具体说明。
4.我们使用回调关键字参数将蜘蛛指向解析函数。
莫哈末·阿拉姆来自未喷涂
隐式数据
有些网站会要求您传递表单数据,这些数据乍一看似乎并不重要。登录,但有必要通过 cookies 认证网站。有时页面有一个隐藏的字段需要通过。获得该值的唯一方法是登录。
为了解决这个问题,我们需要做两个请求,一个请求传递一些数据并捕获隐藏的字段数据。第二次请求登录。
这次我们将使用一个start_request
函数,但是传递第一个请求。我们将处理回调以将信息传递给第二个函数。
对于第二个请求,我们将使用FormRequest.from_response()
方法。这个方法用我们指定的字段中填充的数据来模拟点击。返回一个新的 FormRequest 对象。这个对象的默认设置是模拟一个点击,这个点击有一个类型为<input_type=submit>
的可点击项目。
我们可以在from_response()
方法中指定不同的方式。参考 scrapy 文档是明智的!例如,如果你不想让 scrapy 点击进入,你可以使用关键字dont_click=True
。来自from_response()
方法的所有参数都被传递给 FormRequest。
def start_requests():
return [
Request("URL", callback = self.parse_item)
]def parse_item(self,response):
return FormRequest.from_response(response, formdata=
{'user':'user', 'pass':'pass'})
在这里,我们从start_requests()
获取所需的头,然后将其传递给parse_item()
使用FormRequest.from_response()
我们传递带有适当标题的表单数据。
特雷布雷·巴黑斯
浏览器认证基础知识
HTTP 是一种无状态协议。这意味着从请求到响应,没有消息状态的记录。如果您作为一个请求登录,这将在另一个请求中被忘记。讨厌还是什么!
第一个解决方案是创建一个会话。当我们进行 POST 请求时,会在服务器数据库中创建一个会话。一个 cookie,它是一个字典,附加到带有会话 ID 的响应中。cookie 被返回给浏览器。
当我们用这个 cookie 尝试另一个请求时,服务器会查找它并检查会话 ID。获取配置文件数据匹配,并将响应发送回浏览器。
Cookie 身份验证是有状态的。认证记录存储在客户端和服务器端。
现在我们有了单页面应用程序,前端和后端的分离要复杂得多!当与另一个服务器联系时,我们从一个服务器获得的会话 cookie 将不起作用。这就是基于令牌的身份验证发挥作用的地方。
抢王从未破土而出
基于令牌的认证
令牌是另一种浏览器身份验证方法。现在使用的标准令牌是 Json web 令牌(JWT)。
基于令牌的认证的优点是它是无状态的。服务器不记录哪些用户登录。每个请求都有一个令牌,服务器用它来检查真实性。令牌通常在授权报头载体(JWT)中发送,但也可以在 POST 请求的主体中发送。
JWT 由三部分组成:报头、有效载荷和签名
###HEADER###
{
"alg": "HS256",
"typ": "JWT"
}###PAYLOAD###{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}###SIGNATURE###{
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret)
现在 jwt 使用编码算法进行签名。例如 HMACSHA256,看起来像这样。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkbyBLdWtpYyIsImFkbWluIjp0cnVlLCJpYXQiOjE0
这可以通过像这样的网站解码,知道数据没有加密是很重要的。
用户输入登录详细信息,令牌被附加。然后,服务器验证登录是否正确,然后发送的令牌被签名。这个令牌存储在客户端,但实际上可以存储为会话或 cookie。记住 cookie 是键和值的字典。
对服务器的请求包括此令牌以进行身份验证。服务器对这个令牌进行解码,如果令牌有效,请求就会被发回。当用户注销时,令牌在客户端被销毁。不与服务器交互。这为我们提供了一些保障。
孙富从无到有
CSRF 是关于什么的?
当谈到基于令牌的身份验证时,您会用到这个术语。所以了解它们是很有用的。这就是为什么基于令牌的认证变得流行的核心。
CSRF 代表跨站点请求伪造,是一个网络安全漏洞。它允许攻击者让用户执行他们不想执行的操作。例如通过改变账户的电子邮件地址。
CSRF 袭击的发生需要三个条件。首先是一个相关的动作,它是应用程序中有理由改变的东西。基于 cookie 的会话处理来进行身份验证,并且没有不可预测的请求参数。
有了这些,攻击者就可以创建一个带有表单的网页来更改电子邮件地址。用户输入数据,表单在对原始网站的请求中使用用户会话 cookie。而是使用新的电子邮件地址。
为了应对这种攻击,我们可以使用令牌来检查用户,而不是基于会话的 cookies。
克林特·帕特森
使用 Scrapy 处理基于令牌的认证
为了确定是否有必要使用令牌,我们必须使用 chrome/firefox 开发工具。为此我们将刮掉 quotes.toscrape.com。我们模拟登录过程,看看发送了哪些报头。为此,我们在登录前滚动到网络选项卡,然后模拟一个登录过程。所有请求将出现在下面。
Chrome 开发工具。红色方框显示登录过程
选择左侧的登录名,我们可以看到下面的请求标题。
登录过程的请求标头。突出显示 CSRF 令牌
正如您在表单数据中看到的,csrf_token
是存在的。
请记住,当我们注销时,此令牌会被销毁。我们有必要在将它作为 POST 请求发送之前获取它。要做到这一点,我们必须看看 HTML。表单的 html 标签如下所示。
我们可以在这里看到表单字段< 【
XPATH Basics
Now to do any scrapping it’s useful to go to the shell to see if the xpath selectors will work in your spider. So here we fetch the url and test out the xpath we need.
fetch("url")
response.xpath('//*[@name='csrf_token']/@value').extract_first()
Output
u'xUdwZpPyvkEJYTHOogcAaNFVzuhKtLejqBfbSXMQRCmWDrsinGlI'
Lets break down the xpath selector.
First xpath hands html documents as a tree and seperates the document out into nodes. The root node is parent to the document element 【 . Element nodes represent html tags. Attribute nodes represent any attribute from an element node. Text nodes represent any text in the element nodes.
First 【 is a 【 which means it selects current node or any below it. 【 means to select all nodes without comments or text nodes.
We use 【 to specify the attribute 【 and 【 after this to specify it’s value. 【 will grab the first value it finds.
So we have our xpath selector to get the 【 , we can then use this to pass onto the FormRequest
Now that we have that understanding we can look at the code for using it.
import scrapy
from scrapy.http import FormRequestsclass LoginSpider(Scrapy.spider):
name = 'login'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['[http://quotes.toscrape.com/login](http://quotes.toscrape.com/login)']def parse(self,response):
csrf_token = response.xpath('//*[@name='csrf_token']
/@value').extract_first()') yield FormRequests.from_response(response, formdata={'csrf_token': csrf_token, 'user':'user', 'pass','pass'}, callback=self.parse_after_login)def parse_after_login(self,response):
pass
Notes
1。我们像以前一样导入 scrapy 和表单请求
2.我们填充变量name
、allowed_domains
和start_urls
。
3.我们将csrf_token
分配给登录页面中令牌的 xpath 选择器值。
4.使用 FormRequests 子类,我们指定响应,并输入我们想要的表单数据。在这种情况下,我们指定了csrf_token
以及用户名和密码。
5。登录成功后,使用回调将蜘蛛指向该函数。
唷!很难通过。希望让 Scrapy 的登录程序更易管理。下次见!
关于作者
我是一名医学博士,对教学、python、技术和医疗保健有浓厚的兴趣。我在英国,我教在线临床教育以及运行 www.coding-medics.com网站。
您可以通过 asmith53@ed.ac.uk 或 twitter 这里联系我,欢迎所有意见和建议!如果你想谈论任何项目或合作,这将是伟大的。
更多技术/编码相关内容,请点击这里注册我的简讯。
相关文章
了解如何使用 Python 下载 web 抓取项目中的文件
towardsdatascience.com](/how-to-download-files-using-python-ffbca63beb5c) [## 你应该知道的 5 个 Python 技巧
如何轻松增强 python 的基础知识
medium.com](https://medium.com/swlh/5-python-tricks-you-should-know-d4a8b32e04db)
没有魔法的 Scrum
意见
当一个团队决定采用敏捷,尤其是 Scrum 时,他们通常会有这样的态度——一旦他们完成了所有神奇的仪式,一切都会神奇地运转起来。那就不会发生。
由 Muhammad Haikal Sjukri 在 Unsplash 上拍摄的照片
诚然,Scrum 有时类似于一种崇拜。
甚至会议也被方便地命名为“仪式”,就好像在 Scrum 中没有会议,而是有神奇的仪式。无数的 Scrum 教练和顾问看起来和说起来都像是信徒。
当一个团队决定采用敏捷,尤其是 Scrum 时,他们经常会有这样的态度——一旦他们完成了所有神奇的仪式,一切都会神奇地工作。所有的问题都会消失。如果他们一直重复这些咒语,他们的表现也会一飞冲天。
那就不会发生。人们变得幻灭和失望。他们在互联网上写了无数的文章,认为 Scrum 是一个骗局,根本不起作用。
好吧,Scrum 不是魔术。尽管有可怕的言辞和迷人的教练,Scrum 本身没有力量。它本身不能解决任何问题。
那又怎样?
我不是 Scrum 专家。我是一名软件工程师。你不必听我的。但是当我开始对敏捷感兴趣的时候,我惊讶于有多少人只是模仿 Scrum,而没有任何特定的需求或目标。
Scrum 有一个完整的宇宙:数百万的文章、课程和书籍,数以千计的演讲、认证和培训师。说了这么多,行动却这么少。
框架
让我们像工程师一样交谈。Scrum 被称为“框架”是有原因的。像许多框架一样,它不会给你解决问题的代码,它只给你一个结构。然后,作为 Scrum 大师、团队领导或工程师,你的任务就是填补空白。
你是如何编写代码的?我有个主意。对许多人来说,这听起来可能很奇怪,但这就是:不要光说不练,要去做。你如何称呼这个或那个,或者你的燃尽图有多漂亮,真的无关紧要。重要的是你的输入是否给了你正确的输出。
Sprint 作为一项功能
输入:任务。
输出:一次迭代。
Sprint 是敏捷的基石。最重要的功能。这不仅仅是一句话,或者一个仪式,或者仅仅两个星期的时间。许多团队只是在开始时将一些任务推给别人,然后,在两周结束时,他们将未完成的任务交给下一个“冲刺”。这不是冲刺,只是模仿。
每一次冲刺都应该从一张白纸开始。好的函数是幂等的。
此外,Sprint 不是经理的衡量工具。短跑只能用结果来衡量,用产出来衡量。
Sprint 是一个函数,一个应该产生迭代的动作——一些以前不存在的东西。一些你可以展示的东西。
如果你的工作不是迭代的——也许你只解决了未分类的错误,或者你正在建造一个卫星——那么你不应该试图将你的过程分成冲刺。
作为功能的“仪式”
它们可能被称为仪式,但不要混淆——最终,它们只是会议,如果有效地运行,可以发挥作用并产生结果。
如果会议没有产出,那么它应该以不同的方式运行,或者从人们的日历中删除。如果开会是一种仪式,那只是浪费每个人的时间。
时间的重要性
通常,工程师不喜欢开太多的会。几乎没有人喜欢冗长的会议。如果会议没有一个明确的目标,它就会变成一种折磨。如果一次会议没有产生任何结果,那就感觉毫无意义。如果毫无意义的会议定期重复,感觉就像一种仪式。
如果你的 Scrum 是由仪式组成的,那它就是模仿——当人们开始抱怨它不起作用时,任何人都不应该感到惊讶。
Artem Maltsev 在 Unsplash 上拍摄的照片
站立
输入:团队成员报告
输出:一天的计划
有数百万字是关于单口相声的。他们通常关注事情的“人”的方面——互相汇报,不要分心,在时间范围内。所有这些观点都是正确的。但是我们为什么要这么做呢?所以“每个人都知道每个人在做什么”。但是为什么呢?
他们应该知道,以便计划他们的一天。
积压优化
输入:要求
输出:明确定义的任务,全部记下
Backlog 精化获取原始需求,并将它们转换成明确定义的任务。
明确定义——这很重要。会后应该不需要提问了。请记住,这些任务将在整个流程中继续进行,并在 Sprint 规划期间反馈给您。
如果细化是无效的,它将使计划无效。
此外,规划扑克不只是一个无聊的游戏。“如何”并不重要。重要的是,你是否能迅速集体决定这项任务有多难。
评估定义了冲刺,冲刺定义了你要做什么和有多努力。如果扑克真的有所不同,那么每个人都会感兴趣。
冲刺规划
输入:明确定义的优先任务
输出:冲刺计划
Sprint Planning 将 Backlog 精化产生的东西,划分出来并转化为一个 Sprint。
关于冲刺计划的主要问题是它应该是严肃的。当你计划冲刺和“提交”时,它不应该只是一个象征性的姿态——你决定你要做什么。这将是你必须向同事展示的一个增量。
回顾的
输入:团队成员对冲刺的想法
产出:要实施的行动清单
我曾经在一个团队工作过,在那里,复古是每个人都互相喊叫和抱怨的时代。在另一个团队中,Retro 总是很安静,因为对每个人来说一切都很好。
在许多团队中,复古只是经理们的专利——工程师们只是等着回去工作。
回顾会应该收集每个人对工作过程的意见,并列出行动清单。然后就应该采取这些行动。
如果回顾没有产生任何改进,那就感觉是浪费时间。
演示
Sprint Demo 的存在是为了确保你已经产生了一个迭代。如果你没有什么可以展示的,也许那不是一次冲刺?
圆圈
把 Scrum 想象成 Lambda 函数的流程图。由于这个过程是迭代的,它们不可避免地形成一个循环。
…改进->积压优化->积压->冲刺计划->冲刺->站立->演示->回顾->改进…
这是一个数据管道,真的。
正确的框架
Scrum 不会为团队创建一个过程。作为框架,只能对结构有帮助。这是团队写的“代码”。
为了让它起作用,我们不应该关心是否酷或者做对事情。我们应该关心结果。
尽管有很多关于敏捷是多么酷和有趣的谈论,但是敏捷实际上是关于纪律的。它是作为一个框架创建的,用于在不断变化的需求的混乱中导航。
Scrum 不一定要有趣。嗯,也不一定要无聊。最好是看不见的。最重要的是,它不应该分散人们的工作注意力,也不应该增加更多的混乱。
是的,有时候 Scrum 并不是团队的正确选择。
海洋穿越:从水下图像中去除水
一种新的工具,有可能彻底改变水下图像的计算机视觉。
信用:Derya Akkaynak
先说清楚一点:这不是 Photoshop 教程!当然,相当多的摄影师知道提高水下照片质量的技巧,这对人眼来说已经足够了。但是对于那些开发机器学习算法来分类和识别图像中的对象的人来说,这样的技术是完全不够的。我挑战任何阅读这篇文章的人,让他们想到机器学习和计算机视觉在水下成像和视频中的单一应用。这比你想象的要难。根据德雅·阿克凯纳克和程昕婷·特雷比兹的说法,原因是“目前不存在鲁棒的算法”来可靠地去除水生图像中持续存在的噪声…
…直到现在。
信用:Derya Akkaynak
有什么问题?
水下照片相当于在空气中拍摄的照片,但被厚厚的彩色雾覆盖,受白点和强度随距离变化的光源影响目前用于改进水下摄影的模型扩展了定期观测大气条件下的模型。然而,这些模型没有考虑水对光的极端影响。你知道吗光在水中的速度大约比在空气中的速度慢 46200 英里/秒?
信用:Derya Akkaynak
变化的波长
这些速度的变化影响了我们看到的光的波长和颜色。让事情变得更加复杂的是,物体发出的光仅仅是折射阳光的反射。Akkaynak 和 Treibitz 试图通过认识到观察到的颜色波长是速度、距离和原始色调的函数来克服这一挑战。光在水中的速度是一个常数,光源色调也是如此。因此,如果我们知道一个物体的精确色调和与它的距离,我们就可以逆向工程出观察到的颜色的公式。总之,理论上是这样的。不幸的是,还有其他挑战需要考虑。
信用:Derya Akkaynak
后向散射
如果你曾经在浓雾中开车,你可能会倾向于打开你的远光灯,但你会很快意识到增加的光线更多的是致盲而不是有益的。这是因为空气中的水颗粒将更多的光反射回给你。在水下拍摄图像时也会发生同样的现象,因此会影响拍摄照片的颜色精度。随着与焦点物体的距离增加,反向散射强度增加。
有一种用于处理反向散射的估计器,称为暗通道先验(DCP),它是为在朦胧条件下拍摄图像而开发的。DCP 背后的思想基于以下内容:“室外无霾图像中的大多数局部斑块包含一些像素,这些像素在至少一个颜色通道中的强度非常低。将该先验与薄雾成像模型一起使用,我们可以直接估计薄雾的厚度并恢复高质量的无薄雾图像。
信用:Derya Akkaynak
克服障碍
Akkaynak 和 Treibitz 声称 DCP 的修改版本考虑了水的光吸收和反向散射。然而,这样做的公式需要已知的距离作为输入变量,并且期望用户对每张照片进行这样的测量是不切实际的。幸运的是,摄影测量领域的专家已经开发出处理这个问题的技术。例如,如果成像物体的尺寸是已知的,那么距离可以通过缩放得到。
多镜头相机,如现代智能手机上的相机,为使用视频测量提供了更优雅的解决方案。通过从两个或多个角度拍摄同一幅图像,并使用相机之间的已知距离,我们可以测量深度。(还记得你高中几何的 ASA 三角形吗?)你的手机已经能够实时进行这些测量和计算。最终的结果是一个有潜力为水下摄影和摄像带来计算机视觉革命的产品。
信用:Derya Akkaynak
关于 Sea-thru 的更多信息,包括该算法的未来可用性,请前往 https://www.deryaakkaynak.com/sea-thru。
刚刚发布的 Seaborn 0.11 具有很棒的新特性
Seaborn 刚刚好起来。
Seaborn 是一个基于 Matplotlib 构建的高级 Python 数据可视化库。它可以方便地创建许多不同的信息统计可视化。
照片由 Giovany Pineda Gallego 在 Unsplash 拍摄
Seaborn 的新版本(0.11.0)刚刚发布,在现有版本的基础上增加了新的特性和增强功能。在本帖中,我们将通过可视化示例介绍大部分变化。
引入了三个新功能,分别是显示图、历史图和 **ecdfplot。**这三个函数可用于可视化单变量或双变量数据分布。
注意:为了使用新功能,您需要更新到新版本,这可以通过pip install seaborn==0.11.0
完成。
让我们从距离图开始。可以认为是另外两个的父类。distplot 使用种类参数,提供对 histplot、ecdfplot 和 kdeplot 的访问。因此,它是不同类型分布图的图形级界面。
这里有一个例子。
sns.displot(data=diabetes, x='Glucose', kind='hist', height=6, aspect=1.2)
我们绘制了一个直方图,显示了葡萄糖变量的单变量分布。
下面的代码将创建一个直方图,显示葡萄糖和血压变量的二元分布。
sns.displot(data=diabetes, x='Glucose', y='BloodPressure',
kind='hist', height=6, aspect=1.2)
区域的暗度随着该区域内数据点数量的增加而增加。
distplot 是图形级别的函数,而 histplot 是轴级别的函数。可以使用 histplot 函数的以下语法创建相同的图。
sns.histplot(data=diabetes, x='Glucose', y='BloodPressure')
如果你想了解更多关于 Matplotlib 的图形和轴的概念,这里有一个关于 Matplotlib 结构的详细帖子。
由于 displot 在一个面网格上绘制图形,我们可以在同一个图形上绘制多个图形。
sns.displot(data=churn, x='CreditScore', kind='hist',
col='Geography', hue='Exited')
ecdfplot(经验累积分布函数)提供低于数据集中每个唯一值的观察值的比例或计数。除了对变量分布的概述之外,与直方图相比,我们可以更清楚地了解数据中的每个观察值,因为没有宁滨(即分组)。
sns.displot(data=churn, x='Age', kind='ecdf', col='Geography', hue='Exited')
ecdfplot 只能绘制单变量分布。
新版本带有一个贬值的功能,即 distplot 。这是意料之中的,因为新函数(displot 和 histplot)似乎是 displot 的更好的替代物。
目前使用的一些功能也有所改进或增强。
联合图提供两个变量的单变量和双变量图。色调语义被添加到 jointplot,这增加了额外的信息能力。
sns.jointplot(data=diabetes, x='Insulin', y='BMI', hue='Outcome',
height=7)
jointplot 的另一个新增功能是 kind 参数的“hist”选项。kind 参数的缺省值是“散点”,但如果我们将其更改为“历史”,则会创建关节轴上的双变量直方图和边缘轴上的单变量直方图。
sns.jointplot(data=diabetes, x='Insulin', y='BMI', hue='Outcome',
kind='hist', height=7)
关于 API 的一些亮点
Set
功能已被重命名为set_theme
。它调整可视化中使用的主题的几个属性。axlabel
功能不再可用。建议用ax.set(xlabel=, ylabel=)
代替。
我们已经介绍了一些主要的变化,但是如果你想看到所有的变化,这里有相关的 seaborn 文档。
如果你想自己练习,你可以访问 Kaggle 上的数据集。
感谢您的阅读。如果您有任何反馈,请告诉我。
Python 中数据可视化的 Seaborn 基础
可视化支柱的代码片段:分布、关系、比较、组合
照片由阿尔菲亚诺·苏蒂安托在 Unsplash 拍摄
之前说过,数据可视化的目的是沟通数据中隐藏的信息。可视化从根本上服务于数据科学中的 4 个目的,即理解:
- 特征的分布
- 两个或多个变量之间的关系
- 变量间的比较
- 数据的组成
你可以用各种方法识别和交流隐藏的信息,而且有工具可以做到这一点。无论您选择哪种编程语言,每种语言都有库来有效地处理数据可视化。
如果你使用 Python,你有几个选择,但是我发现seaborn
是最好的。不是因为seaborn
可以做一些其他库做不到的事情,而是因为它的简单和直观的代码结构。
因此,本文的目的是用代码片段演示 Seaborn 的一些用例。我的学习/教学哲学是从一个问题开始,然后找到可以解决它的工具。因此,我不打算展示太阳底下的一切,而是给出一个关于 Seaborn 如何工作以及如何解决我刚才提到的 4 类问题的直觉。
让我们开始吧。
内置数据集
seaborn
自带 17 个内置数据集。这意味着你不必花费大量的时间去寻找合适的数据集并清理它来使 Seaborn 就绪;相反,您将关注 Seaborn 可视化技术的核心特性来解决问题。
首先,让我们看看数据集。
# get names of the builtin dataset
sns.get_dataset_names()
内置数据集 Seaborn 数据集
如果你在数据科学领域呆过一段时间,你会仅仅通过它们的名字就认出它们中的许多(泰坦尼克?艾瑞斯。).否则,只需加载数据集并调用head()
函数来查看数据。
df = sns.load_dataset("iris")
df.head()
加载和查看 Seaborn 内置数据集
我将使用其中的一些数据集来展示不同种类的可视化技术,所以我将它们全部载入。如果你知道每个数据集是关于什么的,那很好,否则就不要麻烦了。只需知道每个数据集包含不同种类的变量;其中一些是离散变量(例如 10、20、30 英里/加仑),一些是连续变量(例如 5.2、3.6 瓣长),一些是分类变量(例如一周中的几天)。现在,知道不同的变量类型以及何时使用它们应该是可行的。
# loading additional datasets
tips = sns.load_dataset("tips")
mpg = sns.load_dataset("mpg")
fmri = sns.load_dataset("fmri")
car_crashes = sns.load_dataset("car_crashes")
分配
我们的第一类问题是理解数据的分布。我所说的分布指的是几件事:频率分布、概率分布或只是数据相对于中心值的分布(平均值、中间值等)。
直方图
假设我们有一个包含 1000 辆汽车及其燃油效率的数据集,单位为每加仑英里数(mpg)。直方图会告诉我们每一 mpg 类别中汽车数量的频率分布。
# Bar histogram
sns.distplot(mpg["mpg"], kde = False)
柱状图
内核密度图
统计学中使用的这种频率分布有一个更复杂的版本,叫做概率分布。你也可以画出来。
# Line histogram/kernel density plot
sns.kdeplot(mpg["mpg"], shade = True)
核密度图
箱线图
还有另一种类型的分布——更广为人知的是 spread——它显示了一个变量相对于其中心趋势是如何分散/扩散的。
箱线图以展示变量的离差而闻名,变量的值如中值、最小值、最大值和异常值都在同一个图中。
# boxplot
sns.boxplot(y = mpg["mpg"])
箱线图
关系
孩子随着年龄的增长而长高——这是两个变量之间的关系:身高和年龄。
房子越大,价格越高——这是变量之间的另一种关系:楼层大小和价格。
关系图显示变量之间的关系。
散点图
可视化可以检查和绘制变量之间的关联。对于连续变量,您可以非常直观地看到散点图中的任何东西,并直观地确定它们之间的关系。
# scatterplot
sns.scatterplot(x = "sepal_length", y = "petal_length", data = iris)
从下面的散点图可以看出,鸢尾花的花瓣长度与萼片长度呈正相关。
散点图
如果你很难直观地找出这种关系,还有一个选择——画一条趋势线。
# trend line
sns.lmplot(x = "sepal_length", y = "petal_length", data = iris, scatter = False)
趋势线/线性模型
下面的趋势线应用于散点图中与前一条相同的数据。您想使用哪一个取决于您的需求,但也有权衡。在散点图中,你可以看到二维空间中每个数据点的可变性,但不容易跟踪趋势。
在基于线性模型创建的趋势图中,您可以确定数据的总体趋势以及置信区间,但会丢失单个数据点(当然,您也可以在趋势图中显示点)。
使用线性模型的趋势线
比较
比较分析是许多商业决策的关键。哪种产品卖得更好——产品 A 还是产品 B?这两种药物的疗效有显著差异吗?在整个决策过程中,这类问题无时无刻不在被问到。
条形图
条形图是一些最简单、最古老但有效的可视化技术,用于交流比较分析。下图显示了顾客在一周的不同日子里支付的餐厅账单总额。这张图片比较了工作日的账单总额。
# bar chart
sns.barplot(x = "day", y = "total_bill", data = tips)
条形图
折线图
折线图是比较数据的另一种方式。它通常用于时间序列分析,比较两组观察值的时间演变。
# line chart/time series
sns.lineplot(x="timepoint", y="signal", hue="event", data=fmri)
折线图
作文
数据可视化的最后一个支柱是组合。成分图的目的是用绝对和相对术语(如百分比)显示一个或多个变量的成分。
在 Seaborn 中创建复合图有点复杂,它不像其他的那样是一行代码。所以请原谅我在下面举两个例子。
堆积条形图
堆积图在单个条形图中显示不同类别的变量的构成。例如,一个显示阿拉巴马州车祸总数的条形图,该条形图还按涉及酒精的车祸进行了分类(见下图)。
# Initialize the matplotlib figure
f, ax = plt.subplots(figsize=(10, 15))# Plot the total crashes
sns.set_color_codes("pastel")
sns.barplot(x="total", y="abbrev", data=crashes,label="Total", color="b")# Plot the crashes where alcohol was involved
sns.set_color_codes("muted")
sns.barplot(x="alcohol", y="abbrev", data=crashes, label="Alcohol-involved", color="b")# Add a legend and informative axis label
ax.legend(ncol=2, loc="upper right", frameon=True)
堆积条形图
堆叠区域
堆积面积图显示同一绘图区中不同观察组的值的变化。在这种情况下,这些值“堆叠”在彼此的顶部。
Seaborn 的内置数据集和代码对于显示堆叠面积图并不太有用,因此我们将创建一个简单的数据集,并使用matplotlib
库显示一个“Seaborn 风格”的图表。
# stacked area
import numpy as np
import matplotlib.pytplot as plt# Data
x = range(1,6)
y = [[2,4,5,6,8], [2,3,6,7,10], [2,6,7,8,5]]# Plot
plt.stackplot(x,y, labels=['X','Y','Z'])
plt.legend(loc='upper left')
plt.show()
堆积面积图
离别赠言
本文的目的是使用 Python 库seaborn
演示一些可视化技术。我想涵盖两件事——介绍您在数据科学中通常会遇到的不同类型的可视化,第二,一些使用 Seaborn 内置数据集的代码片段。希望这有用。想看更多这样的文章就大声喊出来。
Seaborn FacetGrid:进一步发展支线剧情
循序渐进的教程
马库斯·斯皮斯克在 Unsplash 上的照片
数据可视化在数据分析中至关重要。“一张图片胜过千言万语”这句名言同样适用于数据可视化领域。在这篇文章中,我将解释一个结构良好、信息量很大的支线剧情集:、FacetGrid 。
FacetGrid 基本上就是支线剧情的网格。Matplotlib 支持创建具有多个轴的图形,因此允许在一个图形中有子图形。FacetGrid 在 matplotlib 的子情节结构上放了什么:
- 使过程更容易、更流畅(用更少的代码)
- 将数据集的结构转移到子情节
使用 FacetGrids 可以很容易地发现变量的分布或变量之间的关系。它们最多可以有三个维度:行、列和色调。通过例子,我们会更清楚。那么,我们开始吧。
像往常一样,我们从导入库开始。
注意 : FacetGrid 需要存储在 pandas 数据帧中的数据,其中每行代表一个观察值,列代表变量。因此,我们也进口熊猫。
import numpy as np
import pandas as pdimport matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid', color_codes=True)%matplotlib inline
我们将使用 seaborn 的内置“tips”数据集。
tip = sns.load_dataset("tips")
tip.head()
FacetGrid 对象通过传递数据帧和变量名来初始化,以创建轴的结构。用于初始化 FacetGrid 对象的变量需要是分类的或离散的。根据类别的数量创建网格结构。例如,“时间”列有两个唯一的值。
tip['time'].value_counts()
Dinner 176
Lunch 68
Name: time, dtype: int64
让我们通过将“时间”变量传递给列参数来初始化 FacetGrid 对象。
g = sns.FacetGrid(tip, col='time')
我们刚刚创建了一个非常简单的包含两个方面的网格(每个子情节都是一个方面)。使用高度和纵横比参数调整刻面的尺寸。
- 高度是刻面的高度,单位为英寸
- 纵横比是宽度和高度的比值(宽度=纵横比*高度)。坡向的默认值为 1。
让我们用更大的面来更新网格。
g = sns.FacetGrid(tip, col='time', height=5)
是时候使用 FacetGrid.map() 方法在网格上绘制数据了。它需要一个绘图函数和变量作为参数进行绘图。
g = sns.FacetGrid(tip, col='time', height=5)
g.map(plt.hist, "total_bill")
网格显示基于“时间”的“总账单”直方图。根据绘图函数的不同,我们可能需要为 map 方法传递多个变量。例如,散点图需要两个变量。
g = sns.FacetGrid(tip, col='time', height=5)
g.map(plt.scatter, "total_bill", "tip")
让我们用行参数给网格增加一个维度。
g = sns.FacetGrid(tip, row='sex', col='time', height=4)
g.map(plt.scatter, "total_bill", "tip")
“性别”和“时间”列都有两个不同的值,因此创建了一个 2x2 FacetGrid。从上图中我们可以看出,男性和女性的“总账单”和“小费”变量具有相似的趋势。
色调参数允许用颜色给网格增加一个维度。
g = sns.FacetGrid(tip, row='sex', col='time', hue='smoker',
height=4)
g.map(plt.scatter, "total_bill", "tip")
g.add_legend()
我们现在对“总账单”、“小费”和“吸烟者”变量之间的关系有了一个概述。
“日”列有 4 个唯一值:
tip.day.value_counts()
Sat 87
Sun 76
Thur 62
Fri 19
Name: day, dtype: int64
我们可以创建一个 FacetGrid,显示“total_bill”在不同日子的分布情况。它会显示顾客是否在某一天花费更多。
g = sns.FacetGrid(tip, row='day',
row_order = tip.day.value_counts().index,
height=1.5, aspect=4)g.map(sns.distplot, "total_bill", hist=False)
似乎人们倾向于在周末多花一点钱。我们在该图中使用了行顺序参数。顾名思义,它决定了刻面的顺序。在前面的绘图中,我们使用了 matplotlib.pyplot 接口中的绘图函数。但是,对于最后一个,我们使用了 seaborn 包中的绘图功能。这是 FacetGrid 的一个很好的特性,提供了额外的灵活性。
为了丰富 FacetGrids 的功能和外观,可以在 facet grids 上添加更多的特性。如果你想更深入,我建议在 FacetGrid 上查看 seaborn 文档。
感谢您的阅读。如果您有任何反馈,请告诉我。
参考文献
- https://seaborn.pydata.org/generated/seaborn.FacetGrid.html
- https://seaborn . pydata . org/tutorial/axis _ grids . html # grid-tutorial
Seaborn 绘图函数层次结构
每个地块系列一个函数
python 中的 seaborn 包是我们大多数涉及数据可视化探索和提取洞察力的任务的首选。基于 matplotlib,seaborn 使我们能够生成更清晰的情节,更注重美感。
现在,随着本月 0.11.0 版本的发布,seaborn 不仅仅是一个绘图函数的集合,每个函数对应一种特定类型的绘图。
根据手头任务的性质,有明确定义的绘图功能层次和分组:
- 单变量分析(分布): displot()
- 关系分析(特征之间的相互作用和关系): relplot()
- 分类特征分析: catplot()
这 3 个顶级功能,也称为图形级功能,可用于主类别下的所有图。
来源:Seaborn 文档https://seaborn.pydata.org/index.html
我们为什么要关心这个?嗯,理解 seaborn 如何开展工作的这种细微差别是有好处的。这些优势列举如下:
- 你只需要知道三个函数——
relplot()
、displot()
、catplot()
,而不是全部十几个绘图函数。 - 图形级功能为各自类别中的所有图提供了相同的界面/签名。
- 默认情况下,这将确保图例(如果适用)绘制在绘图之外,从而减少对主要绘图元素的干扰
- 与 matplotlib 相比,基于数据集的特征容易生成小平面图,代码也更容易阅读
- 更简单的图形级定制
让我们来看看这些方面。我们将使用 seaborn 中内置的tips
数据集。
单变量分析—分布
displot
功能(你没看错!这不是印刷错误…是displot
而不是distplot
,后者现在已被弃用)迎合了描绘特征分布的三种类型的图——直方图、密度图和累积分布图。注意,我们在所有 3 种情况下都只调用了displot
,只是改变了kind
参数来得到一个不同的图。
双/多变量分析—关系图
relplot
功能适用于 2 个或更多特征之间的关系图系列(显示关系/相互作用或缺乏关系)——散点图和线图。
对于lineplot
,假设我们想要检查一周中每一天的平均账单金额的趋势。我们使用groupby()
按照数据集中的day
特征对观察值进行分组。然后,我们使用relplot
绘制线图。我们可以看到,与工作日相比,周末餐厅的平均账单金额有明显增加。
分类特征分析
图形级函数catplot
满足所有分类特征图
小平面图
与 matplotlib 相比,seaborn 的小平面绘图轻而易举,上面提到的图形级函数使用更少的代码和更少的混乱使它变得更加容易。查看以下图表:
可以通过添加col_wrap
参数并传递一个整数值来包装这些列。
如果您是一个习惯性的生物,您仍然可以继续使用本文开头显示的图形级绘图函数下的单独绘图函数。
总结和结论
seaborn 0.11.0 版本中的通用图形级函数简化了绘图函数接口,并为每类绘图下的所有绘图提供了一个通用函数签名。这也减少了通过 matplotlib 实现相同结果的代码和混乱。小平面情节/支线情节也很容易用一行代码实现。
您可能还会喜欢以下关于简化数据可视化和探索性数据分析的文章:
进一步扩展 ggplot2 的多功能性…
towardsdatascience.com](/patchwork-the-next-generation-of-ggplots-1fcad5d2ba8a) [## Matplotlib 多列、多行布局
…超越支线剧情中的标准网格布局
towardsdatascience.com](/matplotlib-multi-column-row-spanning-layouts-f026eb7c3c27) [## 使用 SmartEDA 开发的 EDA
探索性数据分析——更智能、更快速的方式…
towardsdatascience.com](/eda-in-r-with-smarteda-eae12f2c6094)
感谢您的阅读!
Seaborn: Python
Seaborn 是 Python 中的一个库,主要用于制作统计图形。
沃洛季米尔·赫里先科在 Unsplash 上的照片
Seaborn 是一个构建在 matplotlib 之上的数据可视化库,与 Python 中的 pandas 数据结构紧密集成。可视化是 Seaborn 的核心部分,有助于探索和理解数据。
要了解 Seaborn,你必须熟悉 Numpy 和**Matplotlib和 Pandas 。**
Seaborn 提供以下功能:
- 面向数据集的 API 来确定变量之间的关系。
- 线性回归图的自动估计和绘制。
- 它支持多绘图网格的高级抽象。
- 可视化单变量和双变量分布。
这些只是 Seaborn 提供的部分功能,还有更多,我们可以在这里探索所有的功能。
要初始化 Seaborn 库,使用的命令是:
import seaborn as sns
使用 Seaborn,我们可以绘制各种各样的图,如:
- 分布图
- 饼图和条形图
- 散点图
- 配对图
- 热图
对于整篇文章,我们使用从 Kaggle 下载的 Google Playstore 数据集。
1.分布图
我们可以将 Seaborn 中的分布图与 Matplotlib 中的直方图进行比较。它们都提供了非常相似的功能。代替直方图中的频率图,我们将在 y 轴上绘制一个近似的概率密度。
我们将在代码中使用 sns.distplot() 来绘制分布图。
在继续之前,首先,让我们访问我们的数据集,
从我们的系统访问数据集
数据集看起来像这样,
来自 Kaggle 的谷歌 Play 商店数据集
现在,让我们看看分布图看起来如何,如果我们从上面的数据集中绘制“评级”列,
评级栏分布图的代码
评级栏的分布图如下所示,
分布图—评级
这里,出现在分布图上的曲线( KDE )是近似概率密度曲线。
类似于 matplotlib 中的直方图,在 distribution 中也是如此,我们可以改变箱的数量,使图形更容易理解。
我们只需要在代码中增加仓的数量,
#Change the number of bins
sns.distplot(inp1.Rating, bins=20, kde = False)
plt.show()
现在,图表看起来像这样,
具有特定箱数的分布图
上图中,没有概率密度曲线。要移除曲线,我们只需在代码中写‘kde = False’。
我们还可以向分布图提供类似于 matplotlib 的条块的标题和颜色。让我们看看它的代码,
对于相同的列评级,分布图如下所示:
带标题的分布图
设计航海图
使用 Seaborn 的最大优势之一是,它为我们的图形提供了广泛的默认样式选项。
这些是 Seaborn 提供的默认样式。
'Solarize_Light2',
'_classic_test_patch',
'bmh',
'classic',
'dark_background',
'fast',
'fivethirtyeight',
'ggplot',
'grayscale',
'seaborn',
'seaborn-bright',
'seaborn-colorblind',
'seaborn-dark',
'seaborn-dark-palette',
'seaborn-darkgrid',
'seaborn-deep',
'seaborn-muted',
'seaborn-notebook',
'seaborn-paper',
'seaborn-pastel',
'seaborn-poster',
'seaborn-talk',
'seaborn-ticks',
'seaborn-white',
'seaborn-whitegrid',
'tableau-colorblind10'
我们只需编写一行代码就可以将这些样式合并到我们的图表中。
将深色背景应用于我们的图表后,分布图看起来像这样,
深色背景的分布图
2.饼图和条形图
饼图通常用于分析不同类别中数值变量的变化情况。
在我们使用的数据集中,我们将分析内容评级栏中前 4 个类别的表现。
首先,我们将对内容评级列进行一些数据清理/挖掘,并检查那里有哪些类别。
现在,类别列表将会是,
内容分级计数
根据上面的输出,由于“仅 18 岁以上成人”和“未分级”的数量明显少于其他类别,我们将从内容分级中删除这些类别并更新数据集。
在更新表格之后,在“内容分级”栏中出现的类别是,
更新数据集后的内容分级计数
现在,让我们为“内容评级”列中的类别绘制饼图。
上面代码的饼状图如下所示,
内容分级饼图
从上面的饼状图,我们无法正确推断是否“人人 10+”和“成熟 17+”。当这两个类别的值有些相似时,很难评估它们之间的差异。
我们可以通过在条形图中绘制上述数据来克服这种情况。****
现在,条形图看起来像下面这样,
内容分级栏的条形图
类似于饼图,我们也可以定制我们的条形图,用不同颜色的条,图表的标题,等等。
3.散点图
到目前为止,我们一直只处理数据集中的单个数字列,如评级、评论或大小等。但是,如果我们不得不推断两个数字列之间的关系,比如“评级和大小”或“评级和评论”呢?
当我们想要绘制数据集中任意两个数值列之间的关系时,可以使用散点图。这些图是在机器学习领域使用的最强大的可视化工具。
让我们看看数据集“Rating”和“Size”中的两个数字列的散点图是什么样子的。首先,我们将使用 matplotlib 绘制图形,然后我们将看到它在 seaborn 中的样子。
使用 matplotlib 的散点图
**#import all the necessary libraries** **#Plotting the scatter plot** plt.scatter(pstore.Size, pstore.Rating)
plt.show()
现在,情节看起来像这样
使用 Matplotlib 绘制散点图
使用 Seaborn 的散点图
我们将在散点图和直方图的代码中使用 sns.joinplot() 。
sns.scatterplot() 在代码中仅用于散点图。
上面代码的散点图看起来像,
使用 Seaborn 的散点图
在 seaborn 中使用散点图的主要优点是,我们将在图形中获得散点图和直方图。
如果我们只想看到散点图,而不是代码中的“ jointplot ”,只需将其更改为“散点图
回归图
回归图在接合图(散点图)中的两个数值参数之间创建一条回归线,并帮助可视化它们的线性关系。
该图如下所示,
在 Seaborn 中使用 jointplot 的回归图
从上面的图表中,我们可以推断,如果应用程序的价格增加,评级会稳步上升。
4.配对图
当我们想要查看 3 个以上不同数值变量之间的关系模式时,可以使用配对图。例如,假设我们想了解一家公司的销售如何受到三个不同因素的影响,在这种情况下,结对图将非常有帮助。
让我们为数据集中的评论、大小、价格和评级列创建一个配对图。
我们将在代码中使用 sns.pairplot() 一次绘制多个散点图。
上述图形的输出图形如下所示,
使用 Seaborn 的配对图
- 对于非对角线视图,图表将是两个数值变量之间的散点图****
- 对于对角线视图,它绘制了一个直方图**,因为两个轴(x,y)是相同的。**
5.热图
热图以二维形式显示数据。热图的最终目的是在彩色图表中显示信息摘要。它利用了使用颜色和颜色强度来可视化一系列值的概念。
我们大多数人都会在足球比赛中看到以下类型的图形,
足球运动员的热图
Seaborn 的热图正是创建了这些类型的图表。
我们将使用 sns.heatmap() 来绘制可视化。
当您拥有如下数据时,我们可以创建一个热图。
上表是使用 Pandas 的数据透视表创建的。你可以在我之前的文章 熊猫 中看到数据透视表是如何创建的。
现在,让我们看看如何为上表创建一个热图。
在上面的代码中,我们将数据保存在新变量“heat”中
热图如下所示,
在 Seaborn 上创建的默认热图
我们可以对上面的图形进行一些定制,也可以改变颜色渐变,这样最高值的颜色会更深,最低值的颜色会更浅。
更新后的代码将是这样的,
上面更新的代码的热图如下所示,
对代码进行了一些定制的热图
如果我们观察,在我们给出的代码“ annot = True ”中,这意味着,当 annot 为 true 时,图中的每个单元格显示其值。如果我们在代码中没有提到不能**,那么它的默认值是 False。**
Seaborn 还支持一些其他类型的图表,如线形图、条形图、堆积条形图、等。但是,它们与通过 matplotlib 创建的没有任何不同。
结论
这就是 seaborn 在 Python 中的工作方式,以及我们可以使用 Seaborn 创建的不同类型的图表。正如我已经提到的,Seaborn 构建在 matplotlib 库之上。因此,如果我们已经熟悉了 Matplotlib 及其函数,我们可以很容易地构建 Seaborn 图,并可以探索更深入的概念。
感谢您阅读和快乐编码!!!
在这里查看我以前关于 Python 的文章
- 熊猫:蟒蛇
- Matplotlib:Python
- NumPy: Python
- Python 中的时间复杂度及其重要性
- Python 中的递归或递归函数
- Python 程序检查阿姆斯特朗数(n 位数)和栅栏矩阵
- Python:基础参考问题—对换、阶乘、反数位、模式打印
参考
- ****Seaborn:https://www.w3schools.com/python/numpy_random_seaborn.asp
- ****seaborn 官方教程:https://seaborn.pydata.org/tutorial.html
- ****Seaborn |回归图:https://www.geeksforgeeks.org/seaborn-regression-plots/
- Seaborn 热图:【https://likegeeks.com/seaborn-heatmap-tutorial/】T22
- https://www.kaggle.com/lava18/google-play-store-apps—数据集: 谷歌 Play 商店
Seaborn 可视化教程
使用 NHL 统计数据浏览 Seaborn 的工具箱
作者安德鲁·科尔
克里斯·利维拉尼在 Unsplash 上的照片
如果你和我一样,一个没有体育的世界根本就不是世界。然而,进入 2020 年和新冠肺炎的时间,我们在这里,看着 2003 年 NCAA 锦标赛第二轮的重播,假装我们就像 2020 年锦标赛一样投入(当我打这篇文章时,它应该正在发生)。这个不幸的疫情也意味着我们错过了一年中我个人最喜欢的时间,NHL 季后赛。因此,为了弥补运动带给我们如此多的人的生活的缺乏,我决定将 NHL 统计数据整理成一个给我们中的一些人带来生活的概述。
Seaborn 是 Python 最强大和最基本的可视化包之一,通过数据讲述可视化故事有无限的可能性。所有 NHL 的数据都是从 MoneyPuck.com 的 T4 收集的。这款笔记本的 GitHub 库可以在这里找到。
数据
我们先清理一下现有的信息。我们将从所有情况下的溜冰者中选择数据(5v5,男子优势,人手不足,等等。).接下来,因为有 31 支 NHL 球队,这对于这些教学目的来说是一个很大的处理量,所以我们将数据限制在仅来自中央赛区的球队:芝加哥黑鹰队、纳什维尔掠夺者队、圣路易斯蓝调队、科罗拉多雪崩队、明尼苏达怀尔德队、温尼伯喷气机队和达拉斯明星队。
我们将要处理的数据帧如下所示:
我们有来自 200 个玩家的 153 个统计特征的统计数据。我们将只关注基本的统计数据,如进球、得分、点球等。
海生的
要导入库:
import seaborn as sns
我们将 seaborn 重命名为“sns ”,以便以后在可视化时调用它。
散点图— sns.relplot()
与任何数据集一样,我们想看看统计关系。也许观察二元关系的最好方法是使用散点图。每个点将显示一个统计要素中观测值的联合分布及其第二个要素的位置。我们先来看看积分是怎么来的(1 球= 1 分,1 助攻= 1 分;1 球+ 2 助攻= 3 分)
我们可以看到,正如你直觉所料,玩的游戏越多,得分越高。这只是你的基本散点图,那么我们如何让这个图表给我们更多的信息呢?让我们添加一个色调。
色调图— sns.relplot()
通过在 sns.relplot()代码中添加“hue”参数,我们能够看到每个团队的得分/比赛分布情况。我们能更进一步吗?这个情节只告诉我们每支队伍的分数和比赛,但是还有更多信息需要了解!职位呢?在那些单独的队伍中,谁得分更多?我们来加点支线剧情了解一下。
现在我们有了和上面一样的图表,按团队和位置分类。每行代表不同的球队,每列代表不同的位置(进攻或防守)。举个例子,我们可以看到芝加哥有一个前锋在刚刚打完的 60 多场比赛中得分超过 80 分,而芝加哥的大部分防守队员无论打多少场比赛得分都不到 20 分(只有一个例外)。
直方图— sns.distplot()
直方图是任何分析师都能创建的最强大的可视化工具之一。直方图以图形方式总结了所有数据的分布。简单来说,直方图显示了数据集中每个值出现的频率。x 轴包含变量度量,而 y 轴包含观察值的相对频率。
上面的直方图告诉我们,绝大多数联盟得分在 0 到 20 分之间。我们看到的平滑线是**核密度估计(KDE)——一种基于我们已有样本估计未知变量概率分布的技术。更简单地说,如果新的玩家数据被引入到集合中,它最有可能落在平滑线的最高峰之下。我们在图表底部看到的刻度线被称为地毯。**rug 简单地向我们展示了各个数据观察值在图表上的位置。您可以通过将代码参数设置为 False 来消除直方图中的 KDE 和地毯。
box plot—SNS . cat plot(kind = ’ box ')
另一种有助于我们了解数据的图表是箱线图。具体来说,箱线图帮助我们确定数据的中位数、范围和可变性在哪里。团队得分的箱线图如下所示:
我们看到的方框显示了分布的三个四分位数(大的彩色方框),该组的平均值(通过团队方框中间的水平线),以及异常值(图表上方的点)。例如,科罗拉多州的得分率大多在 0-32 之间。科罗拉多州盒子上方的点是异常值,这意味着雪崩有一个明显高于团队其他人的单点得分,因此使其成为异常值。
为了使这个图更具描述性,我们可以再次添加“位置”作为色调,以显示每个位置(进攻或防守)的团队中的异常信息。
小提琴情节— sns.catplot(kind = 'violin ')
小提琴图是一种不太流行但更具描述性的可视化方法。箱线图实际上没有考虑数据的分布。如果数据发生变化(比如增加整个联盟的数据,而不仅仅是中部地区的数据),中位数和范围不会变化,但小提琴图将反映这种变化。小提琴图将“变宽”,以表示该值附近更高的观察密度。
我们现在可以看到,芝加哥的防守得分在 0-40 分之间,而明尼苏达的防守得分范围要大得多。小提琴图越宽,观察值处的数据越密集。
我们还可以通过添加一个“分割”来创建一个更精简的版本:
图表显示了同样的事情,只是通过将防御和进攻小提琴合二为一而简化了。芝加哥的前锋得分范围很大,而其防守队员的得分都集中在 0 到 20 分之间。
蜂群图— sns.catplot(kind = 'swarm ')
群体图基本上就是一个散点图,X 轴代表一个分类变量。
我们可以通过这个群集图看到,温尼伯拥有分区最高的进球得分者,但他们球队的大部分得分都在 10 分以下。让我们翻转类别,按职位和团队查看目标。
现在我们可以看到前锋明显比防守队员进更多的球,迄今为止最高的射手为温尼伯效力。
抖动图— sns.catplot(抖动=真)
抖动图与我们的蜂群图非常相似,但它让我们更有条理。这是你正常的点状图,但是它增加了一个“抖动”——一个点与点之间的间隔,以便更好的可视化。让我们用一个抖动图来看看各个位置的罚分数量。我们可以看到所有被罚 16 分以上的球员都是前锋
Jointplot — sns.jointplot()
jointplot 是 seaborn 在显示单变量概要文件的同时显示双变量关系的方法。本质上结合了散点图和直方图(没有 KDE)。让我们来看看一个接合图,看看采取的处罚数量是如何与点生产。
如果我们看主要的散点图,我们真的不能做出很大的区别。人们固有地认为,少量的点球意味着更多的时间待在冰上,这意味着更多的得分机会。然而,散点图本身并没有显示任何方向上的强关系。但是,jointplot 为我们提供了沿着顶部和右侧脊线显示分布的好处。通过查看这些,我们可以看到随着处罚数量的增加,这些地区的玩家越来越少。积分也是如此。因此,我们可以推断,两者之间存在轻微的正相关关系。
hex plot—SNS . joint plot(kind = ’ hex ')
另一种可视化双变量关系的方法是 hexplot,特别是当我们有大量数据时。六格图将绘图窗口分成几个六格,然后落入每个格中的观察值的数量用一种颜色表示密度。颜色较深的六边形意味着在那个区域有更多的观察,或更多的密度。观察频率条形图可以沿着脊柱看到,作为额外的参考信息。我们将使用六边形图来分析进球数与射门次数之间的关系。
同样,这个图表可能是固有的。正如伟大的韦恩·格雷兹基/迈克尔·斯科特曾经说过的,“你没有出手的时候,你百分之百会错过”。我们相信随着射门次数的增加,进球数量也会增加。hexplot 重申了这个观点。我们在右下角看到了最暗的 hexbin,因为它是最密集的,因为在 NHL 中进球不是一件容易的事情,大多数球员都将集中在这个区域。当我们向右上方增加六边形时,颜色开始慢慢变淡,这表明射门次数和得分之间的正相关关系逐渐减少。
核密度估计-joint plot-SNS . joint plot(kind = ’ kde ')
与 hexbin 相似的二元图是核密度估计联合图。KDE 联合图也使用颜色来确定观察值最密集的地方,但不是将它们放入预定义的六边形中,而是使用新数据引入的概率来制作连续图。让我们看看同样的射门次数/进球次数的关系。
我们可以看到,给我们的信息与在 hexbin 图中给我们的信息相同,但这显示了观察位置的概率视图。hexbin 没有向我们显示大多数球员都属于低进球/低射门尝试类别,我们可以看到更多射门尝试等于更多进球的概率越来越呈正相关,最大的球员集中在 0-50 次射门尝试范围和 0-5 次进球范围。
相关
双变量关系可以告诉我们很多,但是仅仅看分布图和散点图可能不足以提供我们所需要的关于数据表面数字下发生的事情的所有信息。相关性向我们展示了一个变量的值对另一个变量的影响程度。强相关性(1.00)表示当一个变量发生变化时,另一个变量也会发生 100%的正向变化(相反方向为-1.00)。让我们看看 NHL 中的某些变量在相关性方面有多重要。下面是检查相关性的代码:
观察这些数字让我们明白了很多,注意,z 轴向右下方移动代表了变量与自身的完美相关性。我们可以看到,像分数这样的某些变量**与射门次数、**密切相关,因为相关系数都远高于 0.5。但是统计数据和数据集通常不像体育统计数据那样直观,所以让我们看看如何让这个相关图表对用户更友好。
热图— sns.heatmap()
热图只是我们在上面制作的关联表的一种更友好的可视化方式。如果相关系数较高,表明两个变量之间的相关性更显著,则颜色会更深。同样,参考深蓝色的 z 轴来表示变量与其自身之间完美的 1:1 相关性。
这张热图只是以一种更简单的方式将我们的目光吸引到最佳和最差的相关性上。例如,我们很容易看到射门次数和射门次数的相关性最强,而命中率与进球的相关性最小。
Pairplot — sns.pairplot()
最后,或许对任何分析师来说,最强大、最有用的工具之一就是 Pairplot。pairplot 可视化了单个变量的分布以及与其他变量的双变量关系。简单地说,我们将为数据框架中的每个变量创建一个二元散点图,然后将它们放入一个屏幕中。
这个庞大的图表有一大堆,但是它也非常有助于获得我们正在寻找的东西的整体视图。我们阅读它就像阅读二元散点图一样。如果我们看到两个变量之间有很强的正/负关系,我们就知道这些变量及其关系值得进一步研究。
就是这样!目前…
Seaborn 是一个非常强大的工具,可以将复杂的数据转化为易于理解的信息。可能性似乎是无穷无尽的,但希望这是所有可能性的良好开端。因此,请大家保持安全,保持健康,待在室内,我们都会没事的:)。
用深度学习搜索 COVID 论文
一个语义浏览器使用深度学习和弹性搜索来搜索 COVID 论文
今天,我们将使用深度学习建立一个语义浏览器,在超过 5 万篇关于最近新冠肺炎病的论文中进行搜索。
所有代码都在我的 GitHub repo 上。而这篇文章的现场版是这里
其核心思想是将每篇论文编码成一个表示其语义内容的向量,然后使用查询和所有编码文档之间的余弦相似性进行搜索。这与图像浏览器(例如 Google Images)搜索相似图像的过程相同。
因此,我们的难题由三部分组成:数据、从论文到向量的映射以及搜索方法。
大部分工作都是基于这个项目,在这个项目中,我和来自意大利的里雅斯特大学的学生一起工作。此处有现场演示。
我们开始吧!
数据
一切从数据开始。我们将使用来自 Kaggle 的数据集。由白宫和领先研究团体联盟准备的超过 57,000 篇学术文章的列表。实际上,我们唯一需要的文件是包含论文信息和摘要全文的metadata.csv
。您需要将文件存储在./dataset
中。
让我们来看看
import pandas as pddf = pd.read_csv('./dataset/metadata.csv')df.head(5)
如你所见,我们有很多信息。我们显然对文本栏感兴趣。和熊猫一起工作并不理想,所以让我们创造一个Dataset
。这将允许我们稍后创建一个DataLoader
来执行分批编码。如果你不熟悉 Pytorch 数据加载生态系统,你可以在这里阅读更多关于的内容
按照顺序,我子类化了torch.utils.data.Dataset
来创建一个定制数据集。数据集需要一个 dataframe 作为输入,我们只保留了感兴趣的列。然后,我们删除了一些行,其中的abstract
和title
列分别与FILTER_TITLE
和FILTER_ABSTRACT
中的一个“垃圾”单词相匹配。这样做是因为文章以自动的方式被废弃,并且许多文章有不相关的条目而不是标题/摘要信息。
数据集返回一个字典,因为 PyTorch 不支持pd.DataFrame
类型。为了给我们的搜索引擎更多的上下文,我们将title
和abstract
合并在一起,结果存储在title_abstract
键中。
我们现在可以调用数据集,看看是否一切都是正确的
ds = CovidPapersDataset.from_path('./dataset/metadata.csv')ds[0]['title']
输出:
'Sequence requirements for RNA strand transfer during nidovirus discontinuous subgenomic RNA synthesis'
把…嵌入
我们现在需要一种方法从每个数据点创建一个向量(嵌入)。我们定义了一个类Embedder
,它使用 sentence_transformers 库从 HuggingFace 的 [transformers](https://github.com/huggingface/transformers)
中自动加载一个模型。
选择的模型是gsarti/BioBERT-nliaBioBERT模型,在 SNLI 和 MultiNLI 上进行微调,以产生通用句子嵌入。Gabriele Sarti 进行了微调,复制它的代码可从这里获得。
BioBERT 特别适合我们的数据集,因为它最初是在生物医学科学出版物上训练的。因此,考虑到与我们的数据的相似性,它应该创建更好的上下文感知嵌入。
在幕后,模型首先将输入字符串标记化,然后为每个标记创建一个向量。因此,如果我们在一篇论文中有N
个标记,我们将得到一个[N, 768]
向量(注意,一个标记通常对应一个单词片段,在这里阅读更多关于标记化策略。因此,如果两篇论文有不同的字数,我们将有两个不同的第一维向量。这是一个问题,因为我们需要将它们与搜索进行比较。
为了获得每篇论文的固定嵌入,我们应用平均池。这种方法计算每个单词的平均值,并输出一个固定大小的 dims 向量[1, 768]
所以,让我们编写一个Embedder
类
我们可以在数据点上尝试我们的嵌入器
embedder = Embedder()emb = embedder(ds[0]['title_abstract'])emb[0].shape // (768,)
瞧啊!我们对一篇论文进行了编码。
搜索
好的,我们知道如何嵌入每篇论文,但是我们如何使用查询来搜索数据呢?假设我们已经嵌入了所有论文,我们也可以嵌入查询并计算查询和所有嵌入之间的余弦相似性。然后,我们可以显示按距离(分数)排序的结果。直觉上,它们在嵌入空间中离查询越近,它们共享的上下文相似性就越大。
但是,怎么做呢?首先,我们需要一种适当的方法来管理数据,并足够快地运行余弦相似性。幸运的是,弹性搜索来救援!
弹性搜索
弹性搜索是一个目标只有一个的数据库,是的你猜对了:搜索。我们将首先在 elastic 中存储所有嵌入,然后使用它的 API 来执行搜索。如果你像我一样懒,你可以用 docker 安装弹性搜索
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.6.2
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.6.2
完美。下一步是在弹性搜索中存储嵌入和论文信息。这是一个非常简单的过程。我们需要创建一个index
(一个新的数据库),然后为每篇论文建立一个条目。
为了创建一个index
,我们需要为 elastic 描述我们希望存储的内容。在我们的案例中:
你可以在弹性搜索文档上阅读更多关于索引创建的内容。最后一个条目将embed
字段定义为带有768
的密集向量。这确实是我们的嵌入。为了方便起见,我将配置存储在一个.json
文件中,并创建了一个名为ElasticSearchProvider
的类来处理存储过程。
大部分工作都是在create_and_bulk_documents
完成的,在那里我们每次只解构一个条目,并添加两个弹性搜索参数。
不幸的是,弹性搜索无法序列化numpy
数组。所以我们需要为我们的数据创建一个适配器。该类将纸张数据和嵌入作为输入,并“调整”它们以在我们的ElasticSearchProvider
中工作。
好了,我们一切就绪。一种表示数据的方法,一种将数据编码成向量的方法和一种存储结果的方法。让我们把所有的东西都包起来,把所有的文件都编码。
这里有两个技巧,首先,我们使用torch.utils.data.DataLoader
创建一个批处理式迭代器。总的来说,将数据成批地而不是作为一个单独的点提供给模型可以提高性能(在我的例子中是 x100)。其次,我们替换DataLoader
构造函数中的collate_fn
参数。这是因为,默认情况下,Pytorch 会尝试将我们所有的数据转换成一个torch.Tensor
,但是它无法转换字符串。通过这样做,我们只返回一个字典数组,即来自CovidPapersDataset
的输出。所以,batch
是一个长度为batch_size
的字典列表。我们做完后(1080ti 上~7m),可以看一下[http://localhost:9200/covid/_search?pretty=true&q=*:*](http://localhost:9200/covid/_search?pretty=true&q=*:*.)
。
如果一切正常,您应该会看到弹性搜索显示我们的数据
进行查询
我们差不多完成了。拼图的最后一块是在数据库中搜索的方法。弹性搜索可以在所有文档中的一个输入向量和目标向量场之间执行余弦相似性。语法非常简单:
{
"query": {
"match_all": {}
},
"script": {
"source":
"cosineSimilarity(params.query_vector, doc['embed']) + 1.0",
"params": {
"query_vector": vector
}
}
}
其中vector
是我们的输入。因此,我们创建了一个类,它将一个向量作为输入,并显示查询的所有结果
让我们看看第一个结果(我已经复制并粘贴了第一篇匹配论文的摘要)
es_search = ElasticSearcher()
es_search(embedder(['Effect of the virus on pregnant women'])[0].tolist())
作为应对新发感染的公共卫生专业人员,需要特别关注孕妇及其后代。孕妇可能更容易感染新出现的感染,或受到更严重的影响。新的母体感染对胚胎或胎儿的影响很难预测。一些推荐用于预防或治疗的药物可能会伤害胚胎或胎儿。我们讨论了应对孕妇中新出现的感染的挑战,并提出了克服这些挑战的策略。
成功了!我还创建了一个命令行,用户可以在其中输入查询。最后的结果是:
更多查询
最后可以提一个问题,找感兴趣的论文。从经验上看,越详细的查询效果越好,因为它们提供了更多的上下文。
例如,我们可能想知道氯喹对新冠肺炎有什么效果。结果是
或者新冠肺炎如何与 ACE2 受体结合?**
搜索引擎似乎工作良好,但它并不完美,在本教程的下一部分,我们将努力提高其准确性。
结论
在这个项目中,我们建立了一个语义浏览器来搜索超过 5 万篇新冠肺炎论文。我和的里雅斯特大学的学生合作的最初项目是这里的。这里有现场演示
你也可以使用命令行程序,你需要按照这里的指示。
承认
我要感谢 Gabriele Sarti 对我撰写本文的帮助, Marco Franzon 和 Tommaso Rodani 对我在弹性搜索实现中的支持。
感谢您的阅读
注意安全,
弗朗西斯科·萨维里奥·祖皮奇尼
搜索(第 1 部分)——一个温和的介绍
从基本的积木到 DIY 搜索引擎
照片由 PhotoMIX 公司从 Pexels 拍摄
T4 在不到一秒钟的时间里搜索整个网络,寻找我们想知道的任何东西的能力是近代史上最伟大的成就之一。但是它是如何工作的呢?它的组成部分是什么?而且,最重要的是,……我们能不能拼凑出我们自己的版本?后者很重要,因为搜索不可避免地是个人化的:它关乎我们的关注点、偏好、可支配的资源甚至情感。另外,它真的很酷!
在这个由三部分组成的系列中,我将:
- Pt 1。提供一个温和的介绍使用 Google 和 Elasticsearch 作为例子进行搜索
- Pt 2。我们将解释一些**最新的自然语言处理技术,**将结果与传统方法进行比较,并讨论利弊
- Pt 3。提供一份黑客指南,帮助您构建自己的搜索引擎包含 100 万条新闻标题的 elastic search engine&采用最先进的自然语言处理技术进行增强的语义搜索…
搜索-简而言之
如今,当我们谈论搜索时,我们通常指的是语义搜索。你会问,什么是语义搜索?想象一下,搜索“病毒威胁”这个词。一个简单的词汇搜索方法将返回包含单词的文档,并且按照特定的重要性顺序排列。另外,关于“安全威胁”的文档也将被认为是相关的,因为它们包含查询的一部分。
另一方面,语义搜索也能够获得“疾病”、“感染”和“电晕”的概念——我们有一个更广泛、潜在更准确的搜索,反映我们正在寻找的东西的“意义”,而不是坚持特定的关键词。在这一部分,我经常从巴斯特,汉娜的作品中获得灵感;比约恩·布赫霍尔德;埃尔马尔·奥斯曼(2016 年)。“文本和知识库的语义搜索”。他们在文中指出:
语义搜索表示有意义的搜索,与词汇搜索不同,词汇搜索是搜索引擎在不理解查询的整体意义的情况下寻找查询词或其变体的文字匹配
下图显示了这种搜索引擎的核心组件
作者图片
我们专注于对带有一些附加注释(如姓名、日期、链接等)的文本的语义搜索,而不是对结构化数据库的搜索。这基本上是我们一直在使用的典型网络搜索。
请注意,本文处理的是产生相关文档或单个事实列表的搜索,而不是额外的步骤,如基于源质量的排名,如 PageRank、结果汇总等。
查询类型
这些可以分为:
- 关键词 -这些是速记搜索,不是正确的句子,但其中的一组关键词和有时的顺序带有语义,例如尼尔·阿姆斯特朗的出生日期,10 分钟内的意大利面食谱
- 结构化/半结构化 -查询中使用的特殊语法。它可以表示完整的查询,也可以只是对它的细化。例如,这可能是仅搜索特定来源的限制,例如仅来自 AP 的新闻。在其他情况下,这可能会限制结果的语言或陈述查询的强制元素
- 自然语言&自然问题——完全或大部分语法形式的问题:“尼尔·阿姆斯特朗的生日是哪一天?”。这是与搜索交互的最自然的方式,然而,这也带来了许多困难。例如,我们可以同时问多个问题“哪里可以停车,营业时间是什么时候?”或者提出哲学上的问题,而不是事实上的问题“生命的意义是什么?”。从例子中可以看出,问题的范围相当广泛。虽然这些对我们来说有意义,但算法往往专注于狭窄的任务,因此需要各种算法协同工作,能够确定哪些结果是合适的。
询问处理
在将原始条目传递给搜索算法之前,系统可能需要对其执行不同类型的转换。那些可能是
- 提取 -提取特定的名称、实体、位置,以进一步帮助搜索并与文档元数据中的值或知识库进行比较。例如,在下面的查询 Neil Armstrong 中,左边的信息框是调用 google 知识库的结果,因为该查询与其中的一个条目成功匹配
- 过滤器和限制 -在半结构化查询对结果指定了一些限制的情况下,例如,只有英语新闻,搜索范围将被转换到搜索引擎
- 其他转换是对搜索的修改,例如针对通配符或模糊搜索。在这种情况下,原始查询可以被转换成一个或多个变体。例如,使用模糊搜索,我们可以允许对输入的关键字进行一些字符修改,直到找到最有可能被搜索的单词。请看下面,当我在谷歌上搜索尼尔·阿姆斯隆时的结果。即使一个叫 Armslong 的先生可能存在并且很重要,系统认为我们更有可能是打错了。
搜索和排名
最后,可以使用一种或多种类型的搜索和排序方法。这些将能够找到答案或者返回匹配查询的结果的排序列表。排名确保更相关的结果在更高的位置-这些结果可能比其他结果更频繁地提到搜索的关键字,或者在其标题或开头段落中包含与查询相关的信息。有:
- 关键词搜索——最常见的类型,精确或非常接近文字匹配。搜索的主要部分仍然是这样完成的。什么使它们具有语义——它们将使用术语出现来排列与某个关键词更相关的更高的文档,并识别出某些关键词何时是罕见的,这些关键词的命中率高于查询中更“常见”的词的命中率。有许多算法可用:BM25、tf-idf、各种学习排序方法等。
- 上下文搜索——我指的是任何基于文本嵌入的搜索,试图完全使用查询并找到上下文相关的结果。这与单独依赖任何特定的关键词或短语来确定结果相反。我们将在稍后重点讨论这个问题,因为它是本系列的核心。NLP 技术的一些最新进展将帮助我们显著提高搜索质量。
让我们快速地进行一个**面对面的关键词对比上下文搜索。**搜索“病毒线程”,在新闻标题上,左边的结果来自关键字方法,而右边的结果来自上下文搜索。后者给了我们一些结果,这些结果与任何搜索词都不匹配,如右边的示例 5:“世卫组织强调病媒传播疾病的危险”
- 知识库 -如上所述,知识库中的条目可以直接与知识库中的条目匹配,并进一步用于生成结果。也可以应用更高级的技术,其中关键字或自然语言查询可以被转换成对知识库的查询。例如,“月球上的宇航员”将返回另一个知识库结果
- 问答——传统上,搜索引擎已经使用处理步骤的修改,将一个自然的问题转换成一个更像关键词的查询,并照此进行处理。最近,自然语言处理的进展显示了算法的强大性能,这些算法直接指出在特定文档中是否以及在哪里可以找到自然问题的答案。与上面的其他搜索范例不同,问答侧重于提供一个实际的(单个)答案,而不是一系列文档(就像这个列表中的其他文档)。当我们把登月作为一个自然的问题来问时,会发生什么。除了一系列答案之外,我们还会得到一个具体的答案。
然而,这项技术同样适用于一个不太自然的问题“首次登月的年份”
最后,对查询的轻微修改可能会破坏结果,我们不再得到明确的答案,我们甚至完全在其他地方着陆
把所有的放在一起
总之,任何查询类型都可以经过许多不同的修改,并可以通过许多搜索机制中的任何一种来产生候选结果。这些方法中的每一种都将表达其结果的置信度,然而,不同算法之间的不同置信度分数可能不可比较。在这一阶段,进一步的决策算法将能够确定哪些答案是非常合适的,并且足够“自信”以作为最终的答案列表传递给用户。
作者图片
一个正常运行的搜索引擎可以包含该过程三个步骤中的任何一个或至少一个。我们已经看到谷歌在幕后使用了它们中的大部分,但是如何制作我们自己的呢…
我应该透露我的秘密议程…
事实上,我一直想黑掉我自己的搜索引擎。
选择的工具是 Elasticsearch,主要是因为它实际上自带了很多搜索功能。同时,它得到了很好的支持,并在开源特性方面给了你很大的帮助。
这里是一个图表,显示了我们在讨论中使用 Elastic 后得到的结果。请注意,您不应该相信我的总结完整性,因为我心中有一个具体的目标。
作者图片
您会注意到,Elastic 可以处理任何类型的查询(尽管它们在默认情况下都由关键字搜索机制处理),并允许进一步修改您的查询,以模糊、通配符和许多其他类型的查询。如果数据允许,人们还可以对结果应用任意数量的结构化条件:出版日期、来源等。
就搜索和排名而言,关键字搜索有很大的灵活性,但其他的就不多了。
总的来说,这是一个令人印象深刻的开箱即用特性列表。事实证明,通过一些额外的跑腿工作,我们甚至可以添加上下文搜索。这就是我们接下来要做的…
结论
我们探讨了搜索的主要组成部分,它们是如何协同工作的,以及对搜索结果的影响。不同类型的查询可能触发不同的搜索算法,结果是各种方法的混合。从 Google 的例子中我们可以看到,相同的用户体验(在一个简单的文本框中输入)是由多种技术提供的。
在接下来的文章中,我们将并排比较上下文搜索和关键字搜索( Pt 2 ),最后,我们将结合一些不同的工具来扩展 Elasticsearch 的功能,增加上下文搜索功能来构建我们自己的语义搜索引擎( Pt 3 )。
…
顺便说一句,尼尔·阿姆斯隆
我希望你喜欢阅读这篇文章,下周我们将在第二部分中带来更多内容。与此同时,如果你想打招呼或者只是想告诉我我错了,请随时通过 LinkedIn 联系我
特别感谢 Rich Knuszka 的宝贵反馈。
请注意,我与谷歌或 Elasticsearch 没有任何关系,观点和分析是我自己的
搜索(第二章)——语义赛马
领先的自然语言处理与传统搜索
诺亚·西利曼在 Unsplash 上的照片
在这篇非技术性的文章中,我们将比较上下文搜索和基于关键字的方法。对于前者,我们将利用 NLP 的一些最新发展来搜索大量的新闻。我们将重点解释这种方法与传统方法的区别、优缺点。
这是一个关于搜索的三部分系列。
在的第一部分——一个温和的介绍中,我们提供了一个搜索基本构件的概述。
最后, Pt 3 (Elastic Transformers) 包含了如何构建一个索引的纯技术考虑,作为一个带有上下文文本嵌入的弹性搜索引擎。对于当前的讨论,我们将使用该搜索索引的一些结果。
在本文中,我们将
- 了解关键字和上下文搜索如何与进行比较,以及 NLP 中的最新技术可以在哪些方面帮助我们进行搜索
- 考虑一些例子测试不同的查询以及两者有何不同
- 最后,我们将一起考虑这些方法的利弊
图片由作者使用 gifox
在上一篇文章中,我们鸟瞰了搜索的工作原理、构建模块以及它们之间的区别。这里,我们将考虑上下文搜索和关键字搜索之间的实际比较。对于上下文搜索,我们具体指的是使用 NLP 转换器的文本嵌入,我们稍后会深入探讨。
传统的关键词搜索倾向于使用特定的关键词频率来识别搜索查询的最佳匹配。然而,在某些情况下,这可能是有限制的,特别是,如果我们使用的关键字不代表我们正在搜索的文档。例如,我们可能查找“自然灾害”,但是可用的文档可能包含许多特定灾害的示例,例如“飓风”、“地震”等。而不像我们明确选择的那样提及那些“自然灾害”。这就是语境嵌入可以发挥作用的地方。
为上下文搜索输入文本嵌入
文本嵌入是单词、句子或文档作为向量的数学表示。这意味着我们可以用允许我们对文本进行数学运算的方式来表示文本。我们可以这样说,数字 2 更接近于 3 而不是 10,它们之间的距离都更接近于 100。如果我们能够以这种方式对一个文档的全部含义进行编码,我们就可以使用这些属性来寻找文档之间的相似性,将它们分组到有意义的簇中,等等。
在大多数情况下,机器“学习”这种表示的方式是给它们大量的文本阅读,并敦促它们学习哪些单词或句子“放在一起”。著名的:
从交的朋友就知道一个词——弗斯,J. R
一些陷阱和拯救变形金刚
早期的文本嵌入工具通常只能捕获单个单词(Word2Vec、GloVe 等),然而,对于短语可能与其中的单词有不同含义的例子,这仍然是个问题。“自然”和“灾难”放在一起意味着完全不同的东西。最近自然语言处理的进展,已经产生了一批上下文嵌入模型,如【ELMo】BERT等。(这里有一个很好的概述)。然而,通常这些都是预先训练好的工具,仍然需要针对特定任务进行微调。这样的任务可以是:句子情感识别、句子相似度、问答等。Huggingface 的 变形金刚库使得这些工具的使用变得非常容易。
野兔,我们将使用句子转换器——这是一个 Python 库,它(除其他外)为我们带来了 SBERT 模型,这些模型为句子语义相似性的任务进行了预训练,允许我们比较整个句子的意思。(一定要看看的论文。这是我们所做工作的关键使能因素,因为如果没有强大的文档级嵌入(在这种情况下,新闻标题就是文档),我们就无法在查询和搜索的文档之间提取有意义的比较。注意,SBERT 不是唯一的方法,其他的方法也可以,例如使用、推断等等。此外,这种搜索方法试图将查询与查询中提到相似单词的结果进行匹配。在这种情况下,它类似于关键字搜索,即寻找使用相同查询词的文档,但不取代“问答”,即我们寻找某个问题的特定答案。
现在…真正的赛马…
在这里,我将比较我们并排查询这两种方法得到的结果。我将试着比较这些差异,并从中得出一些结论。
请注意,类似任务的更正式的评估在 IR-BERT 中完成(任务是找到给定(查询)文章的最佳支持文章,任务 1 在此为)。作者证明了上下文方法(SBERT)明显优于纯关键词方法(BM25),然而,当使用更先进的关键词加权技术时,结果是混合的。
首先,在许多情况下,简单的关键字搜索就足够了。考虑搜索“苹果公司”。我们不需要一个语义引擎。因为要寻找的是一个“命名实体”,精确匹配是有意义的,近似匹配可能是完全错误的。在某种程度上,当我们非常明确我们在找什么的时候,在找具体的名字、日期、id 等等。精确匹配可能就足够了。然而,如果我们遗漏了我们需要的确切术语(参见之前的“自然灾害”示例),我们将受益于基于上下文的结果,而不是精确匹配。****
****设置:我在一些查询中并排比较了关键字和上下文搜索的最佳结果。我用绿色突出显示与搜索明显相关的结果,琥珀色——不明确的结果,红色——不相关的结果。请注意,我们使用了一百万个新闻标题——来源于美国广播公司新闻,即新闻有一些澳大利亚的焦点。
病毒威胁
让我们搜索一下“病毒威胁”。
作者图片
这两种方法都产生了良好的结果。注意结果#5。“世卫组织强调病媒传染疾病的危险”不包括我们的任何搜索词,但仍然高度相关。以下是上下文搜索结果不包含任何关键字的更多示例。我们看到相关提及:疫情、感染、寄生虫等
作者图片
自然灾害
语料库中有大量“自然灾害”的精确匹配,因此在两种搜索形式中,前 10 次提及彼此非常接近。然而,考虑没有与关键字精确匹配的上下文结果。我们可以看到相关的提及,如:洪水、反常风暴、灾难性火灾等
作者图片
“监管风险银行业改革”
进一步扩展查询会使事情变得更加复杂。首先,关于查询:这里的问题有点模糊,因为用户显然对银行改革和监管感兴趣,但没有具体说明什么、谁、何时**。这可能是在一个更广泛的研究背景下,试图比较和对比案例。因此,一个好的结果(独立于搜索方法)是寻找不同的例子。巧合的是,结果的多样性也是上下文搜索表现更好的 IR-BERT 中考虑的一个指标**
作者图片
从上下文结果来看,似乎很明显,主题是银行业,但缩小到监管/改革主题会导致有争议的结果。提到了 RBA(澳大利亚中央银行),但并不总是与监管风险相关,还提到了 皇家委员会 (该委员会正在调查金融服务中的一系列金融不当行为)与金融相关,但并不总是与银行业相关**。然而,在关键字搜索方面,尽管缺乏好的文档,但一致返回的文档在查询中至少会有 2 个单词,这似乎经常使结果“语义相关”——只要有几个关键字,结果就可能是相关的。还要注意关键词搜索的可解释性如何帮助我们立即排除一些情况。例如,关键词结果#8“政府互联网监管计划受到批评”——我们确切地知道这是为什么建议,因此可以很快排除它。相比之下,上下文结果# 5“RBA 考虑信用卡附加费的上限”并不清楚为什么模型认为它是一个好的结果**
好的和坏的…
与关键字搜索相比,上下文搜索的优点和缺点是什么
赞成的意见
上下文位— 正如我们所见,关键字搜索有时会有局限性。这在做研究或刚开始探索一个主题时尤为重要,因为我们经常不会搜索相关的术语、关键词和实体
并排功能 —在当前设置中,我们可以轻松地在关键字和上下文搜索之间切换。可以进一步做一些工作来使两个结果并排排列。然而,需要一些假设。请注意,关键字搜索也可以用作良好候选项的初始过滤器,以提高解决方案的整体速度。更多关于速度的考虑在第 3 集中。
骗局
可解释性 —我们不能轻易地逆向工程为什么模型决定显示一个特定的结果。在结果中看到我们的搜索词有助于我们清楚地决定是否同意这些结果。这是一个正在进行的研究和开发领域。最近,谷歌发布了一个工具,用于对语境嵌入的可解释性。然而,这里的使用不是一个简单的应用程序,因为我们还需要评估搜索匹配的具体驱动因素。
上下文是与上下文相关的— 谈论语义搜索和意义可以是我们实际寻找的高度特定的领域。例如,我们可能对自己健康背景下的“疫苗”感兴趣,或者作为投资者决定公司支持,或者作为研究人员——寻找试验、方法等的技术细节。这些方面可以通过细化查询来部分解决,但是结果的质量将受到索引中可用文档以及上下文嵌入的训练方式的限制。特别值得关注的是,系统没有配备向用户指示特定领域查询的质量可能很差——它将始终尽职尽责地提供排序结果的列表。实际上,特定领域搜索引擎有助于解决这个问题。一个例子是基于特定领域的文章和研究为 COVID 搜索者提供的专门工具。这类工具的几个例子是 Corona Papers 和 Google 的 COVID Research Explorer ,它们都使用 CORD-19 (特定于 COVID 的研究文章数据库)来将所使用的文本嵌入上下文化
结论
我们已经看到了什么是文本嵌入以及它们如何帮助上下文搜索的概要。
我们用一些对比例子来说明,并看到上下文搜索如何帮助我们找到我们在查询中没有预料到的主题和术语。
最后,我们讨论了这种方法的一些缺点——缺乏可解释性以及特定领域的查询可能不能很好地工作。
剩下的就是展示构建这样一个工具的技术实现和考虑事项——我们将在第三部分中看到
希望这是有用的。感谢您的阅读。如果你想打招呼,请通过 LinkedIn 联系
搜索和替换文本
Python 中的文本操作
文本操作包括诸如分离字符串、搜索、替换和解析等操作。虽然这些任务中有许多可以使用 python 中的内置字符串方法来完成,但更复杂的操作需要正则表达式。在这篇文章中,我们将讨论如何使用 python 中的正则表达式模块来搜索和替换文本。
我们开始吧!
首先,让我们考虑下面的字符串文字:
text1 = "python is amazing. I love python, it is the best language. python is the most readable language."
假设,出于某种疯狂的原因,我们想用“c++”替换“python”。我们可以使用“str.replace()”方法:
text1 = text1.replace('python', 'C++')
让我们打印结果:
print(text1)
对于更复杂的模式,我们可以使用“re”模块中的“re.sub()”方法。让我们导入正则表达式模块“re”:
import re
假设我们想将以下字符串中的日期格式从“12/01/2017”更改为“2017–12–01”:
text2 = "The stable release of python 3.8 was on 02/24/2020\. The stable release of C++17 was on 12/01/2017."
我们可以使用’ re.sub()'方法来重新格式化这些日期:
text2 = re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text2)
print(text2)
替换方法中的第一个参数“r’(\d+)/(\d+)/(\d+)‘”是要匹配的模式。“\d+”表达式对应于 0–9 范围内的数字字符。第二个参数“r’\3-\1-\2 '”是替换模式。替换模式中的数字指的是模式中的捕获组号。在这种情况下,组 1 是月,组 2 是日,组 3 是年。我们可以使用“group()”、“match()”和“compile()”方法直接看到这一点:
date_pattern = re.compile(r'(\d+)/(\d+)/(\d+)')
date_pattern.match(“12/01/2017”)
print(date_pattern.group(1))
print(date_pattern.group(2))
print(date_pattern.group(3))
编译替换模式还会提高重复替换的性能。让我们编译匹配模式:
date_pattern = re.compile(r'(\d+)/(\d+)/(\d+)')
然后使用替换模式调用替换方法:
date_pattern = date_pattern.sub(r'\3-\1-\2', text2)
我们还可以为更复杂的替换指定一个替换回调函数。例如,如果我们要将“2017 年 12 月 1 日”重新格式化为“2017 年 12 月 1 日”:
from calendar import month_abbr
def format_date(date_input):
month_name = month_abbr[int(m.group(1))]
return '{} {} {}'.format(date_input.group(2), month_name, date_input.group(3))print(date_pattern.sub(format_date, text2))
另一个要考虑的有趣问题是如何以不区分大小写的方式搜索和替换文本。如果我们考虑前面的例子:
text3 = "Python is amazing. I love python, it is the best language. Python is the most readable language."
现在,这篇课文中第一句和第二句的第一个单词是大写的。在这种情况下,替换方法将以区分大小写的方式替换文本:
print(text3.replace('python', 'C++'))
我们看到只有小写的“python”被替换了。我们可以使用’ re.sub()‘通过传递’ flags = re 以不区分大小写的方式替换文本。子方法的“IGNORECASE ”:
print(re.sub('python', 'C++', text3, flags =re.IGNORECASE))
我们看到我们得到了想要的结果。我就讲到这里,但是我鼓励你自己去研究代码。
结论
总之,在这篇文章中,我们讨论了如何使用 python 中的正则表达式模块来搜索和替换文本。我们展示了如何使用替换方法重新格式化字符串中的日期,并以不区分大小写的方式替换文本。如果你想学习更多关于 python 中正则表达式的知识,我推荐 Python 指南 的 中的“第二章:字符串和文本”。这篇文章的代码可以在 GitHub 上找到。我希望你觉得这篇文章有用/有趣。感谢您的阅读!