TowardsDataScience-博客中文翻译-2021-三十七-
TowardsDataScience 博客中文翻译 2021(三十七)
原文:TowardsDataScience Blog
协议:CC BY-NC-SA 4.0
使用 Python 进行探索性数据分析—第 2 部分
原文:https://towardsdatascience.com/exploratory-data-analysis-with-python-part-2-9b0b6c1402c2?source=collection_archive---------29-----------------------
让您开始使用 Python 和 Pandas 分析数据的模板

亚历山大·辛恩在 Unsplash 上的照片
在第 1 部分中,我们回顾了一些事情,让您开始探索您的数据集。
如果您想重温它,或者如果您首先找到了这一部分并想阅读第 1 部分,这里有链接。
https://gustavorsantos.medium.com/exploratory-data-analysis-with-python-part-1-b6248b28dc85
下面的列表是这个模板所遵循的,并且我们覆盖了粗体上的项目符号:
- 库导入和加载数据
- 检查数据类型
- 寻找空值或缺失值
- 描述性统计概要
- 单变量分析
- 相关
- 多变量分析
我们继续吧。
单变量分析
单变量分析是一次只看一个变量的任务。这很重要,因为这样我们可以捕捉一些潜在的问题,如异常值,任何我们出于某种原因没有发现的空值,还可以了解变量是如何根据值分布的。
因此,知道变量 total_bill、tip 和 size 是数字,就可以用 boxt、violin 或 histogram 之类的图来分析它们。我个人喜欢箱线图,因为它们已经显示了分位数和异常值。
# Boxplot of numerical variablesfig, g = plt.subplots(1, 3, figsize=(20,9))g1 = sns.boxplot(data=df, y='total_bill', color='royalblue', ax=g[0])
g1.set_title('Boxplot of Total_Bill', size=15)g2 = sns.boxplot(data=df, y='tip', color='coral', ax=g[1])
g2.set_title('Boxplot of Tip', size=15)g3 = sns.boxplot(data=df, y='size', color='gold', ax=g[2])
g3.set_title('Boxplot of Size', size=15);

数字变量的箱线图。图片由作者提供。
正如我们在统计数据描述部分所指出的,在这里我们可以确认存在异常值,它们正在拉高平均值。总账单和小费见较长的胡须。
查看分类变量,柱状图可用于显示每个类别的计数,让数据科学家清楚地了解哪些变量出现的频率更高。
# Countplots of categorical variables
fig, g = plt.subplots(1, 4, figsize=(28,9))g1 = sns.countplot(data=df, x='sex', color='royalblue', ax=g[0])
g1.set_title('Countplot of Sex', size=15)g2 = sns.countplot(data=df, x='day', color='coral', ax=g[1])
g2.set_title('Countplot of Day', size=15)g3 = sns.countplot(data=df, x='time', color='gold', ax=g[2])
g3.set_title('Countplot of Time', size=15)g4 = sns.countplot(data=df, x='smoker', color='gold', ax=g[3])
g4.set_title('Countplot of Smoker', size=15);

男性、周末、晚餐、不吸烟者:引导观察的类别。图片由作者提供。
观察到更多的男性,周末,晚餐时间和不吸烟的人。因此,我们可以看到,通过几行代码,我们可以从数据中获得很好的理解和一些见解。
相关
相关性是两个数值变量之间线性关系的统计度量。所以,既然我们要做多元分析,我相信计算相关性是很有趣的。这个任务可以推动你的努力。
对于这个小小的“玩具”数据集,不会有太大的不同,因为检查所有的组合很容易。然而,从大处着眼:如果你有许多变量,多重共线性,最好主要检查(如果不是只检查)那些相关性更高的变量。
此外,该部分还有一个警告。许多人只是进行相关性测试,而没有事先进行必要的检查。记得我说过相关性是一个统计测试,所以它需要检查先决条件。
人们必须检查被测数据是否正态分布。如果是,默认方法(Pearson)就可以了。如果数据不是正态分布,Spearman 方法更合适。
import scipy.stats as scs# Function to test normal distribution with an alpha value of 0.05def test_normality(data):
stat, p = scs.shapiro(data)
if p < 0.05:
print(f'p-Value: {p}. Not normaly distributed.')
else:
print(f'p-Value: {p}. Normaly distributed.')**# Tests**
for col in df.select_dtypes(include=['int', 'float']).columns:
test_normality(df[col])**[OUT]:**
p-Value: 3.3245434183371003e-10\. Not normaly distributed.
p-Value: 8.20057563521992e-12\. Not normaly distributed.
p-Value: 3.047565374957781e-20\. Not normaly distributed.
由于我们的测试显示了非正态分布,那么让我们使用 Spearman 的方法进行相关分析。
# Data not normal, use Spearmancorrelation_calc = df.corr(method='spearman')
sns.heatmap( correlation_calc , annot=True, cmap='RdBu');

相关计算。图片由作者提供。
请注意,小费更多地与总账单相关,而不是与聚会的规模相关,这很有道理。我们可以五个人一起去餐馆,每个人喝一杯汽水。小费会很少。或者我们可以两个人一起去吃一顿丰盛的晚餐,这样可以得到更多的小费。
多变量分析
现在,我们对相关性有了更多的了解,我们可以开始探索每个变量如何与我们的目标小费相关,假设它是为了预测小费的数量。
让我们从 tip 和 other 之间的双变量分析开始。
# Scatter plots Numericalfig, g = plt.subplots(1, 2, figsize=(20,9))g1 = sns.scatterplot(data=df, x='total_bill', y='tip', color='royalblue', ax=g[0])
g1.set_title('Tip vs. Total Bill', size=15)g2 = sns.scatterplot(data=df, x='size', y='tip', color='coral', ax=g[1])
g2.set_title('Tip vs. Size', size=15);

散点图。图片由作者提供。
total_bill 金额具有中度到高度相关性,我们可以看到其中的线性关系。尺寸则更为中低。你能理解total_bill比大小更适合可能的线性回归吗?
如果我们画一条直线,试图通过尽可能多的点,哪个变量会“得到”更多的点?如果你同意它是左边的图形,那么你就明白了更高的相关性意味着更适合线性回归。
当对照你的目标检查分类变量时,一个好主意是聚集数据,然后绘图。
# Day and Tipsmean_by_day = df.groupby('day').tip.mean().reset_index()
px.bar(mean_by_day, x='day', y='tip')

周日是平均小费最多的一天。图片由作者提供。
其他可能的检查是多元散点图。使用plotly.express库很容易做到这一点。
# Triple variable Scatter plotspx.scatter(df, x='total_bill', y='tip', facet_col='sex')

更多的男性观察和来自男性的更高数量。图片由作者提供。

不吸烟的人也给更多的小费。图片由作者提供。
还有很多其他的检查可以做,但是我想你已经明白了。
多变量分析的想法是检查其他变量如何与您的目标相关,以及它如何影响您的模型。
寻找可以帮助你解释一种行为和预测你的目标的最佳结果的变量。
结论
好了,现在就这样了。
我想通过 EDA 的“模板”这一概念向您展示的是,您必须执行一些检查,以帮助您理解数据、数据是如何分布的、如何在建模之前格式化和清理数据,并向您展示哪些变量有助于您解释您试图预测的行为。
所以,概括一下:
- 加载数据
pd.read_csv() - 检查形状
df.shape,数据类型df.dtypes, df.info() - 寻找空值
df.isnull().sum() - 使用
df.dropna()或df.fillna()删除或填充 NA 值 - 使用
df.describe()显示统计数据,帮助您理解数据的分布并提取一些有用的见解。 - 使用一些箱线图进行单变量分析。其他好的选择是直方图,QQ 图,小提琴图。
- 检查数据的正常性
- 检查数值变量的相关性。
- 检查变量与目标变量的关系。此任务将帮助您消除不一致并选择最佳变量。
在你走之前
检查这些参考资料。
熊猫文档: read_csv , dropna , fillna 。
如果你对这些内容感兴趣,请关注我的博客。
https://medium.com/gustavorsantos
利用 Python 的 Matplotlib 和 Seaborn 库中的高级可视化进行探索性数据分析
原文:https://towardsdatascience.com/exploratory-data-analysis-with-some-cool-visualizations-in-pythons-matplotlib-and-seaborn-library-99dde20d98bf?source=collection_archive---------6-----------------------

霍利·曼达里奇在 Unsplash 上拍摄的照片
探索国际足联数据集
探索性数据分析对于正确理解数据集非常重要。甚至对机器学习也很重要。我之前发表过几个探索性的数据分析项目。但是这次我要带一个大的数据集。让我们看看进展如何。
我将使用来自 Kaggle 的名为 FIFA dataset 的公共数据集。这里提到的是用户许可。
请随意从以下链接下载数据集:
https://github.com/rashida048/Datasets/blob/master/fifa.csv
首先导入包和数据集:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
df = pd.read_csv("fifa.csv")
这个数据集总共有 80 列。原始 Kaggle 数据集有 89 列,我已经删除了 9 列,并上传到上面提供的链接中。所以,你可以从一个更干净的数据集开始。
但是在开始,我删除了一个不必要的列:
df = df.drop(columns="Unnamed: 0")
现在,这些是剩下的列:
df.columns
输出:
Index(['sofifa_id', 'player_url', 'short_name', 'long_name', 'age', 'dob', 'height_cm', 'weight_kg', 'nationality', 'club_name', 'league_name', 'league_rank', 'overall', 'potential', 'value_eur', 'wage_eur', 'player_positions', 'preferred_foot', 'international_reputation', 'weak_foot', 'skill_moves', 'work_rate', 'body_type', 'real_face', 'release_clause_eur', 'player_tags', 'team_position', 'team_jersey_number', 'loaned_from', 'joined', 'contract_valid_until', 'nation_position', 'nation_jersey_number', 'pace', 'shooting', 'passing', 'dribbling', 'defending', 'physic', 'gk_diving', 'gk_handling', 'gk_kicking', 'gk_reflexes', 'gk_speed', 'gk_positioning', 'player_traits', 'attacking_crossing', 'attacking_finishing', 'attacking_heading_accuracy', 'attacking_short_passing', 'attacking_volleys', 'skill_dribbling', 'skill_curve', 'skill_fk_accuracy', 'skill_long_passing', 'skill_ball_control', 'movement_acceleration', 'movement_sprint_speed', 'movement_agility', 'movement_reactions', 'movement_balance', 'power_shot_power', 'power_jumping', 'power_stamina', 'power_strength', 'power_long_shots', 'mentality_aggression', 'mentality_interceptions', 'mentality_positioning', 'mentality_vision', 'mentality_penalties', 'mentality_composure', 'defending_marking', 'defending_standing_tackle', 'defending_sliding_tackle', 'goalkeeping_diving', 'goalkeeping_handling', 'goalkeeping_kicking', 'goalkeeping_positioning', 'goalkeeping_reflexes'], dtype='object')
正如你所看到的那么多变量!你可以从这个数据集中尽可能多的分析出一本大书。但这是一篇博客文章。我只能把我的分析限制在一个范围内。
此外,有太多的变数,我不太明白,因为我不太熟悉足球。但这也是数据科学家生活的一部分。在不了解所有特性的情况下,您可能会创建一些有趣的可视化和分析。
我们开始吧。
第一个可视化是关于工作率和工资之间的关系。该图是散点图和线图的组合。
plt.figure(figsize=(10, 6))#Scatter plot
ax = sns.scatterplot(x ='work_rate',
y = df['wage_eur'],
hue = "league_rank",
data = df,
palette = ["green", "red", "coral", "blue"],
legend="full",
alpha = 0.4
)#Getting the max wage for each work rate
max_wage_eur = df.groupby("work_rate")["wage_eur"].max()#Making a line plot of max wages
sns.lineplot(data = max_wage_eur,
ax = ax.axes,
color="grey")ax.tick_params(axis= "x", rotation=90)
plt.xlabel("Work Rate")
plt.ylabel("Wage EUR")
plt.title("Relationship between work rate and wage by league rank", fontsize = 18)
plt.show()

作者图片
有一个国籍栏。看到这方面的一些分析会很有趣。
这个数据集中有多少国籍的玩家?
len(df['nationality'].unique())
输出:
149
民族词汇云有助于理解哪些民族占主导地位。为了做到这一点,我们需要加入所有的国籍,然后做一个单词云。
nationality = " ".join(n for n in df['nationality'])from wordcloud import WordCloud
plt.figure(figsize=(10, 10))
wc = WordCloud().generate(nationality)
plt.imshow(wc, interpolation='bilinear')
plt.axis('off')
plt.show()

作者图片
这意味着我们有更多来自英国、法国、德国、西班牙、意大利、荷兰等国家的球员。
我很好奇玩家数量排名前 20 的国家。使用熊猫图书馆的 value_counts 函数,我得到了所有民族的值计数,并取前 20 名来找到前 20 名。熊猫值计数功能自动排序的价值。
nationality_count = df['nationality'].value_counts()
nationality_count[:20]
输出:
England 1627
Spain 1051
France 958
Argentina 867
Italy 795
Germany 701
Colombia 543
Republic of Ireland 460
Netherlands 419
Mexico 416
Brazil 416
Chile 411
Sweden 398
Saudi Arabia 362
United States 342
Poland 342
Turkey 341
Portugal 337
Korea Republic 328
Scotland 323
Name: nationality, dtype: int64
足球运动员在他们的生活中有一定的活跃时间。年龄分布在这里:
df['age'].hist()
plt.title("Distribution of age of the players")

作者图片
这将是一个好主意,检查是否分布匹配的顶级国籍,联赛和俱乐部的球员人数的年龄分布。首先,让我们根据顶级国家、俱乐部和联赛制作数据框架:
top_20_nat= nationality_count.index[:20]
df_nationality = df[df.nationality.isin(top_20_nat)]clubs_count = df['club_name'].value_counts()
top_20_club = clubs_count[:20].index
df_clubs = df[df.club_name.isin(top_20_club)]league_count = df['league_name'].value_counts()
top_20_leagues = league_count[:20].index
df_league = df[df.league_name.isin(top_20_leagues)]
我想把年龄的分布放在同一个图中,以便恰当地比较分布情况:
pd.DataFrame({'nationality': df_nationality['age'],
'club': df_clubs['age'],
'league': df_league['age']}).hist(bins = 15,
figsize=(12, 6),
grid=False,
rwidth=0.9,
sharex=True,
sharey=True)
plt.show()

作者图片
看,俱乐部的年龄分布这么低。也许他们是儿童俱乐部。有趣的是,基于数字的更大的俱乐部是儿童俱乐部。
我还检查了所有三个组的身高和体重的分布,得到了相似的分布。请随意自己检查。
让我们来看一个图表,使用我们创建的前 20 个国籍的数据框架,对每个国籍的工资进行点估计。
ax = sns.catplot(x = 'nationality', y = 'wage_eur', data = df_nationality,
hue = 'preferred_foot', height=6, aspect=2,
capsize=0.2, kind='point')
plt.xlabel("Nationality", fontsize=12)
plt.ylabel("Wage EUR", fontsize=12)
plt.title("Point Estimation of Wages per Top 20 Nationalities", fontsize=20)
plt.xticks(rotation = 60, fontsize=13)
plt.show()

作者图片
上面的工资显示了前 20 名国籍球员的工资。我们也有前 20 名俱乐部的信息。我们来看看俱乐部层面前 20 名国籍球员的工资。
fig, ax = plt.subplots(figsize=(16, 6), dpi=80)
sns.stripplot(x = "nationality", y = "wage_eur",
data=df_clubs, size = 7, ax=ax)
plt.tick_params(axis='x', which='major', labelsize=12, rotation=90)
plt.xlabel("Nationality")
plt.ylabel("Wage")
plt.title("Wages for Nationalities in Club Level")
plt.show()

作者图片
你可以在这个俱乐部群中看到一些新的国籍。同样在点估计图中,巴西位于顶部,巴西位于中间。
下一幅图将按国际声誉显示国家职位的工资。我将在这里使用另一个组合图来显示国家职位的平均工资,不同的颜色表示国际声誉。
为此,我们需要将 national_position 列的字符串值转换为 int 值。下面是创建字典的函数,其中键是 nation_position 列的值,值是每个值的唯一整数。
def make_dictionary(col):
dictn = {}
val = 0
for i in col.unique():
if i not in dictn:
dictn[i] = val+1
val += 1
return dictn
现在,我们将创建一个名为 nation_position1 的新列,其中数据类型为整数:
new_nation_position= make_dictionary(df['nation_position'])
df['nation_position1'] = df['nation_position'].replace(new_nation_position)
让我们使用这个新的 national_position1 列进行绘图:
plt.figure(figsize=(15, 10))
ax = sns.scatterplot(x= df['nation_position'],
y = df['wage_eur'],
hue = df['international_reputation'],
legend = "full",
data = df,
x_jitter = 1000)ax = sns.regplot(x= 'nation_position1',
y = df['wage_eur'],
data = df,
ax=ax.axes,
x_estimator=np.mean
)plt.xlabel("Nation Position")
plt.ylabel("Wage EUR")
plt.title("Wages for National Position by International Reputation", fontsize=18)
plt.show()

作者图片
你可以看到每个国家职位的平均工资。内部声誉越高,工资越高。
我必须承认,我不能完全理解这个数据集中的所有变量。例如,有一个名为“value_uer”的特性和另一个名为“wage_eur”的特性。他们有关系吗?一些图片可能有助于理解这一点。
下一个图探索了玩家的赌注和价值之间的关系,以及偏好的脚对这两个特征是否有任何影响。
在这个图中,我绘制了工资与数值的关系,其中颜色表示喜欢的脚。因为大部分的点都杂乱的在一个地方,我不得不放大它上面的散点图的一部分。两边是工资和价值的密度图。
plt.figure(figsize = (18, 10))
grid = plt.GridSpec(4, 4, wspace =0.3, hspace = 0.8)g1 = plt.subplot(grid[:, 0])
g2 = plt.subplot(grid[:2, 1:3])
g3 = plt.subplot(grid[2:, 1:3])
g4 = plt.subplot(grid[:, 3])g1.set_title("Distribution of Wage", fontsize=12)
sns.kdeplot(x = "value_eur", hue="preferred_foot",
vertical=True,
data=df, ax=g1)sns.scatterplot(x = "wage_eur", y = "value_eur",
hue = "preferred_foot1", alpha = 0.3,
palette=['coral', 'blue'],
data = df, ax=g2)
g2.set_xlim(0, 50000)
g2.set_ylim(0, 0.05*1e8)
g2.legend(['Left', 'Right'])sns.scatterplot(x = "wage_eur", y = "value_eur",
hue = "preferred_foot", alpha = 0.3,
palette=['coral', 'blue'],
data = df, ax=g3)g4.set_title("Distribution of Value", fontsize=12)
sns.kdeplot(x = "value_eur", hue="preferred_foot",
vertical=True,
data=df, ax=g4)
plt.show()

作者图片
身高、体重和体型会影响球员的表现或价值观吗?身高/体重和内部口碑有关联吗?这个图表明。
plt.figure(figsize=(12, 8))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 8))
#fig.suptitle("International Reputation and Value with Height and Weight, and Body Type")sns.scatterplot(x = "height_cm", y = "weight_kg",
hue = "body_type1",
alpha = 0.5, size = "value_eur", sizes = (60, 300),
palette=['green','orange','dodgerblue'],
data = df.sample(frac=0.25), ax=ax1)ax1.set_ylim([50, 100])
ax1.set_xlim([160, 200])
ax1.set_title("Height vs Weight By Body Type and Value",fontsize=18)
ax1.set_xlabel("Height")
ax1.set_ylabel("Weight")sns.scatterplot(x = "height_cm", y = "weight_kg",
hue = "international_reputation",
alpha = 0.5, size = "value_eur", sizes = (60, 300), palette=['green','orange','dodgerblue', 'red', 'black'],
data = df.sample(frac=0.25), ax=ax2)ax2.set_ylim([50, 100])
ax2.set_xlim([160, 200])
ax2.set_title("Height vs Weight By International Reputation and Value",fontsize=18)
ax2.set_xlabel("Height")
ax2.set_ylabel("Weight")plt.show()

作者图片
这是国际声誉的价值计算。
df['international_reputation'].value_counts()
输出:
1 14512
2 1362
3 235
4 37
5 9
名声越高,分数越低。这是意料之中的。
我们有一个有趣的功能叫做“弱脚”。让我们看看弱脚是如何与被偏好脚隔离的价值联系起来的。我会用一个小提琴的情节来说明。
plt.figure(figsize=(15, 10))
sns.set()
_, ax = plt.subplots(figsize=(10, 7))
sns.violinplot(x="weak_foot",
y="value_eur",
hue = "preferred_foot",
data=df,
split=True,
bw=.4,
cut = 0.2,
linewidth=1,
palette=sns.color_palette(["green", "orange"]))
ax.set(ylim = (-0.1, 1*1e7))
plt.show()

作者图片
你可以看到分布变化很大。我使用了一个“ylim”参数来排除异常值。如果我包括所有的值,小提琴的形状不会显示出来。
接下来的剧情,我想探讨一下体型和射击的关系。用喜欢的脚把它们分开也是很好的。我将使用一个点图来添加组的点估计。这一点代表了这些团体的主要倾向。
_, ax = plt.subplots(figsize=(10, 8))
sns.despine(bottom=True, left=True)sns.stripplot(x = "shooting",
y = "body_type",
hue = "preferred_foot",
data = df,
dodge = 0.8, zorder=1)sns.pointplot(x = "shooting",
y = "body_type",
hue = "preferred_foot",
data=df, dodge=0.5, join=False,
palette="dark", markers="d",
scale=0.75, ci=None)
handles, labels = ax.get_legend_handles_labels()

作者图片
所有组的分数估计看起来没有太大的不同。
我们没有过多地探讨民族立场。另外,另一个有趣的特征叫做心态攻击。我们将看到他们之间的关系,也包括联赛排名作为一个“色调”参数。在此之前,我们需要将 nation_position 转换为一个数值变量。我使用前面提到的 make_dictionary 函数创建了一个字典,并最终创建了一个名为“nation_position1”的新列,用整数替换了原来的字符串值。
nat_dictn = make_dictionary(df['nation_position'])df['nation_position1'] = df['nation_position'].replace(nat_dictn)
这是该图的代码块。“lmplot”和“regplot”在此图中组合在一起:
ax = sns.lmplot(x = "nation_position1",
y = "mentality_aggression",
data = df, hue = "league_rank", fit_reg = False, height = 5, aspect = 2.2)
sns.regplot(x = "nation_position1",
y = "mentality_aggression",
data = df, scatter=False, ax=ax.axes[0, 0], order = 3)
plt.ylabel("Mentality Aggression")
plt.xticks(list(range(1,30)), list(df['nation_position'].unique()))
plt.title("Relationship Between Nation Position and Mentality Aggression", fontsize=18)
plt.xlabel("Nation Position", fontsize=14)
plt.ylabel("Mentality Aggression", fontsize=14)
plt.show()

作者图片
你可以看到许多数据是“南”的联赛排名第四。但另一方面,这提供了一个关于民族立场的心理攻击的好主意。
当我们有这么大的数据集和许多连续变量时,我发现做一个配对图很有帮助。它给出了同一图中变量之间的分布和几种关系。这是一个配对图:
sns.set(color_codes=True)
plt.rcParams["axes.labelsize"] = 20
g1 = sns.PairGrid(df.sample(frac = 0.2), vars = ['pace', 'shooting',
'passing', 'dribbling', 'defending', 'attacking_crossing',
'attacking_finishing', 'attacking_heading_accuracy'],
hue = 'preferred_foot')
g1.map_lower(sns.regplot)
g1.map_diag(plt.hist, alpha=0.7)
g1.map_upper(sns.kdeplot, shade=True)
g1.add_legend(title='Foot', fontsize=20)
for axes in g1.axes.flat:
axes.set_ylabel(axes.get_ylabel(), rotation=0, horizontalalignment='right')

作者图片
我想探索一下进攻和防守之间的关系。我想在这次探索中也包括身体类型和心理攻击性:
df2 = df[['attacking_heading_accuracy', 'defending', 'body_type', 'mentality_aggression']]df_encircle=df2[(df2['attacking_heading_accuracy'] >=50) & (df2['attacking_heading_accuracy'] <=70)].dropna()
df_encirclefrom scipy.spatial import ConvexHullplt.figure(figsize=(18, 8))
ax = sns.scatterplot(x = "attacking_heading_accuracy", y = "defending",
hue = "body_type",
alpha = 0.5, size = "mentality_aggression", sizes = (20, 300),
data = df.sample(frac=0.10))def encircle(x, y, ax=None, **kw):
if not ax:
ax=plt.gca()
p=np.c_[x, y]
hull = ConvexHull(p)
poly=plt.Polygon(p[hull.vertices, :], **kw)
ax.add_patch(poly)
encircle(df_encircle.attacking_heading_accuracy, df_encircle.defending,
ec = "k", fc="gold",
alpha = 0.1)encircle(df_encircle.attacking_heading_accuracy, df_encircle.defending,
ec = "firebrick", fc="None",
linewidth = 1.5)plt.xlabel("Attacking Heading Accuracy", fontsize=12)
plt.ylabel("Defending", fontsize=12)
plt.title("Defending vs Attacking Heading Accuracy")
plt.show()

作者图片
你可能会想,为什么我圈了一些区域。如果你注意到以前在配对图中攻击方向准确性的分布,大多数群体位于 50 到 70 的范围内。我只是想把那个区域圈起来,这样我们就可以把注意力集中在那个区域。
下面的图将显示国家位置和运动速度之间的关系。这次我结合了箱线图和带状图。
fig, ax = plt.subplots(figsize=(14, 8))ax = sns.boxplot(x = 'nation_position', y = "movement_sprint_speed",
data = df)
ax.tick_params(rotation=90, labelsize=18)
ax = sns.stripplot(x = 'nation_position', y = "movement_sprint_speed", data=df)
plt.xlabel("Nation Position", labelpad = 16, fontsize=24)
plt.ylabel("Movement Sprint Speed", labelpad = 16, fontsize=24)
plt.title("Nation Position vs Movement Sprint Speed", fontsize=32)
plt.show()

作者图片
接下来,violin plot 和 stripplot 的组合将绘制单词速率与技能曲线。
fig, ax = plt.subplots(figsize=(12, 6))
ax=sns.violinplot(x = "work_rate", y = "skill_curve", hue="preferred_foot",
data=df, inner=None, color = "0.4")
ax = sns.stripplot(x = "work_rate", y = "skill_curve", alpha = 0.6, data=df)
ax.tick_params(rotation=90, labelsize=12)
plt.xlabel("Work Rate", fontsize = 12)
plt.ylabel("Skill Curve", fontsize = 12)
plt.title("Skill Curve for Work Rate", fontsize=24)
plt.show()

作者图片
在我的下一个情节中,我将探索中值力量耐力。首先,根据中值力量耐力找到前 20 名的国籍,然后创建可视化。
d = df.groupby('nationality')['power_stamina'].agg([np.median])
d1 = d[:20].reset_index()
d1 = d1.rename(columns={"median": 'power_stamina'})fig, ax = plt.subplots(figsize=(14, 6))
ax.vlines(x=d1['nationality'], ymin=0, ymax = d1.power_stamina,
color = 'coral', alpha = 0.7, linewidth = 3)
ax.scatter(x = d1["nationality"], y = d1["power_stamina"],
s = 70, color = "firebrick")ax.set_ylabel("Median Power Stamina", fontsize = 12)
ax.set_xticklabels(d1.nationality, rotation=90)
ax.set_ylim(0, 80)
ax.set_title("Median Power Stamina in for top 20 nationality", fontsize=18)for row in d1.itertuples():
ax.text(row.Index, row.power_stamina+1, s = round(row.power_stamina),
horizontalalignment = 'center', verticalalignment='bottom', fontsize=14)
plt.show()

作者图片
这里有相当多的品种。但有趣的事实是,中值力量耐力最高的前 2 个国家不在我们之前看到的前 20 名国籍名单中。
结论
正如我之前提到的,可以用如此庞大的数据集创建一本大书。这是一篇博文。所以,我就此打住。我总是喜欢在探索性数据分析中包含一些预测性建模。但是我必须把它留到以后的文章中,因为我不想再写这篇文章了。我试图在这篇文章中包含一些很酷的可视化。希望这是有帮助的。
请随时在 Twitter 、脸书页面上关注我,并查看我的新 YouTube。频道。
更多阅读
R 中的探索性因子分析
原文:https://towardsdatascience.com/exploratory-factor-analysis-in-r-e31b0015f224?source=collection_archive---------0-----------------------
数据科学家
做中学

作者图片
因子分析是一种统计方法,用来从称为因子的观测变量中寻找一些称为因子的未观测变量。
- 这种方法的开端被称为探索性因素分析(EFA)。
- 因子分析的另一种变体是验证性因子分析(CFA ),本文不做探讨。
因素分析被用于统计分析的许多领域,如市场营销、社会科学、心理学等等。
加载包(库)
我们需要的库如下:psych、corrplot、psynch等等。
**library**(psych)
**library**(corrplot)
**library**("psych")
**library**(ggplot2)
**library**(car)
数据概述
从服务器获取数据
我们首先需要从服务器获取样本数据。这是一个原始数据集,所以每行代表一个人的调查。
# Dataset
url <- “[https://raw.githubusercontent.com/housecricket/data/main/efa/sample1.csv](https://raw.githubusercontent.com/housecricket/data/main/efa/sample1.csv)"
data_survey <- **read.csv**(url, sep = “,”)
描述数据
在进行任何分析之前,我们还会查看数据集。
**describe**(data_survey)

我们使用dim函数来检索数据集的维度。
**dim**(data_survey)

清理数据
在我们的数据框中,第一列中有一个 ID 变量。因此,我们可以在列索引中使用一个-1来删除第一列,并将我们的数据保存到一个新对象中。
dat <- **data_survey**[ , -1]
**head**(dat)

相关矩阵
我们还应该看看我们的变量之间的相关性,以确定因素分析是否合适。
datamatrix <- **cor**(dat[,c(-13)])
**corrplot**(datamatrix, method="number")

数据的可分解性
X <- data[,-c(13)]
Y <- data[,13]
KMO
凯泽-迈耶-奥尔金(KMO)用来衡量抽样充分性是一个更好的衡量因素。
**KMO**(r=cor(X))

根据 Kaiser(1974 年)的指南,确定样本数据可分解性的建议临界值是KMO ≥ 60。总 KMO 为0.83,表明基于该测试,我们可以进行因子分析。
巴特利特球形试验
**cortest.bartlett**(X)

小数值(8.84e-290 < 0.05) of the significance level indicate that a factor analysis may be useful with our data.
**det**(**cor**(X)

We have a positive determinant, which means the factor analysis will probably run.
The Number of Factors to Extract
刮板导向器
**library**(ggplot2)fafitfree <- **fa**(dat,nfactors = **ncol**(X), rotate = "none")
n_factors <- **length**(fafitfree$e.values)
scree <- **data.frame**(
Factor_n = **as.factor**(1:n_factors),
Eigenvalue = fafitfree$e.values)
**ggplot**(scree, **aes**(x = Factor_n, y = Eigenvalue, group = 1)) +
geom_point() + geom_line() +
**xlab**("Number of factors") +
**ylab**("Initial eigenvalue") +
**labs**( title = "Scree Plot",
subtitle = "(Based on the unreduced correlation matrix)")

平行分析
我们可以使用 nFactors 包(Raiche & Magis, 2020 )中的parallel()函数来执行并行分析。
parallel <- fa.parallel(X)

## Parallel analysis suggests that the number of factors = 4 and the number of components = 3
进行因素分析
用 fa 法进行因子分析
fa.none <- fa(r=X,
nfactors = 4,
# covar = FALSE, SMC = TRUE,
fm=”pa”, # type of factor analysis we want to use (“pa” is principal axis factoring)
max.iter=100, # (50 is the default, but we have changed it to 100
rotate=”varimax”) # none rotation
print(fa.none)

用因子分析法进行因子分析
factanal.none <- factanal(X, factors=4, scores = c("regression"), rotation = "varimax")
print(factanal.none)

图形因子加载矩阵
fa.diagram(fa.none)

回归分析
所有行的分数
head(fa.var$scores)

标记数据
regdata <- cbind(dat[“QD”], fa.var$scores)#Labeling the data
names(regdata) <- c(“QD”, “F1”, “F2”,
“F3”, “F4”)
head(regdata)

将数据拆分为训练集和测试集
#Splitting the data 70:30
#Random number generator, set seed.
set.seed(100)
indices= sample(1:nrow(regdata), 0.7*nrow(regdata))
train=regdata[indices,]
test = regdata[-indices,]
使用训练数据的回归模型
model.fa.score = lm(Satisfaction~., train)
summary(model.fa.score)

我们的模型方程可以写成:Y = 3.55309 + 0.72628 x F1 + 0.29138 x F2 + 0.06935 x F3 + 0.62753 x F4
检查 vif
vif(model.fa.score)

检查测试数据集中模型的预测
#Model Performance metrics:
pred_test <- predict(model.fa.score, newdata = test, type = “response”)
pred_test
test$QD_Predicted <- pred_test
head(test[c(“QD”,”QD_Predicted”)], 10)

今天,我们回顾了因子分析的初步步骤,包括检查数据和因子分析所需的假设,以及如何确定要保留的因子数量。
很简单,对吧?
探索性因子分析与主成分分析:从概念到应用
原文:https://towardsdatascience.com/exploratory-factor-analysis-vs-principal-components-from-concept-to-application-b67bbbdb82c4?source=collection_archive---------6-----------------------
如何用探索性因子分析减少参数

艾萨克·史密斯在 Unsplash 上拍摄的照片
在数据科学中,我们经常想要测量社会经济地位(SES)等变量。有些变量有很多参数(或项目),例如,SES 可以基于收入、教育等进行测量。然后,为了进行分析,通常通过主成分分析(PCA)将参数的数量减少到更少的成分。然而,我们将看到为什么一些变量不能通过主成分分析减少,我们将学习如何使用探索性因素分析对我们有利。
全民教育和 PCA 的区别
模型差异
这两种方法都用于将参数数量减少到更少的变量。此外,这两种方法都假设参数的方差分为特定方差、公共方差和误差方差。
在主成分分析中,当我们保留一个成分时,我们同时考虑特定方差和公共方差。而在全民教育中,我们只考虑普通方差。看到下图,我们可以认为 A 是特定方差,B 是普通方差,C 是误差方差。在 PCA 中我们用 A 的+ B,而在 EFA 中我们只用 B。

三个项目的方差及其与一个假设因素的关系的例子。图片作者。
主成分分析基于形成模型,其中成分的变化基于项目反应的变化(即收入水平将影响社会经济地位)。而全民教育是基于反思模型,其中项目的变化是基于一个结构的变化(即一个人的幸福会改变他们对项目的反应,而不是相反)。我们可以从下图中看到这种表示。

PCA 和 EFA 的区别。图片作者。
也就是说,全民教育的常见应用是测量心理变量。例如,如果你想衡量一个人的幸福水平,我们将只使用共同方差,因为该工具的项目试图衡量他们的共同点(即幸福水平)。
PCA 主要有三个主要步骤。
- 计算协方差矩阵
- 计算特征值和特征向量
- 组件的旋转
在全民教育中,我们有:
- 数据充分性的验证
- 协方差/相关矩阵的计算(因子提取)
- 选择要保留的因素
- 因素轮换
因为这里有很多关于 PCA 的关于数据科学的帖子,所以从现在开始我将把重点放在 EFA 上。在下一节中,我将描述 EFA 的每个步骤。
探索性因素分析步骤
数据充分性
我们通常使用两个测试来衡量我们的数据是否足以进行全民教育。
巴特利特球形度试验
该检验验证了变量在总体中不相关的假设。因此,零假设是相关矩阵等于单位矩阵。如果相关矩阵等于单位矩阵,我们就不能进行全民教育,因为变量之间没有相关性。该测试背后的统计分析如下:
χ=-[(n-1)-(2v+5)/6]ln | R |
其中
n 是样本大小
v 是变量个数
|R|是相关矩阵的行列式
在文献中,我们可以看到,如果显著性水平等于p0.05,这意味着我们可以进行全民教育。
凯泽-迈耶-奥尔金(KMO)
验证可能由因素引起的项目差异的比例。该测试验证逆相关矩阵是否接近对角矩阵,比较线性相关的值和部分相关的值。

其中:
rJK = Xj 和 Xk 的相关系数
jk =和 Xk 之间的相关系数,控制其他 x。
低于 0.5 的值被认为是不可接受的,介于 0.50 和 0.70 之间的值是一般的,介于 0.70 和 0.80 之间的值是好的,高于 0.80 的值被认为是伟大的,高于 0.90 的值是优秀的。
因子提取
在全民教育中,我们有许多提取方法可供选择。如果数据呈正态分布,建议使用最大似然法,因为它可以实现各种拟合优度指数、因子负荷的显著性检验、置信区间的计算等。但是,如果数据不符合正态分布,建议使用主轴分解。
保留因素
有很多方法可以用来选择项目的数量,我将主要关注其中的三种。
a)凯泽准则:它提出如果一个因子的特征值大于 1.0,我们应该保留那个因子。背后的逻辑是:如果一个因子的特征值= 3.0,那就意味着这个因子解释了 3 个项目相同的方差。
注意,这个标准被认为高估或低估了因素的数量。我不建议单独使用它。
b) Scree 图:我们评估特征值的幅度何时大幅下降。这种方法也有一些局限性,因为它会产生不明确的结果,并且容易受到主观解释的影响。
c)平行分析:计算样本特征值和随机数据特征值。当真实数据的特征值的数量大于来自模拟数据的特征值的数量时,选择因子的数量。这种方法通常效果很好。
使用 R 的全民教育
现在我们将看到一个在 r 中计算 EFA 的教程。我们将使用包psych来进行计算。此外,我们将使用积极和消极影响量表,它由关于消极和积极情绪的项目组成。
读取数据
我们将使用 R 函数 read.delim 读取数据集
*Affects <- read.delim(“[https://raw.githubusercontent.com/rafavsbastos/data/main/Afetos.dat](https://raw.githubusercontent.com/rafavsbastos/data/main/Afetos.dat)")*
我们为数据框选择的名称是 ffets 。使用View(Affects)我们将看到我们的数据集:

装载心理包**
要开始操作数据,我们需要下载 psych 包。只需运行以下代码。
*install.packages(“psych”)*
好的。现在包在你的电脑上。现在,我们需要让它开始与library(psych)一起工作。
数据充分性
为了看看用我们的数据做 EFA 是否可以,我们将首先计算 Bartlett 的球度检验。
首先,我们计算相关矩阵:
*correlation <- cor(Affects)*
然后,我们用cortest.bartlett(correlation, n=1033)来计算 Bartlett 的检验,其中第二个参数是样本量。我们将得到以下输出:

显著性水平小于 0.05,这意味着我们可以继续进行全民教育(如果我们假设低于 0.05 的值表明我们的数据充分)。
现在我们来计算 KMO。使用以下代码KMO(Affects)。

我们可以看到样本充分性的总体衡量标准(MSA)是 0.94,这意味着它是优秀的。我们还可以在总体 MSA 下面看到每个项目的 MSA。基于这些结果,我们可以继续进行全民教育。
探索性因素分析
提取和保留因子
仅使用一行代码,我们就能够提取因子的数量,并选择要保留的因子。
*fa.parallel(Affects,fm=”pa”, fa=”fa”, main = “Parallel Analysis Scree Plot”, n.iter=500)*
其中:
- 第一个论点是我们的数据框架
fm是提取方法;我们使用主轴分解法(“pa”)fa = “fa”;如果我们写“fa ”,我们就是在做 EFA。如果我们写“pc ”,我们将做一个 PCA。因为我们使用的是反思模型,所以我们要做一个 EFA。main=我们形象的标题。n.iter=我们希望进行的互动次数。
我们将得到以下输出:

它告诉我们,我们需要保留基于平行分析的 3 个因素。同一行代码的另一个输出如下图所示:

我们可以看到我们的数据特征值(蓝色)和模拟的数据特征值(红色)。查看红色和蓝色特征值之间的交叉点,注意一些随机数据的第四个特征值比我们数据的第四个特征值解释了更多的方差。然而,如果我们考虑凯泽标准(图中的黑线),我们将只提取两个因素。因为我们希望保留尽可能少的因素,所以我们将只对两个因素进行分析。
在我们跳到下一节之前,我想给大家展示一下提取特征值时 PCA 和 EFA 的区别。
*fa.parallel(Affects, fm = "pa", cor = "cor", fa= "both", main = "Parallel Analysis Scree Plot", n.iter=500)*

上图中,PC 代表主成分,FA 代表因子分析。如果我们在 PCA 中使用 Kaiser 标准,我们将保留 3 个因子,而如果我们使用平行分析标准,我们将保留 2 个因子。我们还可以注意到,PCA 的特征值比 EFA 的特征值高,这是因为我之前说过,PCA 考虑了共同的和特定的方差,而 EFA 只考虑共同的方差。
项目信息
我们可以看到哪些项目解释了更多的因素,基于因素负荷,共性。此外,通过以下代码,我们可以看到双因素模型的解释方差和拟合优度指数:
*fit <- fa(Affects, nfactors = 2, n.obs = 1033 , rotate = “oblimin”, residuals = TRUE, fm = “pa”)print(fit, sort = TRUE)*
会打印出:

在第一个表中,我们可以看到积极影响项目(AP)对第一个因素(PA1)的影响很大,而消极影响项目(an)对第二个因素(PA2)的影响很大。共性用 h2 表示。
在第一张表下面,我们有每个因素的解释方差的比例。积极影响因素(PA1)解释了 25%的数据方差,而消极影响因素(PA2)解释了 24%。
还计算了拟合指数的优度。虽然它们在验证性因素分析中被广泛使用,但它们在全民教育中的应用却有点神秘。很少有研究评估全民教育中拟合优度指数的使用,因此可能很难解释这部分数据。
为了获得因子加载的可视化表示,我们将使用函数fa.diagram(fit)。

我们还可以看到带有plot(fit)的轴中的要素的几何可视化。

在上图中,我们希望积极影响项目靠得更近,而远离消极影响项目。
鉴于目前为止的所有步骤,我们可以看到我们的措施提出了两个因素的结构,这是相同的,由先前的作者理论。此外,我们发现项目加载到它们的给定因子中,这些因子解释了方差的很大一部分。
我必须强调的是,虽然我们有统计方法来选择要保留的因子的数量,但是只有研究者才能选择哪种方法是最好的。这意味着选择保留因素的最佳方法的标准是开放的主观解释。此外,我提出了一个统计工具来分析心理数据,其中反射模型更合适。
接触
请随时通过以下方式联系我
**Gmail:rafavsbastos@gmail.com
咨询与合作网站:rafavsbastos.wixsite.com/website LinkedIn:linkedin.com/in/rafael-valdece-sousa-bastos/ Github:github.com/rafavsbastos
用探索性图形分析估计维数
原文:https://towardsdatascience.com/exploratory-graph-analysis-3d4f9c63e92f?source=collection_archive---------19-----------------------
来自网络心理测量文献的强大技术的总结和指南

探索性图形分析。作者图片
在心理学、教育学和行为科学中,我们使用量表/仪器来测量给定的结构(例如,焦虑;幸福)。为此,我们通常会有一份包含 X 个项目的调查问卷,并希望了解这些项目中潜在因素的数量。这通常通过探索性因子分析 (EFA)来完成,其中维度的数量通常通过检查特征值的模式来估计(参见我的特征值指南这里)。使用特征值的两种最常见的方法是 Kaiser-Guttman 特征值大于一规则和并行分析。然而,对于这些方法在估计维数方面的性能,已经提出了许多批评。
由于这些限制,Golino & Epskamp (2017)提出了一种新的估计量表维度的方法,称为探索性图形分析(EGA)。本文将简要总结 EGA 的最新发展,旨在传播这一方法。
探索性图形分析
网络心理测量方法最近在心理科学文献中得到了关注。这可能是由于对数据中观察到的相关性的理论解释发生了变化。传统上,如 EFA 所做的,心理测量模型假设潜在原因解释观察到的行为(即项目)。网络心理测量学等新兴领域为心理学研究提供了有前途的模型,因为它支持复杂性的理论观点,即它将心理属性视为动态和相互强化的观察行为系统。
传统心理测量中的一个典型潜变量与网络聚类中的一个典型潜变量之间存在联系。正如 Golino & Epskamp (2017)所说:
可以直接看出,如果潜在变量模型是真正的潜在因果模型,我们将期望网络模型中的指标以每个潜在变量的强连接聚类为特征。由于边对应于在对网络中的所有其他变量进行调节之后两个变量之间的部分相关系数,并且在对观察到的变量进行调节之后两个指标不能变得独立,因为它们都是由潜在变量引起的,所以两个指标之间的边强度不应该为零。
EGA 是一种不依赖于先验假设的探索性方法,因此,它们不需要研究者的任何指导。在 EGA 中,节点表示变量(即项目),边表示两个节点之间的关系(即相关性)。
在作者的第一篇论文中,EGA 是这样完成的:
- 估计可观测变量的相关矩阵。
- 图形最小绝对收缩和选择算子 ( 格拉索)估计用于获得稀疏逆协方差矩阵,正则化参数通过 EBIC 定义在 100 个不同的值上。
- walktrap 算法用于找出上一步中计算的部分相关矩阵的聚类数。
- 识别的聚类数量等于给定数据集中潜在因素的数量。
今天,可以估计一维性和多维性;我们可以用三角最大过滤图(TMFG)来代替glasso;并使用 walktrap 以外的算法(即 louvain)。
Golino 等人(2020)表明,EGA 方法的表现与最佳因子分析技术一样好。因为 EGA(TMFG)在一维结构和多维结构中都表现出了较好的精度,而 EGA 是总体上精度较高的方法之一。
估计稳定性项目和因素稳定性
估算 EGA 时,有一点需要考虑。首先,在一项研究中确定的维度数量可能会因其他样本和样本大小不同的研究而异。此外,一些项目可能在一项研究中聚集在维度 A 中,而在另一项研究中聚集在维度 B 中。正因为如此,克里斯腾森&戈利诺(2019)创造了自举 EGA。
r 教程
安装 EGAnet
为了进行分析,我们将使用 R 包EGAnet(CRAN; GitHub ),由 Golino 和 Christensen 制作。
首先,我们必须安装软件包(我们将使用 GitHub 版本,因为它是最新的)。
library(devtools)
devtools::install_github('hfgolino/EGAnet')
这将在您的设备上安装软件包。然后,我们将使用library(EGAnet)加载这个包。
读取数据
我们将使用来自开源心理测量项目的幽默风格问卷数据。我把它添加到了我的 GitHub 中,这样你就可以轻松下载了。
data <- read.delim("[https://raw.githubusercontent.com/rafavsbastos/data/main/HSQ.txt](https://raw.githubusercontent.com/rafavsbastos/data/main/HSQ.txt)"
增强型图形适配器
我们将使用以下代码运行 EGA:
ega.HSQ <- EGA(
data,
uni.method = "LE",
corr = "cor_auto",
model = "glasso",
algorithm = "walktrap",
plot.EGA = TRUE,
plot.type = "qgraph"
)
其中第一个参数是数据集(即我们的项目);第二个代表应该使用什么样的一维方法;第三个是要计算的相关矩阵的类型;第四个指示要使用的方法;第五是我们用的算法;第六如果我们想策划 EGA;第七,我们希望什么样的情节。
我们有以下输出:

我们可以看到一个清晰的四维“提取”,正如所料。
维度和项目稳定性
为了计算维度稳定性,我们将运行以下代码:
bootdata <- bootEGA(
data,
iter= 1000,
type = "resampling",
)
其中的参数是:
- 数据集。
- 一个整数,包含要从引导分析中生成的复本样本数。
- 参数或非参数方法。
注意,因为我们使用了一些默认参数,所以我们没有在这里指定它。在进行自己的分析之前,请查看软件包的文档!
现在我们将看到一些有用的信息输入:
bootdata$summary.table
其中输出为:

我们可以看到维数的中位数(median.dim),标准差(SE.dim),维数的置信区间(CI.dim),下 CI (Lower。Ci)和上 CI(上。CI),以及维数的较低分位数(较低。分位数)和上限(上限。分位数)。基于该输出,很明显,具有 4 个维度的模型是精确的(SE = 0.27),并且 4 个维度最有可能是量表的结构(给定 CI 3.47,4.53)。
现在输入bootdata$frequency。它为您提供了以下输出:

我们可以看到,4 个因素被复制了 924 次,而 5 个因素只被复制了 75 次,6 个因素被复制了一次。
现为物品稳定型:ic.HSQ <- itemStability(bootdata)。输出是一幅图像:

我们可以看到,项目在其给定的维度中有 80%到 100%的时间是重复的。
结束语
正如到目前为止所做的一些分析和最近的论文所示,EGA 可以向我们展示一种评估测量心理属性的工具的维度的准确方法。此外,作者在 EGAnet 中实现了一系列函数,为我们提供了关于维度和项目的有用信息。软件包仍在更新,结果加载更快,而且(可能)新功能正在制作中。
接触
请随时通过以下方式联系我:
电子邮箱:rafavsbastos@gmail.com
咨询与合作网站
参考
H.F. Golino,S. Epskamp,探索性图形分析:心理学研究中维度数估计的新方法,2017 年, PloS one , 12 (6),e0174035。
H.F. Golino,D. Shi,A. P. Christensen,L. E .,M. D. Nieto,R. Sadana,…和 A. Martinez-Molina,调查探索性图形分析和传统技术的性能,以确定潜在因素的数量:模拟和指导,2020 年,心理学方法。
A.P. Christensen 和 H. F. Golino,通过 Bootstrap 探索性图分析估计因素数量的稳定性:教程,2019 年。
探索性空间数据分析(ESDA)-空间自相关
原文:https://towardsdatascience.com/exploratory-spatial-data-analysis-esda-spatial-autocorrelation-7592edcbef9a?source=collection_archive---------36-----------------------
探索多伦多附近的 Airbnb 列表价格,并确定热/冷地区
在探索性数据分析(EDA)中,我们经常计算相关系数,并在热图中显示结果。相关系数衡量两个变量之间的统计关系。相关值表示一个参数的变化如何影响另一个参数,例如购买数量与价格。在建立模型之前,相关性分析是预测分析领域中一个非常重要的概念。
但是,我们如何衡量地理位置与空间数据集中的统计关系呢?传统的 EDA 和相关性分析忽略了位置特征,并将地理坐标视为类似于其他规则特征。探索性空间数据分析(ESDA)在基于位置的数据分析中变得非常有用。
空间自相关
ESDA 旨在通过空间聚类的正式统计测试来补充地理化,而空间自相关是这些测试的重要目标之一。空间自相关测量变量在空间上的相关性,即与图表上相邻变量的关系。值可以是
- 阳性:附近的病例相似或聚集,例如高-高或低-低(下图左图)
- 中性:相邻病例没有特定关系或随机,没有模式(下图中心图像)
- 否定:邻近案例不相似或分散,例如高-低或低-高(下图右图)

空间自相关的图解。摘自(Radil,2011 年)。
两种最常用的空间自相关测量方法是空间相似性和属性相似性。
空间相似性是通过量化(空间权重)位置对之间关系的相对强度来表示数据集的空间结构。邻居关系被定义为车的情况,主教的情况或女王(国王)的情况-见下图。顾名思义,这些非常简单和直观。您可以阅读基于邻接的空间权重以获得空间权重的更深入的解释。

邻近的定义http://www.lpc.uottawa.ca/publications/moransi/moran.htm
另一方面,通过空间滞后测量的属性相似性是通过比较属性值和它的邻居为每个属性获得的。空间滞后将对行进行归一化,并在每个加权邻域中取平均值。
加载数据集
我们使用 2020/12 年多伦多 Airbnb 房源作为我们的主要数据集。我们还获取了 Toronto neighborhoods 多边形数据集,这样我们就可以将我们的空间列表数据点映射到不同的区域。我们的目标是调查 Airbnb 平均列表价格在相邻区域(即热区域、冷区域或随机区域)之间是否存在任何空间相关性。
以下 Python 库用于操作地理数据:
- GeoPandas 用于存储和操作地理数据
- PySAL 用于空间计算和分析
- 叶子用于创建交互式地理可视化。
Airbnb 房源示例

Airbnb 房源原始数据
Airbnb 数据需要转换为 GeoDataFrame。我们需要用数据集定义 CRS(坐标参考系统)。我们使用EPSG:4326,它定义了标准(WGS84)参考椭球上的经纬度坐标。
data_gdf = gpd.GeoDataFrame(data_orig, geometry=gpd.points_from_xy(data_orig.longitude, data_orig.latitude), crs='EPSG:4326')
多伦多总共有 140 个社区。geometry 列中的坐标定义了邻域的多多边形边界。以下是一些例子:

多伦多街区
接下来,我们想找出每个街区的平均房价。给定列表的纬度/经度坐标和邻域的多边形边界,我们可以使用 GeoPanda 中提供的sjoin 函数轻松连接两个数据集。
temp = data_gdf[['price','geometry']]
listing_prices = gpd.sjoin(nbr_gdf, temp, op='contains')
listing_prices.head(5)

Airbnb 列出附近的价格
这里我们在sjoin中使用 op='contains',这意味着我们希望左边数据框(即邻域数据)中的记录在空间上包含右边数据框(即列表数据)中的地理记录。在结果数据集中,列index_right 是来自列表记录的索引,而price 是相应的列表价格。
我们取每个街区的平均标价,这是一个集合数据集:
nbr_avg_price = listing_prices['price'].groupby([listing_prices['neighbourhood']]).mean()
nbr_final = nbr_gdf.merge(nbr_avg_price, on='neighbourhood')
nbr_final.rename(columns={'price': 'avg_price'}, inplace=True) nbr_final.head()

我们使用 leav 创建了一个交互式 Choropleth 地图。当鼠标悬停在附近时,您可以看到平均列表价格。点击此处探索互动地图。

到目前为止一切顺利。虽然我们可以直观地看到哪些街区有价格更高或更低的房源,但考虑到不同大小和形状的不规则多边形,并不十分明显地看出是否有任何空间模式以及模式是什么。空间自相关是回答这个问题的好方法。
空间权重
如上所述,为了研究空间自相关,我们需要计算空间权重。我们使用 PySal 库进行计算。在这里,我们使用皇后邻接作为空间权重。
w = lps.weights.Queen.from_dataframe(nbr_final)
w.transform = 'r'
全球空间自相关
全局空间自相关决定了数据集中的总体聚类。如果标价的空间分布是随机的,那么我们在地图上不会看到任何相似值的聚集。用于评估全球空间自相关的统计数据之一是 Moran 的 I 统计数据。
y = nbr_final.avg_price
moran = esda.Moran(y, w)
moran.I, moran.p_sim (0.23066396579508366, 0.001)
莫兰的 I 值范围从-1 到 1,1 表示很强的空间自相关。在我们的示例中,我们的莫兰 I 值为 0.23 ,而 p 值为 0.001 ,这被认为在统计上具有高度显著性。因此,我们将拒绝全球空间随机性的零假设,而支持列表价格的空间自相关。我们也可以在莫兰的 I 图上看到这一点。
plot_moran(moran, zstandard=True, figsize=(12,6))
plt.tight_layout()
plt.show()

莫兰散点图
局部空间自相关
全局空间自相关分析非常有助于判断标价与其邻域之间是否存在正的空间自相关。但是它没有显示集群在哪里。空间关联的局部指标(LISA)旨在检测空间聚类并将它们分为 4 类:
- HH —高值旁边的高值
- LL —低旁边的低值
- 左侧—高值旁边的低值
- HL —低值旁边的高值
我们计算莫兰局部分数。
m_local = Moran_Local(y, w)
用莫兰散点图画出来。
P_VALUE = 0.05
fig, ax = plt.subplots(figsize=(10,10))
moran_scatterplot(m_local, p=P_VALUE, ax=ax)
ax.set_xlabel('Average Price')
ax.set_ylabel('Spatial Lag of Avg Price')
plt.text(1.35, 0.5, 'HH', fontsize=25)
plt.text(1.65, -0.8, 'HL', fontsize=25)
plt.text(-1.5, 0.6, 'LH', fontsize=25)
plt.text(-1.3, -0.8, 'LL', fontsize=25)
plt.show()

莫兰局部散点图
现在让我们使用lisa_cluster 函数在地图中绘制 LISA 结果。
fig, ax = plt.subplots(figsize=(12,10))
lisa_cluster(m_local, nbr_final, p=P_VALUE, ax=ax) nbr_final.boundary.plot(ax=ax)
plt.title('Toronto Airbnb Neighbourhood Average Price Spatial Lag Choropleth Map 2020-12')
plt.tight_layout()
plt.show()

Airbnb 多伦多街区平均价格空间滞后 Choropleth 地图
红色的邻域是附近邻域(HH)中价格较高的区域。蓝色表示被低价包围的低价区域(LL)。有趣的是,我们注意到黄色和浅蓝色显示了与邻近地区(HL 和 LH)价格差异较大的区域。您可以使用上面的交互式社区价格图来验证 LISA 分类。
经过进一步调查,似乎在这个乡村社区(被灰色包围的红色街区)有一个标价为 2000 美元的房源,远高于该地区 60 美元的平均价格。这可能是数据问题或不同类型的属性。
结束语
使用空间自相关分析,我们分析了多伦多 Airbnb 价格相对于其邻近地区的全局和局部空间自相关。LISA 分析对于识别具有统计显著性的高值聚类和检测异常值非常有用。它可以帮助许多基于地理的数据分析,包括社会,经济,流行病学研究。
所有代码都可以在 Google Colab 上找到。
机器学习快乐!
原载于 2021 年 1 月 4 日https://ai-journey.com/2021/01/exploratory-spatial-data-analysis-esda-spatial-autocorrelation/。
感谢阅读。如果您有任何反馈,请在下面留言,通过我的博客https://ai-journey.com/联系我,或者通过 LinkedIn 给我发消息。
通过重要的关联网络探索和理解您的数据。
原文:https://towardsdatascience.com/explore-and-understand-your-data-with-a-network-of-significant-associations-9a03cf79d254?source=collection_archive---------25-----------------------
探索以了解您的数据可以决定项目是失败还是成功完成!

照片由 科特托阿加 上 下
理解您的数据是数据科学的重要一步。然而,如果你不对数据做任何假设,这可能会成为一个耗时且具有挑战性的步骤。很容易忽略这一步,在水中跳得太快。不过,明智的做法是先学会游泳再跳入水中!这是一个不成功的项目或成功完成的区别!
探索数据的一种方式是使用方法HNet【1,2,3】。它代表图形化 H 超测 Net 作品,让你轻松探索你的数据,检索新的有意义的见解。在本文中,我将讨论数据理解的重要性,并给出一个实际例子。
如果你觉得这篇文章很有帮助,可以使用我的 推荐链接 继续无限制学习,并注册成为中级会员。另外, 关注我 保持我的最新内容!
数据理解是至关重要的一步。
真实世界的数据通常包含具有连续值和离散值的测量值。我们需要审视每一个变量,并对它们对于特定问题的意义和重要性进行哲学分析。但这不仅仅是一次只看一个变量(单变量),如果不对数据做任何假设,而是以多变量的方式检查变量,那么耗时的部分就很难完成。搜索空间的变量数量呈超指数增长。跳过它?不要!学游泳! 在进行任何分析之前,彻底探究数据是极其重要的。
数据探索将为未来的任务提供提示和线索。
根据提示和线索,您可以决定哪些模型是分析的候选对象,以及检索合理结果的可能性有多大。尽管有许多库可用,但数据集需要密集的预处理步骤,并且探索性分析是一项复杂且耗时的任务。
这里有 HNet 发挥作用,它使用统计测试来确定变量之间的显著关系。最酷的是,您可以简单地在模型中输入原始的非结构化数据。输出是一个网络,可以揭示变量之间的复杂关系。
预处理与探索
数据探索通常与预处理步骤密切相关。但是还是有区别的:
其中预处理是关于:
- 标准化,规范化,规模化。
- 编码变量。
- 清除,如删除或插补。
- …
探索理解是关于:
- 变量之间的关系如何?
- 数据有意义吗?
- 是否存在偏见?
- 底层数据分布是怎样的?
- 单变量/多变量分析
- …
探索性分析可能相当具有挑战性,因为没有真正的问题要追求。 这是你需要用数据获得感觉的部分。 用数据获得‘感觉’的一种方法是通过理解变量之间的关系。
保持冷静,聪明地工作
让我们跳进 HNet 的方法论。目的是确定一个具有重要关联的网络,它可以揭示变量之间的复杂关系。输入的范围可以从通用数据框到带有列表、缺失值和枚举的嵌套数据结构。
HNet 从具有混合数据类型和未知函数的数据集中学习关联。
这意味着可以使用包含分类值、布尔值和/或连续值等特征的数据集。此外,您不需要指定目标(或响应)变量,因为它将以无人监管的方式工作。
HNet 的建筑
HNet 的架构是一个多步骤流程(图 1),其中前两步是通过打字和一键编码处理原始非结构化数据。

示意图概述 HNet 1
- 键入:该过程的第一步是将每个特征键入类别、数字或排除。类型可以是用户定义的,也可以是自动确定的。在后一种情况下,如果值是浮点类型或具有超过最小数量的唯一元素(例如,如果唯一元素的数量>占非缺失总数的 20%),则特征被设置为数字。
- 一键编码:第二步是将类别值编码成一个一键密集数组。这是使用 df2onehot 库 完成的。独热密集阵随后用于统计推断。
- 统计推断:这一步包含两个统计检验。1.使用超几何测试来测试一位热码编码特征与所有其他一位热码编码特征(X 离散)的显著性。2.为了评估与密集阵列(x 离散)相关的数字特征(x 数字)之间的显著性,使用 Mann-Whitney-U 检验。每个数字向量在类别特征上进行分割( Xci 对~Xci) 并进行显著性测试。
- 多重测试校正:在统计推断步骤之后,顶点对之间的所有测试的边概率,无论是类别-类别还是类别-数值,都存储在邻接矩阵中( Padj ),并针对多重测试进行校正。默认多重测试方法设置为 Holm(图 1E)。可选的是各种其他错误发现率(FDR)或家族错误率(FWER)方法。
- 过滤重要边缘:最后一步(图 1F)是声明边缘的重要性。当 alpha 小于 0.05(默认值)时,边缘被称为显著的。边权重由-log10(Padj)计算。
最终输出是包含节点和描述顶点对强度的边权重的邻接矩阵。邻接矩阵在下一步中作为网络表示或热图进行检查。
计算关联。
当我们谈到关联时,它意味着一个变量的某些值往往与另一个变量的某些值同时出现。从统计学的角度来看,有许多关联的测量方法(如卡方检验、Fisher 精确检验、超几何检验),通常用于一个或两个变量为序数或名义变量的情况。我将通过例子演示如何使用超几何测试来分析 Titanic 数据集的两个变量是否相关。众所周知,在这个数据集中,性别状态(女性)是一个很好的生存预测因子。更多信息和单变量分析,见Kaggle【5】和博客【6】。让我们来计算一下幸存和女性之间的关联。
***pip install hnet***
加载泰坦尼克号数据集
无效假设:幸存者和女性没有关系。
超几何检验使用超几何分布来衡量离散概率分布的统计显著性。在这个例子中, N 是群体大小(891),K 是群体中成功状态的数量(342),N 是样本大小/抽取的数量(314),x 是样本中成功的数量(233)。
存活与雌性之间的超几何检验
数学如等式 1 所示。超几何测试有三个要素:1.雌性存活下来的独特组合的总数,2。任何人都可以生存的独特组合的总数,以及 3。所有剩余的组合。

等式 1:超几何检验。
我们可以拒绝在α= 0.05 下的无效假设,因此,我们可以谈论存活和雌性之间的统计学显著关联。重要的是,联想本身并不意味着因果关系。我们需要区分边际关联和条件关联[7]。在条件作用中,一些变量在某些类别中是固定的,而联系的强度是为剩余的变量测量的。另一方面,边缘化考虑变量的子集,而不注意其余的变量,不涉及选择,一组变量的关联指的是整个人口。
一个实际的用例:探索和理解泰坦尼克号数据集。
让我们继续讨论 Titanic 数据集,因为它包含一个在真实用例中常见的结构,即每个样本中存在分类变量、布尔变量和连续变量。在前面的步骤中,我们初始化并加载了 Titanic 数据集。在这一步,我们将预处理 12 个输入特征;打字和一键编码。这都是由 HNet 提供输入数据帧后完成的。
这产生了 2634 个独热编码特征。 HNet 的默认设置是如果看到≤ 10 ( y_min=10 )则删除一键编码特征。这将特征空间减少到 18 个独热编码特征,为其计算关联。
输出是一个包含以下键的 字典 :
- simmatP : 包含变量关联之间 P 值的邻接矩阵。
- simmatLogP:-simmatP 的 log10(P 值)。**
- labx : 被分析的标签。
- 数据类型 : 为标签设置的数据类型。
- 计数 : 基于群体中成功数量的标签的相对计数。
排除变量将节省计算时间!
一些输入变量可以从分析中排除,例如标识符和名称。当变量被列入黑名单时,它们被排除在搜索范围之外,生成的模型将不包含任何这些边。如果变量是白名单,搜索仅限于那些边缘。结果模型将只包含白名单中的边。
包含或排除变量的示例。
有四种方法可以节省你的计算时间。
- 黑名单 : 从分析或绘图中排除的节点。
- 白名单 : 仅包括分析或绘图中列出的节点。
- 阈值 : 关联(边)基于-log10(P) >阈值进行过滤。阈值的范围应该在 0 和最大值-log10(P)之间。
- min_edges : 节点仅在至少包含最小数量的边时显示。
情节
所有关联都存储在 simmatP 中,并用于通过各种交互图更深入地探索关系。
D3 图形
D3 graph【8】将在 d3-javascript 中使用 python 生成一个交互式单机网络。这种可视化将允许您更深入地检查网络,因为它允许使用滑块在其权重上逐渐断开边缘。滑块基于-log10(P ),最小值为(默认)log10(0.05)。向右滑动滑块意味着您将只保留最重要的关系。更多关于 d3graph 的细节可以在这个博客中找到。网络的其他一些属性:
- 每个节点包含一个文本标签
- 当双击感兴趣的节点时,可以突出显示相关节点的链接
- 每个节点都包含一个工具提示,可以很容易地用来显示任何底层数据
制作网络图和热图。

连通边 P≤0.05 的网络
从这个网络中我们可以了解到,通过打破网络,出现了两个集群。存活的雌性群集=1,而存活的雄性群集=0。现在,您可以轻松地更深入地检查变量之间的其他关联。

D3 热图
当网络变成一个巨大的毛团时,热图是理想的选择。矩阵图的有效性在很大程度上取决于行和列的顺序:如果相关节点彼此靠近放置,就更容易识别集群和桥。虽然在矩阵视图中进行路径跟踪比在节点链接图中更困难,但矩阵还有其他优点。矩阵单元也可以被编码以显示附加数据;这里,颜色描述了由社区检测算法计算的聚类。该热图是使用D3 热图库【9】在 d3js 中创建的。

总结结果
网络可能变成巨大的毛团,热图无法阅读。您可能希望看到类别之间的一般关联,而不是标签关联。使用summarize功能,结果将按类别汇总。在这里我们再次清楚地看到了幸存和性之间的联系。
**
功能重要性。
还可以通过计算每个类别的特征重要性来分析网络中的变量。每个类别的独热编码变量的-log10(P 值)除以每个节点的有效边数。高分变量表明许多独立的节点在网络中是重要的。在这里,我们可以看到客舱、Pclass 和幸存是前 3 个变量。

结论
HNet 允许深入检查特征之间的关联。它可以处理包含混合数据类型的原始非结构化数据集。然而,如果你的数据集只包含连续值,它应该遵循不同的方法,想想像 PCA ,t- SNE,奇异值分解,UMAP 这样的方法。更多信息和笔记本示例见参考文献3。
保持安全。保持冷静。
干杯,E.
如果你觉得这篇文章很有帮助,可以使用我的 推荐链接 继续无限制学习,并注册成为中级会员。另外, 关注我 关注我的最新内容!
软件
- HNet github/文档
- Colab 笔记本示例
我们连线吧!
- 我们在 LinkedIn 上连线
- 在 Github 上关注我
- 在 Medium 上跟随我
参考
- E.Taskesen, HNet:图形超几何网络 ,2020, arXiv:2005.04679
- Taskesen,E. (2019)。HNet:图形超几何网络。
- HNet 笔记本示例
- 迈克尔.朗斯。如何避免机器学习陷阱:学术研究者指南。ArXiv,2021 年
- https://www.kaggle.com/c/titanic/
- https://towards data science . com/predicting-the-survival-of-titanic-passengers-30870 ccc7e 8
- Wicher Bergsma 和 Tamas Rudas, 权变表 中的条件和边际关联,蒂尔堡大学,2002 年 10 月 18 日
- Taskesen,E. (2019)。交互式强制定向网络创建器( d3graph )(版本 0.1.12)【计算机软件】,https://erdogant.github.io/d3graph
- Taskesen,E. (2019)。交互式和独立热图(d3heatmap)(版本 0 . 1 . 7)[计算机软件]。https://github.com/erdogant/d3heatmap
在 Google BigQuery 中探索数组和结构以获得更好的查询性能
原文:https://towardsdatascience.com/explore-arrays-and-structs-for-better-performance-in-google-bigquery-8978fb00a5bc?source=collection_archive---------3-----------------------
使用这两个高级 SQL 概念来提升您的 SQL 查询游戏比您想象的要容易。不要错过!

在 Unsplash 上给拍照
在分析大数据时,对数组和结构的良好理解非常有用,因为我们可以更快、更有效地查询来自基于对象的模式(如 JSON 或 Avro 文件)的预连接表。在这篇博文中,我们将通过大量的例子探索数组、结构以及如何在 Google BigQuery 中充分发挥它们的潜力。
你会学到
- 为什么我们需要知道数组和结构?
- 数组以及如何使用它们
- 结构以及如何将它们与数组结合起来创建嵌套记录
说够了,我们开始吧!
为什么我们需要知道数组和结构?

作者照片
看看上面的图片,你能看到什么?
这张图片代表了 3 种不同的模式,以捕捉餐厅的名称,位置和它的美食标签之间的关系。为了简单起见,我们假设一家餐厅只有一个位置,但有多个美食标签。
最左边是选项 1,其中我们有两个独立的表通过一对多关系连接在一起。规范化有助于避免存储位置冗余,从而降低存储成本。但是我们也牺牲了查询性能,因为如果我们想知道泰国餐馆在哪里,就需要连接两个表。
选项 2 则完全相反。查询性能会快得多,因为不需要连接两个表,然而存储成本会更高,因为位置在整个非规范化表中是冗余存储的。
但是如果我们能拥有两个世界的最好的东西呢?当然,我们可以用选项 3 在 Google BigQuery 中实现。每家餐馆的菜肴标签都很好地嵌套在一行中,这带来了两个主要好处。
更好的查询性能
- 所有数据都在一个表中,所以您可以跳过昂贵的连接。
- 如果我们在 location 下做类似 SELECT DISTINCT 的事情,我们只需要迭代 3 行而不是 6 行。
降低存储成本
- 名称和位置不必被冗余地存储
听起来不错!但是,我们如何根据选项 3 来组织信息,以获得这些令人敬畏的好处呢?嗯,我的朋友,这就是为什么我们需要数组和结构,因为它们表示 BigQuery 中嵌套和重复的字段。在接下来的几节中,我们将详细探讨数组和结构。
数组
定义
数组是包含相同数据类型的值的有序列表。当我们想要在单个行中存储重复值时使用它,例如下面的场景。
- 一名员工拥有一项或多项技能
- 一个学生可以说一种或多种语言
- Zomato 上列出的餐馆有一个或多个美食标签,如意大利菜、披萨、意大利面、休闲菜
- 客户的销售订单包括一个或多个项目
使用 SQL 数组
用方括号创建一个数组
让我们从简单的事情开始。我们将尝试创建一个永久的表,用一个数组来捕获悉尼 4 个不同餐馆的美食标签。

作者照片
查看查询结果,有 3 个要点需要注意。
- 我们可以用方括号[ ]创建一个数组,每个元素用逗号分隔。每对方括号内的值组成一个数组。
- 由于每个数组代表与一家餐馆相关联的美食标签,BigQuery 将它们存储在与一行相关联的重复字段中。总共,我们有 4 个不同的行,对应于 4 个餐馆。
- 在每一行下面,菜肴标签垂直显示在不同的行中,而不是水平地将它们放在方括号中。我们说 BigQuery 已经自然地展平了输出。
用 UNNEST 打开一个数组
如果您想将数组解包,以便将其导出到 MS Excel 或 Google Sheets 中的电子表格中,该怎么办?好吧,很公平,UNNEST 操作员将在这种情况下派上用场。

作者照片
如您所见,UNNEST 操作符将数组转换成非规范化的格式,准备好供 MS Excel 或 Google Sheets 使用。从 Haymarket 的 Ho Jiak 餐厅的 1 行嵌套 2 个美食标签,我们现在得到 2 个单独的行。
用 ARRAY_AGG 将元素聚集成数组
如果我们事先有一个非规范化的表,并且需要将所有的美食标签聚合成数组格式,这样就可以减少行数,那该怎么办?
让我们将上述查询的结果保存到一个名为“restaurant_cuisine_unnested”的 BigQuery 表中。接下来,我们将使用带有 GROUP BY 的 ARRAY_AGG 操作符,将其恢复为数组格式。

作者照片
瞧啊。给你,东西被很好地重新包装成正常的 4 排 4 个餐厅。你注意到每一排的菜肴顺序有什么不同吗?(提示:请注意 Chaco Bar 的日文上方现在显示的是多么随意)
是的,因为我们选择了使用 ARRAY_AGG 的“ORDER BY cuisine ”,所有的美食标签现在都是按字母顺序排序的。如果我们将它保存为永久表,这个顺序将被保留。
用 ARRAY_LENGTH 对数组中的元素进行计数
是时候让事情更上一层楼了,看看我们还能用数组做些什么。我们来数一数每家餐厅都关联了多少个美食标签。

作者照片
用 UNNEST 和 WHERE IN 查询数组
如果我们想列出休闲餐厅的所有相关信息怎么办?
为了解决这个问题,我们必须逐个检查数组的每个元素,只过滤包含“Casual”的菜肴。但是数组列的行为与普通列不同。如果不首先取消嵌套,你就不能遍历和过滤数组中的元素。记住这一点,我包含了两个略有不同的查询,根据您的需要,它们会以两种不同的格式给出相同的结果。
备选方案 1。如果您想获得一个没有任何数组 的 非规范化查询结果(在 Excel 或 Google 工作表中使用)

作者照片
选项 2。如果您希望将您的 美食列很好地打包到一个数组 (以节省存储成本和查询时间)
注意:以下问题由迈克尔·恩廷提供。我决定用原来的查询替换它,因为它更简单、更简洁。再次感谢@mentin 开导我!

作者照片
结构
定义
结构是有序字段的灵活容器,每个字段都有一个类型(必需)和一个名称(可选)。
-来自 谷歌云
与数组相比,你可以在一个结构中存储多种数据类型,甚至数组。在 Google BigQuery 中,Struct 是一个父列,表示一个有多个子列的对象。举个例子,
- 餐馆的位置由不同的字段表示,如地址、城市、州、邮政编码。
- 员工具有与不同字段相关联的资格,如大学、学位、开始日期和结束日期。
请记住,当您在 Google BigQuery 中加载嵌套和重复数据时,您的模式不能包含超过 15 层的嵌套结构。
使用 SQL 结构
创建一个简单的结构
理解结构如何工作的最简单的方法是创建一个结构。让我们将现有的 cuisine_array 与其他细节(例如价格范围、交付)结合起来,形成一个结构。下面是如何用 STRUCT 操作符来实现的。

作者照片
在其最简单的形式中,具有相同前缀“basic_info”的 3 列形成一个结构。结构中的不同元素可以有不同的数据类型。例如,上面我们有一个数组、字符串和布尔数据类型,它们都和谐地存在于单个结构中。
既然我们在这里,你可能想看看下面的模式来理解数组和结构之间的区别。如果模式是重复的,那就是一个数组(又名重复字段)。当类型为 RECORD 时,它表示一个结构(也称为嵌套字段)。

作者照片
用点符号查询结构
考虑到 BigQuery 中的上表,是时候让自己更努力一点了。把所有有送餐服务的休闲餐厅都标识出来怎么样?

作者照片
下面是查询结构时需要注意的两点。
- 使用点标记法查询嵌套列(例如,其中 basic_info.has_delivery = true)
- 不要忘记在查询数组中的元素之前取消数组嵌套,即使该数组嵌套在结构下
从结构数组中创建嵌套记录
这是您的 SQL 知识达到顶峰的地方,因为嵌套记录的概念非常先进。所以我们开始吧,各位!
让我们仔细看看从查询中获得的表。你注意到了什么?

作者照片
在前面,我们看到了一个包含数组的结构。但是现在,我们反过来看,第一个数组“Sydney”包含 4 个结构,而第二个数组“Wollongong”包含 2 个结构。这有什么意义呢?

作者照片(照片中仅显示选定的子值)
在这里,我们见证了嵌套记录的创建,这些记录在没有连接的情况下反规格化了多个级别的一对多(即父子)关系。所有数据都驻留在一个表中;每个记录都是嵌套的,以优化存储,同时还可扩展(使用 UNNEST)以获得高查询性能。因此,在 BigQuery 中,我们说嵌套记录是结构的数组。维护嵌套记录消除了在数据分析过程中重复数据、创建额外子表或使用昂贵连接的需要。这就是 Google BigQuery 作为一个数据仓库如此强大的关键所在,尤其是在分析来自 JSON 或 Avro 文件的数据时。
包扎
在这篇文章中,我们探讨了数组和结构的定义,以及为什么嵌套和重复的字段在 BigQuery 中如此重要。以下是这篇文章的三个要点。
- 数组是 SQL 中支持的数据类型,即使在 Google BigQuery 之外也是如此。在数组中,每个元素必须具有相同的数据类型,并且值的顺序保持不变。
- 一个结构可以包含不同数据类型的元素,包括数组。
- 在 BigQuery 中,嵌套记录是结构的数组。维护嵌套记录消除了在数据分析期间重复数据、创建附加子表或使用昂贵的连接的需要,从而提高了查询性能并降低了存储成本。
感谢您的阅读。有问题或者想聊天?请在评论中告诉我,或者在 Twitter 上找到我。祝你摇滚一天,回头见,鳄鱼!
原载于 2021 年 1 月 20 日【http://thedigitalskye.com】。
使用深度图卷积网络和贝叶斯优化探索更好的材料
原文:https://towardsdatascience.com/explore-better-materials-using-deep-graph-convolutional-networks-and-bayesian-optimization-144a9b6699fc?source=collection_archive---------30-----------------------
从理论计算中提取知识来设计材料,以最大限度地减少破坏性实验的次数。

Louis-Etienne Foy 在 Unsplash 上拍摄的照片
你想要更好的材料吗?是的,我们有。我们想要容量更大、寿命更长的更好的电池。或者,我们想要更好的太阳能电池板和更高的发电量。或者,我们想要焦耳热损失更少的更好的半导体。所有这些性能限制都源于组成它们的材料。所以,是的,我们确实希望任何产品都有更好的材料。
动机
但是我们如何从几乎无限的现有材料中找到它们呢?一种方法是利用模拟数据库,如材料项目。这种类型的数据库已经使用 DFT 计算来编译材料特性,这有利于缩小探索范围。然而,众所周知,有几个性质的可预测性很差,比如带隙,所以我们不能从表面上接受它们的值。虽然,根据结晶学开放数据库显示,目前为止,累计晶体数量已经达到476995,以至于无法通过实验的方式检查其性能。啊,我希望有一个好的实验数据库和材料发现的搜索引擎!可悲的是,根本不存在。(至少现阶段是这样。)是的,我们应该创造一种方法来利用从理论数据库到实验晶体结构数据库的知识。那么,在机器学习的帮助下,让我们考虑有效探索所需材料的方法。
问题设置
让我们考虑探索具有期望的带隙的材料。当我们想要一个更好的半导体时,这个特征值是最基本的指导。根据产品的设计,所需的带隙值会有所不同。因此,我们可以将半导体材料发现问题简化如下:
当我们设置 E 为带隙的理想值时,如何用最少的探索试验从晶体数据库中尽可能多地找到带隙最接近的晶体?
让我们把这个问题说得更具体一些。我们要澄清的事情如下:
1.我们需要多精确地接近目标值?
2。哪个指标最合适?
3。我们如何构建数据集?
4。我们需要探索到什么程度?
首先,我们应该在 0.01 eV 误差内估计目标值。eV,电子伏特,是带隙的单位。根据其制造方法或条件,带隙可以在这个尺度上变化。所以,继续追求可能会徒劳无功。其次,当我们强调绝对值而不是比率时,我们可以采用 MAE(平均绝对值)度量。顾名思义,这可以通过目标值与估计值之差的绝对值的平均值来获得。接下来,我们可以使用 CIF(晶体信息文件)数据集。CIF 是描述晶体结构的国际格式,它包含基本信息,但不是定量的。第三,我们可以使用材料项目数据库作为伪实验数据集。假设我们可以从假设的实验数据集中探索带隙为 E 的材料。在这种情况下,我们可以应用同样的策略来有效地进行实际的实验。考虑到实际情况,我们可以通过调查科技论文来收集目标值的初步信息。假设这个数字是 100 左右。此外,我们可以通过考虑其他不希望的方面如合成成本或反应性来排除许多候选物。这样,我们可以获得 100 个初步信息,并将勘探候选对象缩小到 6000 个左右。
那么,我们可以把实验材料探索问题概念化如下:
当我们将 E 设置为带隙的理想值时,如何利用 100 个先验信息,用最少的探索试验从大约 6000 个候选者中尽可能多地找到 MAE 小于 0.01 eV 的晶体?
机器学习策略
贝叶斯优化
正如我们可以很容易地在谷歌搜索中发现的,贝叶斯优化似乎是一个有效的探索。贝叶斯优化是一种算法,旨在以最少的尝试探索更好的数据点。例如,谷歌大脑团队将这种算法应用于智能优化巧克力饼干配方。不管怎样,这看起来很实用,是的,我们应该把它应用到材料发现上。但是等等,我们需要描述符。换句话说,这组变量用来识别唯一的晶体。谷歌团队使用了每个饼干烹饪程序的定量值,例如木薯淀粉的重量比。如何才能量化晶体?
水晶图卷积神经网络
一个简单的方法是利用预先训练好的水晶深度学习模型。 CGCNN ,Crystal Graph 卷积神经网络,是材料科学领域开创性的深度学习架构。在作者的 GitHub 资源库中,他们开放了预先训练好的模型,每个人都可以使用。当我们查看准备好的模型文件夹时,我们可以找到带隙模型(band-gap.pth.tar)。通过使用该模型作为特征提取器,我们可以在 autopilot 上将 CIF 文件转换为 128 个定量描述符。
主成分分析
不幸的是,128 个描述符对于贝叶斯优化来说太多了。虽然有很多针对高维优化的前沿算法,但基本上,低维效果更好,不需要额外的努力。此外,这 128 个描述符仅用于定量识别晶体,因此高维数不是必需的。因此,我们可以使用 PCA,主成分分析来降低维数。通过将 128 维减少到 3 维,我们可以设置更有效的探索空间。
代码
Python 库要求:
- pymatgen
- pytorch
- sci kit-学习
- GPyOpt
- GPy
数据集构建
我们将使用材料项目 API 来构建数据集。首先,您需要在 Materials 项目上创建一个帐户,然后获取 API 密钥。这可以通过遵循官方指令来完成。然后,我们将为先验信息和探索编译两个数据集。您应该将 MY_API_KEY 更改为您的密钥。
在这个代码中,我们搜索带隙在 2.3 和 2.8 之间的晶体,得到 6749 种材料。然后,它们被分成两个文件夹;“cif_prior”和“cif_experiment”,分别包含 100 个和 6649 个 cif 文件。此外,带隙值在每个文件夹中存储为“id_prop.csv”。
使用预训练的 CGCNN 模型将 CIF 转换为 128 个描述符
您可以按照这里的官方说明来 git 克隆 CGCNN 资源库。你需要将atom _ init . JSON复制到“cif_prior”和“cif_experiment”两个文件夹中。然后,您可以通过修改 predict.py 来创建特征提取代码。我在 predict.py 中的 validate 函数的基础上创建了extract _ feature . py,代码太长,这里写不下,所以我只展示修改的部分。
首先,主要功能的修改部分是这样的最后一部分。
然后,像这样修改 validate 函数的中间部分。
然后,可以用下面的参数执行这个extract _ feature . py。
python3 extract_feature.py ./pre-trained/band-gap.pth.tar ./cif_experiment
这样你就可以得到 128 个描述符作为CG CNN _ features . CSV。我们应该为“cif_prior”和“cif_experiment”创建特征。并将CG CNN _ features . CSV重命名为CG CNN _ features _ prior . CSV和CG CNN _ features _ experiment . CSV。
通过主成分分析将维数从 128 减少到 3
我们将把 128 个特征转换成三维数据。同样,对两个数据集执行两次代码,将输出CG CNN _ PCA . CSV重命名为CG CNN _ PCA _ prior . CSV和CG CNN _ PCA _ experience . CSV。
贝叶斯优化
最后,我们将使用贝叶斯优化探索更好的材料。实验设置将通过定义以下类实例来完成。
一切准备工作都已就绪。下一个代码将自动探索更好的材料。在该设置中,目标带隙被设置为 E = 2.534 [eV]。我们期望的材料应该具有 0.01 eV 的 MAE 误差,因此,目标范围将在 2.524 和 2.544 eV 之间。贝叶斯优化循环将按照 n_experiment 重复探索 30 次。发现的材料和相应的值存储在以下 self 实例中:
- 探索的带隙值;exp.explored _ bandgaps
- 水晶名称;出口晶体
- 累积损失曲线;结果
通过添加基于这些实例和变量的代码,您可以自由地可视化或导出这些结果。
在我的设置中,在 5 次内,贝叶斯优化可以找到想要的材料。也就是说,这种方法在实际的材料勘探方案中似乎是有用的。享受材料发现!
想要一个有挑战性但有意义的阅读吗?探索减轻算法偏差的技术方法
原文:https://towardsdatascience.com/explore-the-technical-approaches-to-mitigating-algorithmic-bias-602bb397c7e1?source=collection_archive---------31-----------------------
探索数据科学
既然机器学习社区已经认识到需要认真对待算法偏差,那么是时候探索减轻它的潜在方法了——包括算法解决方案。在这篇文章中,前 DeepMind 研究工程实习生 Joyce Xu 问“在算法上,我们如何确保我们建立的模型不会反映和放大数据中的人类偏见?”从对抗性去偏差到分布稳健优化,Joyce 深入探讨了几个关键的潜在解决方案。伟大的长篇大论!
探索雪花上的四方世界
原文:https://towardsdatascience.com/explore-the-world-with-foursquare-places-on-snowflake-c1351394e3ff?source=collection_archive---------38-----------------------
Foursquare Places 数据集覆盖了全球超过 9600 万个地方——包括每个地方的受欢迎程度等有趣的领域。在本帖中,我们将探索数据集,并强调它的一些可能性。
在 youtube 上观看
这是一张显示美国各州最受欢迎的餐馆类型的地图,模式很明显-东部各州喜欢披萨,西部各州喜欢墨西哥食物:

美国各州最受欢迎的餐馆类型。作者图片
但是也有一些例外:
- 西部的爱达荷州更喜欢披萨。
- 南部的路易斯安那州到处都是卡津美食。
- 而东北部的缅因州爱吃海鲜。
为了构建这张地图,我通过雪花数据市场使用了 Foursquare Places premium 数据集。你可以在下面找到我的问题。
该数据集包括来自世界各地的 9600 多万个地方,因此我们可以将我们的食物地图扩展到全世界。这些是结果:

每个国家最受欢迎的餐馆类型。作者图片
如果我们放大到欧洲,我们会发现像意大利、法国、西班牙和德国这样的国家热爱他们自己的食物——但是英国呢?他们宁愿吃印度食物。

每个欧洲国家最受欢迎的餐厅类型。作者图片
南美也是如此:秘鲁和巴西热爱自己的食物,但智利人呢?我们喜欢寿司。

南美国家最受欢迎的餐厅类型。作者图片
Foursquare 的迷人之处在于,你可以探索整个世界,也可以关注超本地。例如,让我们去旧金山,在那里你可以发现每个邮编都有自己最喜欢的食物类型:中国,墨西哥,泰国,意大利,我们都有。

最受欢迎的餐馆类型。作者图片
现在有了 Foursquare,你还可以超越食物。例如,这是每个邮政编码中最受欢迎的业务类型:

最受欢迎的商业类型。作者图片
办公室、购物、精品店、洗衣服务,当然还有面包店。
我们还可以更深入,例如找到旧金山周围每家美容院的确切位置,以及它们的受欢迎程度。

SF 周边的美容院。作者图片
Foursquare Places 数据集可以帮助您了解您的客户在哪里,如何发展您的业务——所有这些都是通过 SQL 和数据云的力量实现的。你会在哪里开新店?
问题
创建视图以过滤数据和自定义模式
我遵循一些规则来产生这些可视化效果:
- 我只关注 2020 年 1 月以后更新数据的地方。
- 对于餐馆,我只计算 Foursquare 上用户留下至少 1 个小费的餐馆。
create or replace view fsq
as (
select *
from fsq.public.global_latest_venues
where date_refreshed > '2020-01-01'
)
;create or replace view restaurants_us_state
as
select region
, count(*) c
, any_value(name) sample_name
, any_value(locality||', '||region) sample_city
from fsq
where country='us'
and total_tips > 1
and category_labels[0][2]='Restaurants'
group by 1
;create or replace view restaurants_us_zip
as
select postcode
, count(*) c
, any_value(name) sample_name
, any_value(locality||', '||region) sample_city
from fsq
where country='us'
and total_tips > 1
and category_labels[0][2]='Restaurants'
group by 1
;create or replace view restaurants_world
as
select country
, count(*) c
, any_value(name) sample_name
, any_value(locality||', '||region) sample_city
from fsq
where total_tips > 1
and category_labels[0][2]='Restaurants'
group by 1
order by c desc
;
每个州的顶级餐厅类型
比萨和墨西哥餐厅是美国各州最受欢迎的餐厅类型——除非你去掉同样受欢迎的类别“美国餐厅”、“快餐店”和“三明治店”。我这样做是为了得到一张更有趣的地图。
create table viz_pizza_mexican_other_state
as
select a.region, venue_primary_category
, count(*) cc
, count(*) / any_value(b.c) ratio
, row_number() over(partition by a.region order by ratio desc) rn
, any_value(name) sample_name
, any_value(locality||', '||a.region) sample_locality
from fsq a
join restaurants_us_state b
on a.region=b.region
where country='us'
and total_tips > 1
and category_labels[0][2]='Restaurants'
and venue_primary_category not in ('American Restaurant', 'Fast Food Restaurant', 'Sandwich Place')
group by 1,2
having cc>10
qualify rn=1
;
世界餐馆
create table viz_world_fav_restaurants
as
select a.country, venue_primary_category
, count(*) cc
, count(*) / any_value(b.c) ratio
, row_number() over(partition by a.country order by ratio desc) rn
, any_value(name) sample_name
, any_value(locality||', '||a.region) sample_locality
from fsq a
join restaurants_world b
on a.country=b.country
where total_tips > 1
and category_labels[0][2]='Restaurants'
and venue_primary_category not in ('American Restaurant', 'Fast Food Restaurant', 'Sandwich Place', 'Restaurant')
group by 1,2
having cc>10
qualify rn=1;
旧金山餐馆
create table viz_san_fco_restaurants
as
select a.postcode, venue_primary_category
, count(*) cc
, count(*) / any_value(b.c) ratio
, row_number() over(partition by a.postcode order by ratio desc) rn
, any_value(name) sample_name
, any_value(locality||', '||a.region) sample_locality
from fsq a
join restaurants_us_zip b
on a.postcode=b.postcode
where total_tips > 1
and category_labels[0][2]='Restaurants'
and venue_primary_category not in ('American Restaurant', 'Fast Food Restaurant', 'Sandwich Place', 'Restaurant')
and locality='San Francisco'
group by 1,2
having cc>10
qualify rn=1;
旧金山商店
select a.postcode, venue_primary_category
, count(*) cc
, row_number() over(partition by a.postcode order by cc desc) rn
, any_value(name) sample_name
, any_value(locality||', '||a.region) sample_locality
from fsq a
where total_tips > 1
and category_labels[0][2] not in ('Restaurants', 'Cafes, Coffee and Tea Houses', 'Beauty Salons and Barbers', 'Hotels and Motels')
and locality='San Francisco'
group by 1,2
having cc>10
qualify rn=1
旧金山美容院
create or replace table viz_sf_beauty
as
select a.postcode, venue_primary_category
, name, latitude, longitude, popularity
from fsq a
where total_tips > 1
and category_labels[0][2] in ('Beauty Salons and Barbers')
;
后续步骤
现在轮到你探索世界了——通过雪花数据市场联系 Foursquare,获得这个数据集。
通过 Foursquare 查看我们的网络研讨会,进一步了解:
https://www.snowflake.com/webinar/thought-leadership/how-to-select-your-next-retail-location-using-third-party-data/
想要更多吗?
我是 Felipe Hoffa,雪花的数据云倡导者。谢谢你和我一起冒险。你可以在推特上关注我,并查看reddit.com/r/snowflake上最有趣的雪花新闻。
https://github.com/Snowflake-Labs/awesome-snowflake
用 R 和“spotifyr”探索你在 Spotify 上的活动:如何分析和可视化你的流媒体历史和音乐品味
原文:https://towardsdatascience.com/explore-your-activity-on-spotify-with-r-and-spotifyr-how-to-analyze-and-visualize-your-stream-dee41cb63526?source=collection_archive---------6-----------------------
使用您的个人数据副本和“spotifyr”包,了解您如何从 Spotify 消费以及消费了多少

用于在 Spotify 上查看流媒体历史记录的仪表盘。文末的链接。
我可以非常自豪地说,我是那些仍然有幸去最好的音乐 CD 店购买他们最喜欢的艺术家的最新唱片的人之一。我记得当时我只有 15 岁左右(在这个年龄,我们大多数人都会接受一种音乐类型、乐队和我们觉得认同的艺术家),它对我来说代表了一个完整的仪式,从不知疲倦的寻找到那个神奇的时刻,在那个时刻,第一首曲目的第一个音符在立体声音响或戴着耳机的随身听上以最大音量播放。音乐很可能是来自另一个星系的任何文明(如果有的话)通过将我们自己识别为人类而向我们发出的信号之一。
“沉默之后,最接近表达不可表达的东西是音乐”
——阿尔多斯·赫胥黎。
像生活中的一切事物一样,随着时间的推移,音乐也在变化和转变,进化(或者奇怪地变异?).这也发生在我们听音乐和消费音乐的方式上。17 年后的今天,我的仪式不复存在,也很少有商店出售音乐 CD,随着时间的推移,我完全用订阅世界上最受欢迎的音乐流媒体服务的便利取代了它们: Spotify 。根据最近的统计数据,全球 35%的音乐流媒体服务订阅者订阅了 Spotify,几乎是 Apple Music 订阅者的两倍,远高于亚马逊、Deezer 和其他平台。
这就是为什么我觉得能够利用这样一个事实很有趣,即 Spotify 像当今几乎任何应用程序一样,允许你下载它保护的个人账户数据。这包括你的回放活动,搜索历史,以及其他 JSON 格式的东西。

截图:Spotify Mail《2020 年回顾》
是的,我知道 Spotify 年复一年非常友好地向你提供“年度回顾”,提供关于你的习惯和消费的信息。毫无疑问,在 12 月,你收到了一封电子邮件,邀请你探索这一年的活动。
然而,除了仔细检查 Spotify 不会单独提供给你的其他数据之外,看看事情是如何自己完成的总是很有趣,也更有趣。
我还想让你知道一个有趣的包,叫做“spotifyr”,它可以让你轻松地用 Spotify API 更深入地了解你的音乐品味。但是,嘿,就像开膛手杰克说的……让我们分开走吧。
在哪里以及如何获得我在 Spotify 上的数据副本?
你必须用你的用户名和密码登录,才能进入你的个人 Spotify 账户隐私设置的网址:https://www.spotify.com/us/account/privacy/。在那里,你会发现几乎在页面的末尾有一个部分,其文本为“下载你的数据”,它邀请你通过三个简单的步骤下载你的数据。

截图:Spotify 个人账户中的隐私设置
一旦您提出请求,出于安全考虑,Spotify 将向您发送一封电子邮件,以确认确实是您请求了该数据副本。有必要确认你收到的邮件。

截图:请求在 Spotify 中复制个人数据的确认邮件
确认后,Spotify 几乎会立即向您发送另一封电子邮件,通知您您的请求已成功,但您必须等待 30 天,在此期间,Spotify 会收集您的信息并发送给您。不要绝望,等待进一步的通知。
实际上,我认为这在很大程度上取决于你在这个平台上的活跃程度。对比我活跃的其他账户的测试花了将近 30 天,而对我来说,在提出请求三天后,我收到了邀请我下载数据的最后一封电子邮件。几天后,你会发现自己收到了如下邮件。

截图:电子邮件邀请您在 Spotify 上下载您的个人数据副本
通过点击“下载”按钮,您将再次被重定向到您的 Spotify 帐户的隐私设置页面,出于安全原因,即使您已经登录,它也会要求您再次输入密码。完成后,您将在浏览器中自动看到一个名为“my _ Spotify _ data . zip”的文件正在下载中。请记住,正如上一封邮件中提到的,从邮件发送到您手中起,您最多只有 14 天的时间来下载您的文件,否则,您将不得不重新申请。

截图:在 chrome 中下载文件“my_spotify_data.zip”
恭喜你,你现在在 Spotify 上有了你的个人资料。我希望你不必等太多天。
在 Spotify 上阅读您的播放历史
当你解压你下载的文件时,你会发现一个名为“我的数据”的文件夹,它包含了 JSON 格式的不同文件,其中有一个非常特别的文件包含了你感兴趣的信息:你的播放历史,文件名为“streaminghistory 0 . JSON”。

截图:解压后的“my_spotify_data.zip”文件
如果你的账户比我的账户有更多的活动,你很可能会发现自己有不止一个文件,后面是下一个数字。比如:“StreamingHistory 1 。json》、《流历史 2 。json”等。Spotify 会将你的全部历史记录分成不超过 1.5 MB 的文件。一个需要考虑的细节:Spotify 只保留你的数据和历史记录一年。拥有一个完整的历史记录是很棒的,例如,我使用这个平台刚刚超过六年,但是现在,分析去年的数据就足够了。
现在,您可以开始创建新的 R 脚本了。除了读取 JSON 格式的历史文件之外,首先要确定将要使用的库的负载。
# REQUIRED LIBRARIESlibrary(jsonlite)
library(lubridate)
library(gghighlight)
library(spotifyr)
library(tidyverse)
library(knitr)
library(ggplot2)
library(plotly)# READING JSON STREAMING HISTORYstreamHistory <- fromJSON("StreamingHistory0.json", flatten = TRUE)
你会发现,你的历史基本上包含了四个变量,你将与这些变量一起玩【结束时间】【艺人名称】【曲目名称】****【ms played】。
你还会注意到你已经加载了“spotifyr”库,我现在告诉你的只是一个介绍,它属于用户在 GitHub“Charlie 86”中创建的包,它将允许你轻松地使用 Spotify API 来获得额外的细节。除了本文中的例子,您还可以在库中找到其他有趣的例子:https://github.com/charlie86/spotifyr。
在哪些日期,你在 Spotify 上听了或多或少的音乐?
这是你可以回答的第一个问题。从我们的变量开始,你可以定义小时,分钟和秒,以及基本上任何时间性。你可以先画一张图,观察你在 Spotify 上全年的活动行为,比如每周一次。
# ADDING DATE AND TIMINGmySpotify <- streamHistory %>%
as_tibble() %>%
mutate_at("endTime", ymd_hm) %>%
mutate(endTime = endTime - hours(6)) %>%
mutate(date = floor_date(endTime, "day") %>% as_date, seconds = msPlayed / 1000, minutes = seconds / 60)# PLAYBACK ACTIVITY PER WEEK AND HOURSstreamingHours <- mySpotify %>%
filter(date >= "2020-01-01") %>%
group_by(date) %>%
group_by(date = floor_date(date, "week")) %>%
summarize(hours = sum(minutes) / 60) %>%
arrange(date) %>%
ggplot(aes(x = date, y = hours)) +
geom_col(aes(fill = hours)) +
scale_fill_gradient(low = "yellow", high = "red") +
labs(x= "Date", y= "Hours of music playback") +
ggtitle("On what dates I've listened to more or less music on Spotify?", "Playback activity per week")
streamingHours
结果,你会得到一个类似下面的图,在我的例子中,Spotify 的使用在 4 月份之后的减少是非常明显的。也许在很大程度上,这是因为在 2020 年 4 月,当我们被新冠肺炎强迫在家工作时(至今),我最喜欢听音乐的时候是在办公室,而今天在家工作的我们轮流在他们的平台上播放他们的音乐。

Spotify 上你的历史和音乐品味的数据分析和可视化——每周播放时间图
你在哪些日期或多或少听了某个特定艺术家的音乐?
另一个问题,你可以用一个非常简单的方式来回答,并用一个折线图来可视化,你也可以使用 "gghighlight" 库,并通过 artistName 变量来突出显示你正在寻找的结果。
# PLAYBACK ACTIVITY PER SPECIFIC ARTISThoursArtist <- mySpotify %>%
group_by(artistName, date = floor_date(date, "month")) %>%
summarize(hours = sum(minutes) / 60) %>%
ggplot(aes(x = date, y = hours, group = artistName)) +
labs(x= "Date", y= "Hours of music playback") +
ggtitle("On what dates I've listened to more or less music by a specific artist?", "E.g: Alton Ellis and Jarabe de Palo") +
geom_line() +
gghighlight(artistName == "Alton Ellis" || artistName == "Jarabe De Palo")
hoursArtist
例如,在我的例子中,我选择了两个我经常听的艺术家:“Alton Ellis”,一个 rocksteady 流派图标,和“Jarabe de Palo”,一个西班牙语摇滚图标,结果获得了下面的情节。有一个非常臭名昭著的峰值听后者,因为只有在 2020 年 6 月,他的歌手,Pau Donés,不幸去世,这一天,我播放了他的许多歌曲,以示敬意。

Spotify 上您的历史和音乐品味的数据分析和可视化——按日期和特定艺术家显示播放时间
你在 Spotify 上听得最多的艺术家是哪些?
你可以设定一个最小播放时间,并从中找出你听得最多的艺术家。例如,让我们说,你认为听得最多的是那些你花了至少 3 个小时或更多时间播放的艺术家。
# MOST LISTENED ARTISTS (MORE THAN 3 HOURS)minutesMostListened <- mySpotify %>%
filter(date >= "2020-01-01") %>%
group_by(artistName) %>%
summarize(minutesListened = sum(minutes)) %>%
filter(minutesListened >= 180) %>%
ggplot(aes(x = artistName, y = minutesListened)) +
geom_col(aes(fill = minutesListened)) +
scale_fill_gradient(low = "yellow", high = "red") +
labs(x= "Artist", y= "Minutes of music playback") +
ggtitle("What were the most listened artists on my Spotify?", "> 3 hours listened") +
theme(axis.text.x = element_text(angle = 90))
minutesMostListened
你会得到一个类似于下图的情节,在其中你可以查看这些艺术家。就我而言,像你们中的许多人一样,我和我的女朋友分享我的账户,所以我可以主要观察各种各样的拉丁流行艺术家。

Spotify 上你的历史和音乐品味的数据分析和可视化——最受欢迎艺术家图
您在 Spotify 上的播放活动在什么时候最多?
你可以通过热图查看你完整历史的活动日志,观察这个习惯是如何随着时间而改变的。
# PLAYBACK ACTIVITY BY DATE AND TIME OF DAYtimeDay <- mySpotify %>%
filter(date >= "2020-01-01") %>%
group_by(date, hour = hour(endTime)) %>%
summarize(minutesListened = sum(minutes)) %>%
ggplot(aes(x = hour, y = date, fill = minutesListened)) +
geom_tile() +
labs(x= "Time of the day", y= "Date") +
ggtitle("When has there been more playback activity on my Spotify?", "Activity by date and time of day") +
scale_fill_gradient(low = "yellow", high = "red")
timeDay
因此,你会发现下面的情节,其中一个非常直观的方式,你可以看到,如果有变化,在你平时听音乐的时间。以我为例,2020 年 4 月后,主要在晚上听音乐的习惯改变了。

Spotify 上您的历史和音乐品味的数据分析和可视化——按日期和时间划分的活动图
您还可以创建一个条形图,以便更详细地查看一天中您的帐户上最高活动记录的时间。
# PLAYBACK ACTIVITY BY TIME OF THE DAYhoursDay <- mySpotify %>%
filter(date >= "2019-01-01") %>%
group_by(date, hour = hour(endTime), weekday = wday(date, label = TRUE))%>%
summarize(minutesListened = sum(minutes))hoursDay %>%
ggplot(aes(x = hour, y = minutesListened, group = date)) +
geom_col(fill = "#ff6600") +
labs(x= "Time of the day", y= "Minutes of music playback") +
ggtitle("What time of day I've listened to the most music on Spotify?", "Activity from 0 to 24 hours")
你将能够观察到复制的累积分钟数更多的具体时间。以我为例,一天中没有一段时间我不使用我的帐户至少几分钟来听一些东西,即使是在失眠的时候。

Spotify 上您的历史和音乐品味的数据分析和可视化——按时间划分活动
一周中的哪几天你的 Spotify 播放量最大?
您也可以通过创建另一个热图来回答这个问题,但这一次可视化星期几和一天中的时间之间的关系。
# PLAYBACK ACTIVITY BY TIME OF THE DAY AND WEEKDAYhoursDay %>%
group_by(weekday, hour) %>%
summarize(minutes = sum(minutesListened)) %>%
ggplot(aes(x = hour, weekday, fill = minutes)) +
geom_tile() +
scale_fill_gradient(low = "yellow", high = "red") +
labs(x= "Time of the day", y= "Weekday") +
ggtitle("What weekday and time of day I've listened to the most music on Spotify?", "Weekly activity from 0 to 24 hours")
您将能够观察到一周中的哪一天标志着您的帐户中更大的活动趋势。例如,在我的例子中,我可以清楚地看到在星期二、星期四和星期五有更多的活动记录。

Spotify 上您的历史和音乐品味的数据分析和可视化——按一天中的小时和一周中的天绘制活动图
查看之前获得的详细信息的另一种方法是创建一个折线图。
# PLAYBACK ACTIVITY BY TIME OF THE DAY AND WEEKDAY - LINE CHARTweekDay <- hoursDay %>%
group_by(weekday, hour) %>%
summarize(minutes = sum(minutesListened)) %>%
ggplot(aes(x = hour, y = minutes, color = weekday)) +
geom_line() +
labs(x= "Time of the day", y= "Minutes of music playback") +
ggtitle("What weekday and time of day I've listened to the most music on Spotify?", "Line chart - Weekly activity from 0 to 24 hours")
weekDay
从我的情况来看,周日绝对是我听音乐最少的日子。结果你会得到一个类似下图的图,你也可以看到哪一天你的播放活动最少。

Spotify 上您的历史和音乐品味的数据分析和可视化——按一天中的小时和一周中的天绘制活动图
尽管在获得之前的可视化结果后,这种情况将被排除,但您也可以创建另一个图表,以查看您的帐户中哪一天(工作日或周末)的活动最频繁。
# PLAYBACK ACTIVITY BY DAY TYPEdayType <- hoursDay %>%
mutate(day_type = if_else(weekday %in% c("Sat", "Sun"), "weekend", "weekday")) %>%
group_by(day_type, hour) %>%
summarize(minutes = sum(minutesListened)) %>%
ggplot(aes(x = hour, y = minutes, color = day_type)) +
geom_line() +
labs(x= "Time of the day", y= "Minutes of music playback") +
ggtitle("What day type I've listened to the most music on Spotify?", "Weekday and weekend activity from 0 to 24 hours")
dayType
然后,您将获得一个类似于下图的图,通过一天中的类型和时间来识别您的帐户的活动。

Spotify 上您的历史和音乐品味的数据分析和可视化——按一天中的时间和类型划分的活动图
让我们来玩玩“spotifyr”:在 R 中设置一个新的应用程序
是时候看看“spotifyr”包是如何工作的了。正如我在开头提到的,基本上这个包可以让我们连接到 Spotify API 。Spotify 存储了关于其平台上的歌曲、艺术家和专辑的非常有趣的数据,如流行度、能量、效价、它的“可跳舞”程度等。
为了能够通过“spotifyr”开始消费这些数据,你要做的第一件事是从 r 中的应用程序建立一个连接,为此你主要需要获得两个参数: SPOTIFY_CLIENT_ID 和 SPOTIFY_CLIENT_SECRET 。这些是从 Spotify 开发者门户网站获得的,在 https://developer.spotify.com/dashboard/applications创建了一个新的应用程序,你将被要求使用与你的 Spotify 帐户相同的凭证登录。

截图:Spotify 网站上的仪表盘开发者
登录后,您会发现一个仪表板,您可以在其中创建和管理您创建的应用程序。你必须点击“创建一个应用程序”按钮来建立一个新的应用程序,并为它分配一个名称和描述。

截图:在 Spotify 上创建一个新应用
然后,您将立即看到我们需要的两个参数,客户端 ID 和客户端密码。显然,我编辑了下面作为例子的图片,但是你的应该是完整的。

截图:Spotify 上一个新应用中的客户端 ID 和客户端机密
“spotifyr”软件包的作者在他的知识库中让我们知道了以下内容,我引用如下:
对于某些功能和应用程序,你需要以 Spotify 用户的身份登录。为此,您的 Spotify 开发人员应用程序需要有一个回调 URL。您可以将它设置为任何适合您的应用程序的值,但是一个好的默认选项是 http://localhost:1410/
换句话说,最后,您必须添加 URL 回调(http:// localhost:1410/),单击绿色的“编辑设置”按钮,然后保存更改。

截图:在 Spotify 的新应用中设置回拨 URL
很好,现在您已经准备好开始使用“spotifyr”了。在您的 R 脚本中,您必须首先执行一个函数,以便使用 Spotify 对其进行授权。
# ESTABLISH CONNECTION SPOTIFY APISys.setenv(SPOTIFY_CLIENT_ID = 'YOUR_CLIENT_ID_HERE')
Sys.setenv(SPOTIFY_CLIENT_SECRET = 'YOUR_CLIENT_SECRET_HERE')get_spotify_authorization_code()
一旦执行了" get _ Spotify _ authorization _ code()"函数,您将在浏览器中看到一个新窗口,您的应用程序将在该窗口中请求权限。

截图:接受 Spotify 上的应用权限
接受时,您将立即看到一个文本,表明身份验证成功,因此您现在可以关闭该窗口,返回到 r。

截图:Spotify 上的认证
这里一切都好吗?很好,所以现在,您可以利用“spotifyr”从 r。
在特定的播放列表中,你最不喜欢听的歌曲是什么?
在回答这个问题的同时,你可以给出用“spotifyr”可以获得的数据的第一种方法。亲爱的读者,我很确定你和我一样,有一个或多个播放列表,或者是你最喜欢的歌曲,或者是你最喜欢的流派,或者是为了那些特殊的场合。在 Spotify 上登录您的帐户,从您的库中选择任何播放列表,并复制其 URL 的唯一标识符。

截图:浏览器中 Spotify 上的播放列表
例如,我有一个播放列表,其中有我最喜欢的英文歌曲,我将以该播放列表为例,但你可以对任何人这样做。为此,除了您的用户名之外,您还需要我提到的那个标识符,以将它们作为参数传递,并使用" get _ playlist _ audio _ features()"函数,使用该函数,您将从生成新数据帧的播放列表中获得大量信息。
# GET SPECIFIC PLAYLIST FEATURESplaylist_username <- 'cosmoduende'
playlist_uris <- c('0PV31w7ireeI6d0oSYJ2H0')
playlistFavsEnglish <- get_playlist_audio_features(playlist_username, playlist_uris)
停下来看看这个新数据框包含的每个变量会花费我们很长时间,我建议您稍后仔细看看它在 R 中的内容。你会惊讶于你能处理的有价值的数据。
回到最初的问题,你可以用“track . popularity”来回答这个问题,它包含了每首歌的流行度,范围从 0 到 100。考虑到这一点,你可以确定最不受欢迎的曲目是那些从 0 到 35 的曲目,并想象哪些是你在 Spotify 的播放列表中听到的稀有曲目。
# PLOT LESS POPULARITY TRACKS ON SPECIFIC PLAYLISTplaylistFavsEnglish %>%
group_by(track.popularity) %>%
filter(track.popularity <= "35") %>%
ggplot(aes(x = track.name, y = track.popularity)) +
geom_col(aes(fill = track.album.name)) +
labs(x= "Track name", y= "Popularity") +
ggtitle("What are the least popular songs I listen to on Spotify?", "Popularity ranking < 35 in a specific playlist") +
theme(axis.text.x = element_text(angle = 90))
结果,你会得到一个类似于下图的图,你可以看到哪些是你选择的播放列表中那些不太受欢迎的歌曲。显然,根据播放列表的大小,您可能会获得更多或更少的曲目。

Spotify 上您的历史和音乐品味的数据分析和可视化——特定播放列表中不太受欢迎的歌曲的图表
根据你添加为“喜欢”的歌曲,你最喜欢的 10 位艺术家是谁?
你可能已经注意到,在 Spotify 的许多应用中,给你听的歌曲甚至整张专辑“点赞”也就是“♥”并不陌生。无论是从您的桌面应用程序,从应用程序,还是从网络版本,它总是可用的。

截图:伊基·波普赛道上的“赞”
这极大地允许 Spotify 改进其推荐算法,如果你想知道的话,能够根据你的口味对你可以听的其他内容提出新的建议。
你可以在https://open.spotify.com/collection/tracks查看你添加到 Spotify 账户的所有歌曲

截图:你在 Spotify 上“喜欢”的歌曲集
是时候使用“get _ my _ saved _ tracks()”函数来获取关于您在某个时候“喜欢”的歌曲的完整集合的所有信息,从而生成包含许多新信息的新数据帧。Spotify 设置了请求限制,每次请求最多可以获得 50 个项目,但你可以通过“偏移”获得更多项目,跳过项目并获得更多项目。
# GET FAVORITE TRACKSmyFavTracks <- ceiling(get_my_saved_tracks(include_meta_info = TRUE)[['total']] / 50) %>%
seq() %>%
map(function(x) {
get_my_saved_tracks(limit = 50, offset = (x - 1) * 50)
}) %>%
reduce(rbind) %>%
write_rds('raw_myFavTracks.rds')
如果你记得自从你在 Spotify 上开了账户后,你给了“赞”的第一首歌曲是什么,那会很有趣。您将使用包 "lubridate" 来处理日期,将日期从字符类转换成日期类。
# GET FIRST ADDED LIKED TRACKmyFavTracks %>%
mutate(added_at = ymd_hms(added_at)) %>%
arrange(added_at) %>%
head(1, wt = added_at) %>%
select(track.name, track.album.name, added_at) %>%
kable()
以我为例,我可以看到第一首歌是 2015 年 Vicentico 的,他是我最喜欢的“西班牙摇滚”乐队之一“Los Fabulosos Cadillacs”的主唱。

屏幕截图:控制台显示第一首歌曲被添加为“喜欢”
变量 "track.artists" 存储为一个列表,记住不是所有的歌曲都是由一个艺术家创作的,所以你需要从列表中提取到列表中。在这种情况下,您将选择“id”作为艺术家品牌,以避免名称重复,然后,您将添加“n”,作为曲目数量列。最后,您可以创建图来查看获得的结果。为了避免改变频率组的顺序,我建议将类别变量转换为因子变量。
# GET TOP ARTISTS BASED ON LIKED TRACKSfavTracksArtist <- myFavTracks %>%
select(track.artists) %>%
reduce(rbind) %>%
reduce(rbind) %>%
select(id, name)trackNumArtist <- favTracksArtist %>%
count(id, sort = TRUE) %>%
left_join(favTracksArtist, by = 'id',.) %>%
unique() %>%
select(-id) %>%
top_n(10, n)# PLOT TOP 10 ARTISTS BASED ON LIKED TRACKSplotMyFavs <- trackNumArtist %>%
mutate(freq = case_when(n > 100 ~ '> 100 tracks',
between(n, 50, 99) ~ '50-99 tracks',
between(n, 20, 49) ~ '20-49 tracks',
TRUE ~ '< 20 tracks')) %>%
mutate(freq = factor(freq, levels = c('> 100 tracks', '50-99 tracks', '20-49 tracks', '< 20 tracks'))) %>%
ggplot(mapping = aes(x = reorder(name, -n), y = n, fill = freq)) +
geom_col() +
scale_fill_brewer(palette="Dark2") +
labs(x= "Artist name", y= "Number of tracks", fill = NULL) +
ggtitle("What are my Top 10 favorite artists?", "Based on my ♥ tracks") +
theme(axis.text.x = element_text(angle = 90))
plotMyFavs
你将得到一个类似于下图的结果,在其中你可以看到你最喜欢的 10 位艺术家,按照你“喜欢”的每一位艺术家的曲目数量进行分类。是的,举例来说,你可以看出我是本伯里的粉丝。

Spotify 上您的历史和音乐品味的数据分析和可视化——基于喜欢的曲目的十大最喜欢的艺术家图
告诉我你听谁的…我会告诉你你怎么样
我们特别被某些艺术家或某些音乐流派所吸引,因为我们感觉被认同了,所以我们把它作为我们自己和我们个人文化的一部分。在我看来,尤其是音乐可以揭示你的个性。
继续上面获得的信息,看看你最喜欢的艺术家在他们的音乐创作中表达了什么样的感受也是很有趣的。它们传达和平、喜悦、愤怒、悲伤吗?好吧,你可以用 Spotify 建立的两个衡量标准来找出答案:价态和能量。由 Spotify 定义,valence 定义如下:
从 0.0 到 1.0 的测量值,描述轨道所传达的音乐积极性。高价曲目听起来更积极(例如,快乐、愉快、欣快),而低价曲目听起来更消极(例如,悲伤、沮丧、愤怒)。
另一方面,能量对它的定义如下:
能量是一种从 0.0 到 1.0 的度量,代表强度和活动的感知度量。通常,高能轨道感觉起来很快,很响,很嘈杂。例如,死亡金属具有高能量,而巴赫前奏曲在音阶上得分较低。对该属性有贡献的感知特征包括动态范围、感知响度、音色、开始速率和一般熵。
通过这两个测量,您可以创建一个带有散点图的象限来揭示这些信息。你这次可以利用“get _ artist _ audio _ features()”函数,它基本上期望你的艺人名字作为参数,返回给你很多关于她的专辑和曲目的信息,包括价态和能量。举例来说,只取你以前获得的前 10 名中最喜欢的前四名艺术家,稍后在“情感象限”中创建和可视化所获得的结果。
# GET FEATURES TOP FOUR FAVORITE ARTISTSfavArtist1 <- get_artist_audio_features(artist= "Bunbury")
favArtist2 <- get_artist_audio_features(artist= "Kevin Johansen")
favArtist3 <- get_artist_audio_features(artist= "Vicentico")
favArtist4 <- get_artist_audio_features(artist= "Los Auténticos Decadentes")# MAKE A SINGLE DATA FRAMEtopFourArtists <- rbind(favArtist1, favArtist2, favArtist3, favArtist4)# PLOT EMOTIONAL QUADRANT TOP FOUR ARTISTSemotionalQuadrant <- ggplot(data = topFourArtists, aes(x = valence, y = energy, color = artist_name)) +
geom_jitter() +
geom_vline(xintercept = 0.5) +
geom_hline(yintercept = 0.5) +
scale_x_continuous(expand = c(0, 0), limits = c(0, 1)) +
scale_y_continuous(expand = c(0, 0), limits = c(0, 1)) +
annotate('text', 0.25 / 2, 0.95, label = "Angry / Turbulent") +
annotate('text', 1.75 / 2, 0.95, label = "Joyful / Happy") +
annotate('text', 1.75 / 2, 0.05, label = "Peace / Chill") +
annotate('text', 0.25 / 2, 0.05, label = "Depressing / Sad") +
labs(x= "Valence", y= "Energy") +
ggtitle("Emotional quadrant Top four artists", "Based on energy y valence")
emotionalQuadrant
你可以看到,每个点代表一个特定艺术家的一个轨迹,显示她表达了什么感觉。考虑到你正在查看每个艺术家的整个唱片目录,或者至少是 Spotify 记录的所有专辑。你觉得呢,这也揭示了你的个性吗?

Spotify 上您的历史和音乐品味的数据分析和可视化——“情感象限”图,根据喜欢的曲目列出 10 位最喜欢的艺术家中的前四位艺术家
非常感谢您的善意阅读。和我的大多数文章一样,我在一个 flexdashboard 中分享了用plotely生成的情节,我把它放在一起更美观一点:https://rpubs.com/cosmoduende/spotify-history-analysis
在这里你可以找到完整的代码:https://github.com/cosmoduende/r-spotify-history-analysis
感谢你坚持到最后,祝你分析非常愉快,可以把一切都付诸实践,对结果感到惊讶,和我一样开心!
使用 ggplot2 可视化浏览数据集
原文:https://towardsdatascience.com/exploring-a-dataset-visually-with-ggplot2-501d1399bc52?source=collection_archive---------19-----------------------
ggplot2 数据可视化库实用指南

约书亚·厄尔在 Unsplash 上的照片
数据可视化是探索性数据分析的有力工具。我们可以用它来揭示数据中的潜在结构或变量之间的关系。基本描述性统计的概述也可以从数据可视化中获得。
数据可视化是数据科学领域的重要内容。因此,这个领域中有许多库和包。尽管它们创建可视化的语法和方法不同,但最终目标是相同的:探索和理解数据。
在本文中,我们将使用 R 编程语言的 ggplot2 库来探索一个医疗成本数据集。数据集非常适合实践,因为它包含了不同数据类型的变量。
我们在本文中介绍的内容也可以被视为 ggplot2 库的实用指南。我将尝试清楚地解释生成图背后的逻辑,以便您可以将其应用于其他任务和数据集。
我使用 R-studio,这是一个非常流行的 R 编程语言 IDE。第一步是导入库并读取包含数据集的 csv 文件。
> library(ggplot2)
> library(readr)
> insurance <- read_csv("Downloads/datasets/insurance.csv")

保险表(图片由作者提供)
该数据集包含保险公司客户的个人信息以及他们的保险费用。
在 ggplot2 中,我们首先使用 ggplot 函数创建一个坐标系作为基础层。然后,我们通过指定绘图类型和要绘制的变量来添加层。我们还可以添加进一步的说明,以使可视化更加丰富。
我们的数据集中的目标变量是 charges 列,所以最好从研究这个列开始。我们可以用直方图来显示分布情况。
直方图将连续变量的值域划分为离散的区间,并计算每个区间中的观察值数量。
> ggplot(insurance) + geom_histogram(mapping = aes(x=charges), color='blue', fill='lightblue')
- 我们将数据传递给 ggplot 函数,该函数创建一个坐标系作为基础层。
- geom_histogram 函数通过根据给定参数绘制直方图,在此坐标系上添加一个图层。我们使用映射参数来指定要绘制的列。颜色和填充参数与绘图的视觉属性相关。

电荷直方图(图片由作者提供)
收费大多低于 2 万。随着该值变高,观察次数减少。让我们把吸烟者和不吸烟者分开,让这个情节更有知识性。
> ggplot(insurance) + geom_histogram(mapping = aes(x=charges, color=smoker), bins = 15)
我们将用作分隔符的列传递给 aes 函数中的颜色参数。请注意,它不同于我们之前使用的颜色参数,它不在 aes 函数中。
柱参数用于指定直方图中柱的数量。

电荷直方图(图片由作者提供)
我们清楚地看到,吸烟者比不吸烟者被收取更多的保险费。
ggplot 这个名字来源于 grammar of graphics 。
我们可以通过添加其他分类变量(如性别和地区)来进一步分隔 charges 列。
ggplot 库提供了 facet_grid 函数来生成子图网格。
> t <- ggplot(insurance) + geom_histogram(mapping = aes(x=charges, color=smoker), bins=15, fill='white')> t + facet_grid(rows = vars(sex), cols = vars(region))
第一行像前面的例子一样,通过区分吸烟者和非吸烟者创建了一个直方图。在第二行中,我们根据行和列添加了两个额外的维度。用作分隔符的变量在 facet_grid 函数中指定。

直方图网格(图片由作者提供)
我们没有观察到地区之间的显著差异,但我们看到男性比女性更有可能吸烟。
有不同类型的图来研究变量之间的关系。其中之一是散点图,这通常是在比较两个数值变量的情况下首选。
让我们创建一个绘制电荷和 bmi(身体质量指数)之间关系的网格图。我们将使用吸烟者和儿童列作为分隔符。
> t <- ggplot(insurance) + geom_point(mapping = aes(x=charges, y=bmi, color=smoker))> t + facet_grid(rows = vars(children))

散点图(图片由作者提供)
geom_point 函数生成散点图。我们看不出收费和 bmi 栏之间有明显的关联。然而,吸烟者和不吸烟者是明显分开的。
另一种常用的可视化类型是箱线图。它通过显示值如何以四分位数和异常值的形式分布来概述变量的分布。
下面的代码生成 bmi 列的箱线图。我们将根据 region 列中的类别来区分观察值。
> ggplot(insurance) + geom_boxplot(mapping = aes(y=bmi, color=region))

bmi 的箱线图(图片由作者提供)
方框中间的线表示中间值。盒子的高度与数值的分布成正比。
东南地区人民的平均体重指数明显高于其他地区的平均水平。东北和西北挺像的。
可视化也可以用来检查不同类别在观察数量方面的大小。只考虑平均值会误导我们,所以最好知道每一类中的观察数量。
ggplot 的 geom_count 函数可用于可视化两个离散变量。例如,显示 region 和 sex 列中类别大小的绘图生成如下。
> ggplot(insurance) + geom_count(mapping = aes(x=region, y=sex, color=region)) + labs(title = "Number of Observations in Each Region")
我们还使用实验室功能添加了一个标题。它还允许为 x 轴和 y 轴添加标签。

计数图(图片由作者提供)
东南部明显超过其他地区。另一个有趣的发现是,西北地区女性多于男性,而东北地区男性多于女性。
结论
我们已经看到了如何使用数据可视化来探索数据集。当然,我们可以生成更多的图来进一步研究变量之间的关系。
随着数据集的复杂性和维数的增加,我们在探索性数据分析中使用更复杂的可视化。然而,基本类型的情节和技术可能是相同的。
感谢您的阅读。如果您有任何反馈,请告诉我。
使用 Python 探索音频数据集
原文:https://towardsdatascience.com/exploring-audio-datasets-with-python-498c295f73d9?source=collection_archive---------16-----------------------
创建一个简单的 GUI 来浏览大型数据集
影像数据集可以轻松浏览。即使我们有数百张图片,我们也可以滚动浏览目录,浏览数据。这样,我们可以很快注意到有趣的属性:颜色、位置、一天中的时间。然而,如果我们对音频数据集使用同样的策略,我们不会走得太远。相反,我们必须一次听一个文件或者跳过一个文件。对于大量的样本,这种方法是禁止的。
一种解决方案是将音频文件可视化。我们不是按顺序听样本,而是画出不同的特征。最基本的图是波形图,如下例所示:

几个音频样本的波形可视化。图片由作者提供。
我们可以看到第一行中的样本都显示出相似的模式。还有,对于第二排来说,声音似乎集中在开头。然后,切换到频谱图表示,我们可以进一步检查样本:

几个音频样本的声谱图的可视化。图片由作者提供。
在 y 轴上,我们看到频率,在 x 轴上,我们看到时间。最后,颜色保存了最后的信息:越亮,这个区域拥有的能量越多。对于我们的第二排,能量似乎集中在前 2、3 秒。
然后,在下一步中,我们取一个样本并同时可视化几个转换,如下所示:

不同可视化技术的综合视图。图片由作者提供。
一遍又一遍地手动完成所有这些是乏味的。但是,多亏了 python 和 streamlit,我们可以开发一个简单的 GUI,在浏览器中呈现。这里可以现场试一下。

探索音频数据集的图形用户界面。图片由作者提供。你可以在这里现场试用,在这里在 GitHub 上找代码。
这个脚本让我们在侧边栏上选择一个可视化类型,然后在主屏幕上可视化每个类的一些示例。对于上面显示的音频样本,我使用了流行的 ESC-50 数据集的子集。该脚本的主要逻辑由 Akhil Vasvani 大大改进,如下所示:
我们导入所有必需的包和负责创建不同类型可视化的 python 脚本。从第 11 行开始,我们构建了你在上面看到的工具条。在这个侧边栏中,我们创建了一个带有七个不同可视化选项的单选按钮,并添加了一个复选框来显示可选绘图的文件名。如果我们发现了一个特别感兴趣的样本,并希望对其进行进一步的检查,那么这个选项是很有帮助的。从第 24 行开始,我们迭代我们的单选按钮并创建选中的可视化。
如果你想为你的音频数据集使用脚本,你必须采用文件 audio_loading_utils 中的 get_dir_overview() 方法。它创建了一个 label- > samples 映射,用于选择稍后要可视化的文件。
总而言之,脚本保持相对简单,有许多重复的功能。如果你想添加自己的可视化效果,你可以在GitHub 库中找到完整的代码。
现在我们有了一个脚本和简洁的 GUI 来探索音频数据集,我们可以用它来做什么呢?
如果我们将我们的音频分成片段,我们可以确定片段的长度:它必须足够长以捕捉相关的特征。
如果我们使用光谱图,我们可以评估缩放比例(线性、对数、Mel)或箱数(对于 Mel)。
此外,如果我们使用 MFCCs,我们可以检查系数数量的影响。
最后但同样重要的是,我们可以通过以下方式扩展脚本:
- 添加更多的可视化效果(看看可能的功能选择这里
- 使参数可选(FFT 大小等。)
- 可视化增强(如果您感兴趣,请从这里的和这里的开始)
探索 BERT 变体(第 1 部分):ALBERT,RoBERTa,ELECTRA
原文:https://towardsdatascience.com/exploring-bert-variants-albert-roberta-electra-642dfe51bc23?source=collection_archive---------4-----------------------
对伯特家族的一次温柔的深度探索……

由纳迪·博罗迪纳在 Unsplash 上拍摄
我的一个朋友最近开了一家生产食用油的公司。很快,他意识到这个行业竞争太激烈了,为了在残酷的市场中生存,他需要开拓第二种商业模式。这时候,肥皂制造的想法出现了。他意识到肥皂可以作为食用油加工的副产品来生产,而且利润丰厚。最后,他的生意开始蒸蒸日上。
我称之为进化!!
谁会想到,亚马逊这个曾经卖书的公司会钻研利润惊人的云业务。
有时,进化会带来比最初想法更多的东西。
机器学习在过去十年里突飞猛进。尽管具有 ML 前景的每个子领域都有大幅增长,但 NLP 尤其增长最快,NLP 中最大的战略突破是“变形金刚”和“伯特”。毫无疑问,他们已经成为一个杠杆,将 NLP 领域推向更大、更强大的领域。
只是为了刷新关于 BERT 的记忆,这里有一个关于 NLP 作为一个域从 BoW,TFIDF,Word2Vec,RNN,LSTM 到最后 BERT 的增长的故事情节——珍惜旅程。
https://medium.com/analytics-vidhya/attention-transformer-and-bert-a-simulating-nlp-journey-2a4abbfb6e74
这种发展导致了行业中常用的 BERT 模型的多种变体。在深入研究各种变体之前,有必要修改几个 BERT 概念。
-
BERT 基本模型有 12 层,110M 参数,768 个隐层和等嵌入层。这种大尺寸使得训练的计算量非常大。
-
MLM 和下一句预测(NSP) :伯特通过对几项任务进行预训练来利用双向性——掩蔽语言模型(MLM)和下一句预测(NSP)
a. MLM :在 MLM,模型屏蔽了输入中的一些值,并试图根据上下文预测被屏蔽(缺失)的单词。这里的想法是不要以特定的顺序使用单词,而是以深度双向的方式进行预训练。
b. NSP :在训练过程中,模型得到输入的句子对,它还学习预测第二个句子是否是原文中的下一个句子。
现在基础已经建立,让我们从一行程序开始讨论变体。

作者图片
鉴于文章的篇幅,我选择将本文分为两部分。。在这一部分,我们将深入探讨前三个变体,即 ALBERT、RoBERTa 和 ELECTRA。第二部分将涵盖其余三个。下面是第二部分的链接:
https://durgiachandan.medium.com/exploring-bert-variants-part-2-spanbert-distilbert-tinybert-8e9bbef4eef1
所以,让我们开始吧:
1.阿尔伯特:如前所述,BERT base 包含 1.1 亿个参数,这使得它的计算量很大,因此需要一个精简的版本。艾伯特模型有 1200 万个参数,768 个隐藏层和 128 个嵌入层。正如所料,较轻的模型减少了训练时间和推理时间。为了获得较少的参数集,使用了以下两种技术:
a.跨层参数共享:在此方法中,仅学习第一个编码器的参数,所有编码器使用相同的参数。这些参数可以共享给:
I .仅前馈层
二。前馈和多头关注层
三。只有多头关注层。
b.因式分解嵌入层参数化:嵌入层不是保持 768,而是因式分解减少到 128 层。人们可以认为这种分解类似于推荐系统中常用的矩阵分解。如果你想恢复记忆,这里有一篇很好的文章。
除了阿尔伯特很轻,不像伯特研究 NSP,阿尔伯特研究一个叫做 SOP(句子顺序预测)的概念。NSP 和 SOP 之间的关键区别在于,对于 NSP,在训练期间,该模型获得输入的句子对,并学习预测第二句话是否是原文中的下一句话。SOP 是一个“分类模型”,其目标是“分类”两个给定的句子是否互换,即它们的顺序是否正确。
- RoBERTa 代表“稳健优化的 BERT 预训练方法”。在许多方面,这是伯特模型的一个更好的版本。区别的要点如下:
a.动态屏蔽 : BERT 使用静态屏蔽,即句子的相同部分在每个时期被屏蔽。相反,RoBERTa 使用动态屏蔽,其中对于不同的时期,句子的不同部分被屏蔽。这使得模型更加健壮。
b.移除 NSP 任务:据观察,NSP 任务对于伯特模型的预训练不是很有用。因此,罗伯塔只带着 MLM 的任务。
c.更多数据点 : BERT 在“多伦多图书语料库”和“英语维基百科数据集”上进行了预训练,即总共 16 GB 的数据。相比之下,除了这两个数据集,RoBERTa 还在其他数据集上接受了训练,如 CC-News(公共抓取新闻)、Open WebText 等。这些数据集的总大小约为 160 GB。
d.大批量:为了提高模型的速度和性能,RoBERTa 使用了 8000 的批量和 300000 个步骤。相比之下,BERT 使用的批量大小为 256,步长为 100 万。
- ELECTRA 代表“有效地学习一个编码器,该编码器准确地对令牌替换进行分类”。该模型使用生成器-鉴别器结构。除了是 BERT 的较轻版本之外,ELECTRA 还具有以下显著特征:
a.替换令牌检测:ELECTRA 不使用 MLM 进行预训练,而是使用一个名为“替换令牌检测”(RTD)的任务。在 RTD 中,不是屏蔽记号,而是用错误的记号替换记号,并且期望模型进行分类,不管记号是否被错误的记号替换。
b.不执行 NSP 预训练。
c.更好的训练:ELECTRA 模型不是仅在掩码数据上进行训练,而是在完整数据上进行训练。
哼!!目前差不多就是这样。
正如他们所说“我们应该不断进化。进化并没有随着我们长出相对的拇指而结束。”令人惊讶的是,一个模型可以通过考虑组件来适应许多版本。
也许这就是保持进化的原因。
免责声明:本文中表达的观点是作者以个人身份发表的意见,而非其雇主的意见
探索 BERT 变体(第 2 部分):SpanBERT、DistilBERT、TinyBERT
原文:https://towardsdatascience.com/exploring-bert-variants-part-2-spanbert-distilbert-tinybert-8e9bbef4eef1?source=collection_archive---------22-----------------------
对伯特家庭的一次温和的深入探究

纳吉布·卡利尔在 Unsplash 上的照片
请注意,这是本系列的第 2 部分。本文将深入探讨伯特模型的三个变体的细节,即斯潘伯特、迪夫伯特、蒂尼伯特。第 1 部分涵盖了其他三个变体— ALBERT、RoBERTa 和 ELECTRA。如果你还没有读过第一篇文章,我强烈建议你也读一读。本文利用了第一篇文章中的一些基础知识,并扩展了这些概念。
以下是快速参考的链接:
让我们从三种变体中的一种开始考虑:

作者图片
所以,让我们开始吧:
1.SpanBERT : SpanBERT 是 BERT 的一个特殊变体,其目标是使用模型进行问题回答、关系提取等。为了避免任何混淆,应该注意的是,该模型不是“创建答案”,而是“找到”段落(文本块)中要回答的单词组(跨度)。直观上,这意味着 BERT 模型需要进行以下修改:
a. 【字长】掩蔽比【随机掩蔽】:
让我们考虑一个句子的例子。
Tokens = 【数据,科学,结合了,领域,专长,编程,技能,和,知识,数学,和,统计】
在 MLM 的情况下,执行随机屏蔽:
令牌= 【数据,科学,[面具],领域,[面具],编程,技能,[面具],知识,of,数学,[面具],统计】
然而,对于跨度,掩蔽是随机进行的,但是是在连续单词的跨度上进行的:
Tokens = 【数据、科学、组合、【面具】、【面具】、【面具】、【面具】、【面具】、知识、of、数学、统计】
b .更新损失函数,以便给予“单词跨度”中的记号必要的考虑
除了屏蔽逻辑的改变之外,成本函数也需要被更新以考虑以下两种情况:
- MLM 目标:预测屏蔽词,即词汇表中所有单词成为屏蔽词的概率。
2。 跨度边界目标(SBO): 对于 SBO,不采用屏蔽记号的表示,而是考虑相邻记号的表示。
在我们的例子中:
Tokens = 【数据,科学,结合了,【面具】,【面具】,【面具】,【面具】,知识,的,数学,和,统计
单词“组合”和“知识”的表示将用于导出屏蔽令牌。这进一步意味着位置嵌入将用于理解记号的相对位置。
斯潘伯特总体损失函数= MLM 目标损失+ SBO 目标损失
该模型被训练以最小化该损失函数。这个预先训练的模型可以用于任何问答任务或关系提取。
2 和 3。在你在笛卡尔曲线中扬起眉毛之前,我有一个理由来折叠这两个变体。与调整 BERT 模型的某些方面以创建新版本的其他变体不同,DistilBERT 和 TinyBERT 都以不同的方式工作。
这些变体不调整模型,而是试图通过创建一个较小的模型来减小模型的大小,该模型以某种方式复制了较大模型的输出。
这一过程被称为知识提取,即这些较小的模型提取大模型的知识,并试图以较小的规模获得相似的模型结果。较大和较小模型的通用术语是“学生”和“教师”。如果你想了解更多关于师生框架如何运作以及知识是如何提炼的,请浏览下面的文章。本文还介绍了 DistilBERT 的细节,所以我不打算在这里详细介绍。
https://towards data science . com/why-you-neural-network-models-d 43681d 9916 f
总的来说,在 DistilBERT 中,预先训练的网络(教师)被用来训练学生网络,以通过蒸馏从教师网络获得知识。
如果您对 DistilBERT 的工作方式有所了解,TinyBERT 是它的进一步扩展,除了从输出层提取知识之外,TinyBERT 还从中间层提取信息。
即
TinyBERT =蒸馏+从中间层提取的知识
下面是解释差异的图示。

图片来自作者
正如所料,TinyBERT 的损失函数不仅包括来自输出层的损失,还包括来自隐藏注意力和嵌入层的损失。
差不多就是这样。希望你喜欢这篇文章,现在所有的 BERT 变体都更加直观了。
如果您有任何问题或意见,请随时留言。
免责声明:本文中表达的观点是作者以个人身份发表的意见,而非其雇主的意见。
探索纽约市自行车共享数据
原文:https://towardsdatascience.com/exploring-bike-share-data-3e3b2f28760c?source=collection_archive---------15-----------------------
如何从 Citi Bike 访问行程数据,并使用 Python 和 Pandas 准备数据以供分析
许多自行车共享系统为那些想了解他们的系统如何使用的人提供了他们的出行数据。纽约市的自行车共享系统花旗自行车(Citi Bike)就是其中之一,但除了数据之外,他们并没有提供太多信息。我在获取和准备用于可视化的数据方面有一些经验,因此在本文中,我将向您展示如何开始使用这个丰富的数据源。

作者照片
以前,我从新泽西的郊区通勤到我在纽约市的一个办公室做产品经理,现在已经关门了,在宾州车站上面。午餐或下班后,我经常依靠纽约的自行车共享系统花旗自行车(Citi Bike)。我发现我可以比步行更快地到达市中心的目的地,甚至更远的地方,比坐公交车或地铁更便宜。当我发现 Citi Bike 将出行数据公开时,我想这可能会为我管理的数据准备产品提供一个有趣的用例。
使用真实的数据比我们一直使用的样本文件有趣得多,因为需要清除实际的异常,以使数据对分析有用,并且从数据中可以讲述有趣的故事。
旅行数据文件包含每次乘坐的记录,根据季节的不同,每月大约有 200 万条记录。这是一个传统的自行车共享系统,有固定的站点,用户在一个码头用钥匙链或密码取车,然后在另一个码头还车。每次游乐设备开始和停止时的站点和时间被记录下来。
一些关于骑手的有限信息也被记录下来:他们的性别和出生年份。Citi Bike 还区分了他们所谓的购买年卡的用户(目前的费用是 179 美元,不限时骑行 45 分钟)和购买一日卡(15 美元,不限时骑行 30 分钟)或单程卡(3 美元)的客户。
对于每一种用户类型,较长时间的乘坐都有超额费用。对顾客来说,每 15 分钟 4 美元;对于订户,每分钟 0.15 美元。这些费用似乎是为了阻止长途旅行,而不是为了增加收入。
花旗自行车系统数据页面描述了所提供的信息。每次乘坐的具体信息如下:
- 行程持续时间(秒)
- 开始时间和日期
- 停止时间和日期
- 起点站名称
- 终点站名称
- 车站 ID
- 车站纬度/经度
- 自行车 ID
- 用户类型(客户= 24 小时通票或单骑用户;订户=年度会员)
- 性别(零=未知;1 =男性;2 =女性)
- 出生年
我们想要回答的问题包括这样的问题:最常见的乘车时长是多少?一天中的哪些时间系统使用率最高?一个月内的客流量变化有多大?最常用的站有哪些?骑手都多大了?
虽然可以在行程数据文件中找到这些问题的答案,但是需要扩充数据以提供简单的答案。例如,以秒为单位的行程持续时间太细;几分钟会更有用。
多年来,我使用这些数据向客户和用户组会议做了大量的演示。我创建的净化数据被产品经理用作他们自己演示的可视化工具。
当我碰巧在另一个项目中使用 Jupyter Notebook、Python 和 Pandas 时,我决定使用这些工具来准备花旗自行车旅行数据。
Jupyter Notebook 是一个开源的基于网络的应用程序,允许你创建和共享包含代码、可视化和叙述性文本的文档。它通常用于数据准备和可视化,但也有许多其他用途。 Python 是默认使用的编程语言 Pandas 是广泛用于数据操作和分析的软件库。我还使用了 Seaborn 作为可视化数据的简单方法。
包含所有代码和输出的 Jupyter 笔记本可以在 github 上找到。
下载花旗自行车旅行数据
这些数据可以从上面提到的Citi bike trip data 的可下载文件页面上的链接下载。每个月都有一个文件,一个包含一个 csv 文件的 zip 文件。这些文件从 2013 年开始按时间顺序排列,所以要找到最近的,你必须向下滚动。但是不要一直向下滚动到底部;名字里有 JC 的文件是泽西城的,不是纽约的。
在本教程中,我使用的是 2020 年 3 月的文件,这个文件比大多数文件都要小,原因很明显。查找名称以 202003 开头的文件。
在 Windows 系统上下载文件,将其解压缩到用户目录下的新目录中。
在 Linux 上,您需要使用wget命令来下载文件,并使用unzip来扩展它。如果您没有安装它们,您可以使用 Linux 发行版的命令来安装它们,如下所示:
sudo apt-get install wget unzip # Ubuntu
sudo yum install wget unzip # RHEL and CentOS 7 and earlier
sudo dnf install wget unzip # RHEL and CentOS 8
要在 Linux 系统上获取文件,您只需要从网页上获取 URL。右键单击以 202003 开始的文件名,然后选择复制链接地址。在 Linux 命令提示符下,为这些文件创建一个目录,并切换到该目录。键入wget并右击从剪贴板复制地址。然后解压存档并删除它。
mkdir bikeshare
cd bikeshare
wget [https://s3.amazonaws.com/tripdata/202003-citibike-tripdata.csv.zip](https://s3.amazonaws.com/tripdata/202003-citibike-tripdata.csv.zip)
unzip 202003-citibike-tripdata.csv.zip
rm 202003-citibike-tripdata.csv.zip
安装 Jupyter 笔记本电脑
安装 Jupyter 有多种方法,但首先你需要安装 Anaconda,一个数据科学平台,这里也有多种选择。我用的是 Miniconda,“conda 的免费最小安装程序”,可以从 miniconda 下载。确保获得基于 Python 3 的 miniconda3。
在 Linux 上,您可以使用wget下载一个 shell 脚本,然后使它可执行并运行它。
wget [https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh](https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh)
chmod +x [Miniconda3-latest-Linux-x86_64.sh](https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh)
./[Miniconda3-latest-Linux-x86_64.sh](https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh)
然后将 minconda3 的安装位置添加到您的路径中,可以是在当前会话中,也可以是在您的配置文件中。例如:
export PATH=~/miniconda3/bin:$PATH
一旦完成,你就可以安装 Jupyter 和这里使用的其他库了。我选择用经典的 Jupyter 笔记本。更多信息参见安装 Jupyter 软件。
conda install -c conda-forge notebook pyarrow
conda install pandas seaborn
然后启动笔记本,您可以从 web 浏览器连接到它。
jupyter notebook
导入行程数据
在 Jupyter 主页上,单击之前创建的 bikeshare 目录以选择它。您应该在那里看到花旗自行车旅行数据文件。
单击“新建”创建新笔记本。然后在第一个单元格中输入下面的命令来导入所需的库。单击 Ctrl-Enter 执行命令。这些命令没有输出,括号中的数字在命令运行时变成星号,在命令完成时又变回原来的数字。
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
将行程数据文件读入 DataFrame,data frame 是内存中的对象,类似于包含行和列的表。如果该文件与您的笔记本在同一个目录中,您可以只使用所示的文件名。如果在其他地方,请使用文件名的完整路径。
df = pd.read_csv('202003-citibike-tripdata.csv')
您可以使用head()方法查看数据框中的前五行。
df.head()

从头部输出
您可以使用info()方法获得关于列的更多信息。
df.info()
输出显示了行数(刚刚超过一百万)和列数。
Count 的值都是相同的,告诉我们没有任何列缺少值。
Dtype 列显示了每一列的数据类型。int64 和 float64 类型表示 64 位整数和浮点类型值,object 表示字符串。
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1068457 entries, 0 to 1068456
Data columns (total 15 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 tripduration 1068457 non-null int64
1 starttime 1068457 non-null object
2 stoptime 1068457 non-null object
3 start station id 1068457 non-null int64
4 start station name 1068457 non-null object
5 start station latitude 1068457 non-null float64
6 start station longitude 1068457 non-null float64
7 end station id 1068457 non-null int64
8 end station name 1068457 non-null object
9 end station latitude 1068457 non-null float64
10 end station longitude 1068457 non-null float64
11 bikeid 1068457 non-null int64
12 usertype 1068457 non-null object
13 birth year 1068457 non-null int64
14 gender 1068457 non-null int64
dtypes: float64(4), int64(6), object(5)
memory usage: 122.3+ MB
None
对于数值字段,您还可以使用describe()方法查看数据是如何分布的。它显示了最小值和最大值、平均值和中值(第 50 百分位)以及四分位数。
默认情况下,这些值以难以阅读的科学记数法显示,但是可以使用round方法将它们四舍五入到两位小数。
df.describe().round(2)

描述的输出
从这个总结中我们可以看到,我们需要清理数据,使其对分析有用。例如:
行程持续时间。这里我们可以看到最短的行程是 61 秒(花旗自行车忽略了 60 秒或更短的行程)。最长的是 3247190 秒(或 902 小时),听起来好像有人没有对接他们的自行车。这么大的数字会偏离均值,所以我们要解决这个问题。
出生年份。最早的年份是 1885 年。我觉得那时候出生的人都还活着,更别说骑自行车了!
开始时间/停止时间。这些列用数据类型 object 加载。为了使用组件(日期或时间),我们需要将它们转换成时间戳。
熊猫数据框
我们在上面看到了列名和数据类型,我们可以通过这样的表达式获得数据框中数据的附加信息,该表达式显示每列的名称、唯一值的数量、数据类型以及该列使用的内存量。
pd.DataFrame.from_records([(col, df[col].nunique(), df[col].dtype, df[col].memory_usage(deep=True) ) for col in df.columns],
columns=['Column Name', 'Unique', 'Data Type','Memory Usage'])
这里我们可以看到,使用数据类型 object 存储的列占用的内存空间大约是整数或浮点数据类型的十倍。只要我们只看一个月的数据,这就不是什么大问题,但对于一年的数据来说,问题就大了。

我们可以轻松地将开始和结束时间列从对象转换为时间戳:
df['starttime'] = pd.to_datetime(df['starttime'])
df['stoptime'] = pd.to_datetime(df['stoptime'])
我们还可以通过使用类别数据类型来减少用于其他一些列的内存量。当与行数相比,列的唯一值数量有限时,类别很有用。实际值只存储一次,而不是在每行存储一个长字符串,只存储一个指向实际值的整数。
该文件包含超过一百万行,但是从起点和终点桩号名称的唯一计数中,我们看到本月有 899 个桩号,因此它们是作为分类数据存储的良好候选。
只有两个唯一值的用户类型列应该得到类似的处理,有三个值的性别也应该得到类似的处理。 Bikeid 也可以进行类似的处理。
cols = ['start station name', 'end station name', 'bikeid', 'usertype', 'gender']
for col in cols:
df[col] = df[col].astype('category')
现在,如果我们运行与之前相同的报告,我们可以看到数据类型已经更改,并且这些列使用的内存已经大大减少。这也将加快使用这些列的处理速度。

探索数据
行程持续时间
我们想知道最常见的旅行持续时间是多少。但是 tripduration 列是以秒为单位存储的,这对于这个目的来说太精确了,所以我们将创建一个额外的列 tripminutes ,它具有以分钟为单位的旅行持续时间。
然后,我们可以使用 Seaborn displot 来显示每次旅行持续时间内的乘车次数。
使用的参数有:
data —数据框的名称。
**x** —图表列的名称。
bins —值列表。由于超过 30 分钟或 45 分钟的游乐设施的超龄费,较长时间的游乐设施数量很少,所以我们将只计算一个小时以内的游乐设施。
aspect —高宽比。值 10/5 给出了比默认值更宽的图表
行尾的分号防止图表上方出现虚假的文本行。
df['tripminutes'] = df['tripduration'] // 60
sns.displot(data=df,x="tripminutes", bins=range(1,61), aspect=10/5);
这张图表显示,最常见的行程是 5 分钟(最高的柱线),然后是 4 分钟和 6 分钟(之前和之后的柱线)。在那之后,图表显示了一个经典的“长尾”现象,即乘坐时间越长,旅行次数越少。

按小时和天数乘车
接下来,我们想看看一天和一个月中的乘车时间分布。因为我们将 starttime 列转换成了 datetime 值,所以我们可以使用它的方法来提取小时、日和星期几。只有 24、31 或 7 个可能的值,这些是存储为类别的好列。
df['start hour']=df['starttime'].dt.hour.astype('category')
df['start day']=df['starttime'].dt.day.astype('category')
df['weekday']=df['starttime'].dt.weekday.astype('category')
然后我们可以使用 seaborn countplot绘制每小时的乘坐次数。这里的figsize 设置允许比默认设置更宽的图表。
plt.figure(figsize=(12,5))
sns.countplot(data=df, x="start hour" ) ;
这里我们可以看到早高峰和晚高峰。

接下来让我们看看每天的乘车次数。突出显示周末有助于理解数据,因此我将创建一个额外的列weekend来指示某一天是否是周末。
用于countplot的附加参数有
hue —控制
条颜色的列的名称dodge —显示多个色调值的单个列
df['weekend'] = [if d >= 5 for d in df['weekday']] # 0=monday
plt.figure(figsize=(12,6))
sns.set_palette("Set2")
sns.countplot(data=df,x="start day", hue='weekend', dodge=False) ;
在这里,我们可以清楚地看到疫情对 3 月份花旗自行车使用量的影响。每日乘车次数在 3 月 9 日达到高峰。在宣布百老汇剧院将于 3 月 12 日关闭后,每天的乘坐人数进一步下降,并在本月剩余时间内保持不变。

乘坐次数按月计算
车站使用
接下来我们想知道:哪些站使用最多?我们可以使用数据帧方法value_counts(),它为一列返回一系列索引(这里是站名)和值(计数)。计数方便地按频率降序排列,因此如果我们选择前 20 个值,我们将得到 20 个最常用的电台。为了可读性,我将使用一个更大的 figsize 值使这个图表更高。
startstation = df['start station name'].value_counts()[:20]
plt.figure(figsize=(12,10))
sns.barplot( x=startstation.values , y=list(startstation.index),
orient="h" ) ;
这表明最常用的车站是潘兴广场北。这是系统中最大的车站之一,它就在中央车站的南面,中央车站是一个主要的铁路和地铁枢纽,所以它应该是第一个。

骑手的年龄
Citi Bike 提供了关于骑自行车者的有限数据:他们的出生年份、性别,以及他们是(年度)订户还是(每日)顾客。不过,知道这些骑手的年龄还是很有趣的。正如我们看到的出生年份列中的年份值是不可能的,或者说是不太可能的,所以限制我们的分析是有意义的。我将以 1946 年为分界点,这一年通常被认为是婴儿潮一代的第一年。
sns.displot(data=df, x="birth year" , bins=range(1946,2004), aspect=10/5) ;
关于这张图表,我们注意到的第一件事是 1969 年像谚语中的拇指疼痛一样突出:

按出生年份的乘坐次数
我怀疑这可能与用户类型有关。Citi Bike 拥有在线注册用户的个人资料,但客户在自助服务亭购买一日通票,可能不会倾向于输入他们的实际出生年份。
事实上,如果我按用户类型给条着色,那么我会看到大多数出生于 1969 年的骑手都是顾客。
sns.displot(data=df,x="birth year", bins=range(1946,2004), hue='usertype', multiple='stack' , hue_order=["Subscriber","Customer"], aspect=10/5);

按出生年份显示用户类型的乘车次数
但是,即使我们除去顾客,1969 年的价值看起来还是太高了。性别的值呢?零值表示缺少值。这里的条形按性别以 0(未声明)、1(男性)、2(女性)的顺序着色。

按出生年份显示性别的乘坐次数
因此,当计算骑手的年龄时,我想忽略骑手出生于 1946 年之前或性别为零的骑手。当这些条件满足时,我创建一个为真的遮罩。然后我在数据流中创建了一个新的年龄列,它被设置为 2020 减去出生年份,除非掩码为真,然后它被设置为 None,这表示缺少一个值。
skip = (df['birth year'] < 1946) | (df['gender'] == 0)
df['age'] = (2020 - df['birth year']).mask(skip,None)
最后,我可以根据骑手的年龄绘制一个情节,仍然显示骑手的性别。
sns.displot(data=df, x='age', hue='gender', multiple='stack', aspect=10/5) ;
从这里我们可以看到最常见的年龄(众数)是 30 岁,事实上最常见的年龄集中在 30 岁左右。我们现在还看到,使用花旗自行车的男性远远多于女性。

按年龄显示性别的乘坐次数
行程距离
与一些自行车上带有 GPS 系统的“无码头”自行车共享系统不同,Citi Bike 没有任何方法来记录骑车人所走的路线。
然而,有了每次旅行的起点和终点纬度和经度,至少可以使用计算地球上任意两点之间“大圆”距离的哈弗辛公式来计算距离。当两个站在同一条街道或大道上时,距离将相当准确,否则就不那么准确。尽管如此,它还是提供了两站之间的最小距离。
下面是一个表示公式的 Python 程序。
import numpy as np
def haversine(lat1, lon1, lat2, lon2):
lon1, lat1, lon2, lat2 = \
map(np.radians ,[lon1, lat1, lon2, lat2]) h = np.sin((lat2-lat1)/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin((lon2-lon1)/2.0)**2 miles = 3959 * (2 * np.arcsin(np.sqrt(h)))
return miles
我们可以用它来创建一个变量添加到数据框,并用于图表。
distance = haversine(df['start station latitude'],df['start station longitude'],df['end station latitude'],df['end station longitude'])
df['distance'] = distance
sns.displot(data=df, x="distance", aspect=10/5 ) ;
但是图表告诉我们有很多距离为零的旅行!原来,尽管花旗自行车是为的交通而设计的,但它也被用于娱乐,在那里行程的起点和终点都在同一个车站。

按英里计算距离的行程计数
我们可以从距离变量中去掉这些行程,以得到一个可读性更强的图表。
distance = list(filter(lambda distance: distance !=0 , distance))
sns.displot(x=distance , aspect=10/5 ) ;

按英里计算距离的行程计数
时间和距离信息的组合为进一步分析提供了许多机会,例如,您可以计算每次出行的(大概)速度,并按年龄、性别或站点进行分析。
节省到拼花地板
在我们更改了一些列的数据类型并派生出新的列之后,我们需要将数据帧保存到一个文件中,以便进行额外的分析。我们不想把它作为 csv 文件写回来,因为它需要很长时间来加载并丢失分类列。
有许多选项,如 Pickle 和 Feather,但在我看来,最佳选择通常是 Parquet,这是一种与 Pandas 很好地集成的柱状文件格式。作为列存储,当不需要每一列时,它可以提供更快的检索速度。
df.to_parquet('202003-citibike-tripdata.parquet')
即使使用默认的 SNAPPY 压缩,Parquet 也比其他格式占用更少的磁盘空间。我们可以比较这两个文件的大小,发现即使添加了额外的列,Parquet 文件占用的空间也只有原始 csv 文件的 17%。
202003-citibike-tripdata.csv 202,642,779
202003-citibike-tripdata.parquet 35,086,348
稍后,要将拼花文件读入数据框以供进一步分析,请使用:
df = pd.read_parquet('202003-citibike-tripdata.parquet')
当通过 SQL 查询引擎(如 Apache Drill 或 PrestoDB)访问时,大多数可视化工具也可以使用 Parquet 文件。
进一步分析
这是关于这个主题的系列文章的第一篇。Citi Bike tripdata 可以与其他来源的数据相结合,以提供关于如何使用自行车共享系统的更多见解。更多信息请参见:
对纽约自行车共享数据使用反向地理编码
结论
花旗自行车旅行数据虽然对所提供的分析有用,但可以通过一些数据准备来增加更多或更少细节的额外列。Pandas 提供了数据结构和操作来促进数据准备,Seaborn 使制作分布图来理解数据变得非常容易。Jupyter Notebook 提供了一种记录准备步骤的便捷方式。
使用 Pandas 和 Matplotlib 探索苏黎世的气候变化
原文:https://towardsdatascience.com/exploring-climate-change-in-zürich-using-pandas-and-matplotlib-cc98748be043?source=collection_archive---------54-----------------------

瑞安农·埃利奥特在 Unsplash 上拍摄的照片
使用 Python 进行探索性数据分析
本文涵盖的主题
- 使用 Pandas 包读取和操作数据
- 使用 Matplotlib 可视化数据
- 探索苏黎世平均气温的长期趋势
介绍
也许没有一天我们不在某种程度上面临全球气候变化的问题。但是如果你和我一样,你可能会觉得这些东西显得有些遥远,比较理论化。
这就是为什么我想看看能否在我的家乡瑞士苏黎世找到气候变化的证据。不是在加勒比海,不是在菲律宾,而是在这里。
幸运的是,如今找到气候数据相当容易,有大量的数据集可供公众免费下载。所以我开始了我的数据探索,结果发现一个合适的数据集只需要一次谷歌搜索。瑞士气象局的网站可以让你下载瑞士各地气象站的历史气候数据。
https://www.meteoswiss.admin.ch/home/climate/swiss-climate-in-detail/homogeneous-data-series-since-1864.html?station=SMA
在本文中,我们将利用 Python、Pandas 和 Matplotlib 包来探索和可视化来自瑞士苏黎世气象站的气候数据。
要跟进,您将需要以下依赖项:
- 数据集(参见上面的链接)
- Python 安装(本文使用版本 3.x)
- 熊猫套餐(https://pandas.pydata.org/)
- Matplotlib 包(https://matplotlib.org/)
注意:我在整篇文章中都包含了代码片段。如果您对实现细节不感兴趣,可以忽略这些。
是否安装了所有依赖项?所有系统都准备好了吗?太好了,那我们开始吧!
熟悉数据集
从仔细查看手头的数据开始通常是一个好主意,以便更好地了解我们正在处理的是什么。如果您打开数据文件,您应该会看到类似这样的内容:
Federal Office of Meteorology and Climatology MeteoSwiss
MeteoSchweiz / MeteoSuisse / MeteoSvizzera / MeteoSwissMonthly homogenized valuesStation: Z¸rich / Fluntern
Altitude [m asl]: 556 m
Coordinates: 47∞ 22.7' N / 8∞ 33.9' EReference date of homogenization:
Temperature: 01.12.2009
Precipitation: 01.12.2007Last inhomogeneity provisionally corrected:
Temperature: yes
Precipitation: noUnits:
Temperature: ∞C
Precipitation: mmMissing values: NAData source: MeteoSwiss
Creation date: 01.02.2021Year Month Temperature Precipitation
1864 1 -6.6 25.7
1864 2 -1.5 32.9
1864 3 4.5 51.0
1864 4 6.8 46.9
1864 5 12.3 78.4
1864 6 14.7 146.4
1864 7 16.9 77.5
1864 8 15.3 31.3
1864 9 12.9 61.9
1864 10 6.7 15.4
1864 11 2.5 78.5
1864 12 -4.2 4.0
1865 1 0.0 63.7
我们可以看到,该文件以一个包含数据集信息的头开始,包括其来源和气象站的位置。我们感兴趣的部分开始于标题之后,即四列数据:
- 年份:测量的年份,从 1864 年到 2020 年
- 月:测量的月份,从 1(一月)到 12(十二月)
- 气温:当月平均气温
- 降水量:当月平均降水量
准备数据集
首先,让我们把数据转换成一种格式,使数据争论对我们来说更容易。这就是熊猫套餐发挥作用的地方。对于那些不熟悉的人:Pandas 是一个 Python 包,在数据科学家中很流行,因为它具有强大的数据处理和数据操作功能。
额外收获:参见本文底部的参考资料部分,获得有用的熊猫备忘单!
在下面的代码片段中,我们正在导入模块,并将数据读入 Pandas DataFrame 对象。确保指定 header 关键字来告诉函数标题行数和实际数据开始的位置。最后一个参数告诉函数数据由空格分隔。
探索数据
现在我们有了数据框架中的数据,我们可以开始对天气数据进行探索性的数据分析。我们将从绘制 1950 年每个月的平均温度开始。
神奇的事情发生在上面代码片段的第 4 行,在这里我们获取数据的“温度”列,同时我们将数据集缩减到 1950 年。数据的散点图如下所示。
提示:操作【df。Year==1950]应用布尔过滤器。您还可以使用以下语法按多个条件进行筛选:df[(条件 1) &(条件 2) & …]。

我们从数据中看到了什么?嗯,夏天似乎比较暖和,冬天比较冷。咄!更有趣的是,我们将看到数据集中记录的 156 年间平均气温是如何变化的。
第 2 行是我们做实际工作的地方。这里,我们按年份对数据进行分组,并使用聚合函数计算该年所有月份的平均值。这给了我们从 1864 年到 2020 年每年的平均温度。

我们注意到数据参差不齐,年间平均气温波动很大。然而,引起你注意的可能不是年度间的波动,而是始于 20 世纪 80 年代左右的明显趋势。这个趋势是向上的!在 1994 年之前平均气温从未超过 10 摄氏度的地方,在过去的 26 年中有 12 年都是如此。
与更具乡村特色的地区相比
虽然气温趋势非常明显,但我问自己,这是否会受到城市本身在一段时间内大幅增长的影响,产生更多热量,从而影响城市气候。
为了找到答案,让我们走近阿尔卑斯山,将我们的数据与该国更具乡村特色的地区梅林根进行比较。
梅林根的数据并没有一直追溯到 1864 年,但我们仍然有大量的数据可以利用。让我们看看它与苏黎世相比如何。

总体而言,梅林根的气温低于苏黎世。但最重要的是,我们可以观察到在苏黎世已经观察到的平均气温上升的趋势。同样,这似乎是在 20 世纪 80 年代左右,事情真正开始起飞。
对极端情况的观察
平均气温上升是一回事。但是更极端温度的频率呢?如果你来自中欧,你可能会觉得近年来非常炎热的夏天越来越多。让我们来看看我们的苏黎世数据集是怎么说的…
Year Month Temperature Precipitation
126 1874 7 20.3 176.9
210 1881 7 20.3 64.2
570 1911 7 20.3 40.6
571 1911 8 20.4 57.6
774 1928 7 20.6 26.0
967 1944 8 20.3 98.2
1003 1947 8 20.1 39.6
1062 1952 7 20.4 49.0
1434 1983 7 22.0 36.7
1543 1992 8 20.7 91.8
1566 1994 7 21.4 43.3
1578 1995 7 20.5 73.2
1673 2003 6 22.3 57.8
1675 2003 8 22.7 81.8
1710 2006 7 22.5 46.9
1794 2013 7 20.7 81.6
1818 2015 7 22.2 40.3
1819 2015 8 20.4 60.7
1854 2018 7 21.2 63.2
1855 2018 8 20.8 113.3
1866 2019 7 20.7 96.5
这里,我们再次使用布尔过滤将数据集减少到当月平均温度超过 20°C 的条目(注意,平均值包括夜间温度!).
总共有 21 个月的平均温度超过 20 摄氏度。最热的月份通常出现在 7 月或 8 月。在某些年份(如 2018 年),平均气温连续两个月高于我们的阈值。
如果我们看看这些炎热夏季发生的频率,我们会注意到在这 21 个异常炎热的夏季月份中,有 12 个发生在过去的 30 年里。换句话说:超过 50%的极热月份发生在数据集中最近 20%的年份。
最热的月份呢?
Year Month Temperature Precipitation
1675 2003 8 22.7 81.8
二零零三年八月的平均气温为 22.7 度
从这里去哪里?
我们可以利用这些数据挖掘更多的东西。我们可以观察降雨量,分析冬季月份,甚至开始建立一些统计模型来预测苏黎世未来十年的气候变化。但那是以后的事了…
判决
气候变化在苏黎世数据中可见吗?好吧,我让你来判断。或者更好的是:通过寻找合适的数据集并使用 Pandas 和 Matplotlib 讨论数据,为您自己的家乡找到它!
资源
熊猫小抄:https://pandas.pydata.org/Pandas_Cheat_Sheet.pdf
最终免责声明
显然,这篇文章中的轶事分析不符合科学标准。它仅用于娱乐和教育目的。
探索数据治理 SG API
原文:https://towardsdatascience.com/exploring-data-gov-sg-api-725e344048dc?source=collection_archive---------32-----------------------
Data Gov SG 提供的实时 API 入门笔记本

照片由萨法尔·萨法罗夫在 Unsplash 拍摄
笔记本:https://nb viewer . jupyter . org/github/tonyngmk/DataGovSG/blob/main/DataGovSG . ipynb
1.介绍
Data.gov.sg提供来自新加坡 70 多个公共机构的公共数据集。迄今为止,已经有超过 100 个应用程序是利用政府的公开数据开发出来的。
在其开发者部分,有实时数据集,如天气和交通状况。本文是对通过这些实时 API 获得的底层数据的用法和基本分析的基本探索的开始。
2.要求
可以用一个简单的GET HTTP 请求调用 API。这可以通过使用基本 python 库中的requests包来实现。
import requests # HTTP requests (GET / POST)
在我们进入应用程序之前,知道这相当于我们在互联网上执行搜索时的查询字符串可能会有所帮助。例如,假冒网站:
- https://example.com/path/to/page?name =雪貂&color =紫色
[?name=ferret&color=purple](https://example.com/path/to/page?name=ferret&color=purple)的加粗部分概述了对‘name = ferret’和‘color = purple’的查询,它们由一个“与”符号“&”连接。因此,我们可以用它来设计我们的 API 调用。
然而,这将是相当多的字符串公式工作,因为我们必须通过各种字符串方法来创建每个 URL。谢天谢地,有一个更直观的方法。我们可以将所需的参数存储在字典中,然后将它们传递给 get 请求函数。
例如,这些 API 接收的一个公共参数是字符串格式为 YYYY-MM-DD 的日期。我们可以用strftime()方法轻松获得这种日期格式与其各自的格式。
today = datetime.datetime.today()
params = {"date": today.strftime("%Y-%m-%d")} # YYYY-MM-DD
之后,我们可以在 GET 请求函数中将字典作为参数传递。有各种各样的属性要获得,比如 HTTP 响应代码,但是我们可以用.json()获得字典格式的输出。
requests.get('https://api.data.gov.sg/v1/environment/psi', params=params).json()
3.蜜蜂
在讨论了获取数据的技术细节之后,让我们试着检查和理解可用的数据。在 8 个可用的实时 API 中,有 5 个天气 API 和 3 个杂项 API。首先,我们将浏览一下各种 API。
3.1 IPOS 应用程序 API
新加坡知识产权(IPOS) API 允许用户查询新加坡的知识产权(IP)申请。对于大多数企业来说,知识产权是保护发明、商标、设计或品牌等人类智慧创造的基础。
在我看来,这大概是酷炫度排名最高但可用性排名最低的。我们可以获得大约数百张专利照片的事实很有趣,但可能无法解决任何现实世界的问题(在我看来可能是数据检索或 ML wise)。
尽管如此,我们还是要试一试。首先,尽管今天(2021 年 1 月 11 日)有 HTTP 200 成功响应,API 还是返回空数据。这可能是因为没有在日期中应用 IPs,因此我们需要一种方法来搜索非空数据。我们可以使用一种简单的方法来迭代循环过去 365 年的日历,一旦找到项目,循环将在 2020 年 9 月 9 日中断,然后尝试检查其数据。
today = datetime.datetime.today()
dateRange = [today - datetime.timedelta(days=i) for i in range(365)]print("Searching:")
for date in dateRange:
params = {"lodgement_date": date.strftime("%Y-%m-%d")} # YYYY-MM-DD
ipos = requests.get('<https://api.data.gov.sg/v1/technology/ipos/designs>', params=params)
print("No items found for: ", date)
if ipos.json()["items"]: # Not empty
print("Found first item at: ", date)
break
共有 3 项专利可能是由同一个创作者或企业申请的,我们可以通过深入到文档字段来进一步检查。

图片由作者提供| IPs 应用于 2020 年 9 月 9 日
在每个文档实例中,都有一个 URL 字段。我们可以使用requests.get('url').content直接访问每张图片的内容,并通过其各自的 URL 获取其像素值,然后使用IPython.display.Image将其显示在笔记本上
# Obtain 5 images from patent
from IPython.display import display, Imagefor i in range(1, 6):
display(Image(requests.get(ipos.json()['items'][0]['documents'][i]['url']).content))
由于版权问题,我不会把图片贴在这里,但是你可以在 nbviewer 中查看,这也可以通过 API 公开检索。
3.2 停车场可用性 API
停车场可用性 API 显示每分钟刷新的大约 2072 个停车场的可用地段。不幸的是,没有地理坐标作为元数据,因此无法在地图上绘制。尽管如此,2000 多个停车场可能太多了,以至于无法在地图上解释任何东西。

作者图片|停车场可用性 API 检索的数据
有用的数据将是停车场数量、可用地段和总地段。不幸的是,我不知道如何为这个 API 创建一个有用的情节(如果你知道任何可行的方法,可以留下评论😀).
3.3 出租车可用性 API
该 API 为出租车可用性提供 GeoJSON 数据。这对我来说很有趣,因为 API 每 30 秒刷新一次,因此可用的出租车总数也会随着每次呼叫而波动。我们可以使用 Python 中的folium库来绘制这些排序作为标记。由于获得的出租车数量约为 3000+,我仅选择绘制前 100 辆出租车,以减少绘图滞后。

图片由作者提供| API 首批 100 辆出租车
老实说,我不确定这个 API 可以扩展成什么。没有提供可用出租车的其他信息,因此该 API 主要用于告知:
- 新加坡的出租车数量
- 可用出租车的位置
我敢肯定,公共出租车集团 ComfortDelgro 可能已经将这些信息集成到他们的应用程序(app)中。
3.4 天气空气污染指数
有 5 个不同的 API 可用,其中一些可以真正地进一步聚合在一起。尽管如此,我还是会浏览一下,希望对您有用。
天气预报
这个 API 提供 2 小时、24 小时和 4 天的天气预报。在我的例子中,我只使用了 2 小时的预报,但是对于其余的 API 也是一样的。例如,如果请求的日期时间是 11 Jan 11 pm ,最后一个实例将返回 12 Jan 1 am 的预测。
天气预报 API 提供新加坡超过 47 个不同地区的特定预报。我试图在大约 1 月 11 日晚上 11 点打电话给这个 API,所有地区都返回了“小雨”2 小时预报。

图片由作者提供| 2 小时天气预报
实时天气读数
提供的名称是“实时天气读数”,但基本上它只返回几个气象站记录的温度。然而,当我依稀记得路过 NTU 行政大楼后面的一个地铁站时,我很高兴但并不惊讶地看到一个地点标绘在我的学校(南洋大道)。

作者图片|实时天气读数(温度)
PSI
污染物标准指数(PSI)衡量空气质量。PSI 是新加坡近期的一个重要指标,因为我们的国家容易受到来自印度尼西亚多个热点地区的雾霾影响。总共有 5 个 PSI 报告区域:“北”、“南”、“东”、“西”、“中”,“国家”读数将是所有区域的最大值。

图片由作者提供|新加坡 PSI 读数
还提供多种污染物分类指数,如一氧化碳、二氧化氮等的水平。我们可以尝试绘制由门户创建的所有污染物子指数的 24 小时图。首先,我们必须对今天和昨天的日期进行两次 API 调用。此后,我们可以连接两个按日期排序的数据集,并为下面的绘图获得最后 24 个实例。

图片由作者提供|过去 24 小时的污染物分项指数图
PM 2.5
颗粒物 2.5 (PM 2.5)是空气中降低能见度的颗粒水平。PSI API 中已经提供了这些数据,因此我将不提供多余的解释。

图片由作者|多余的 PM2.5 图
UVI
紫外线指数(UVI)描述了地球表面的太阳紫外线辐射水平。这个 API 是所有 API 中最乏味的,因为它提供的数据量最少。在将日期参数传递给 API 的 GET 请求后,只返回一个文本“Healthy”。这方面也没有进一步的地理细分。

作者图片| UVI API
4.结论
总而言之,我已经尝试偷看我们的政府提供的各种 API,所以你不必。老实说,我认为这些 API 对创建任何应用程序或解决任何现实世界的问题没有任何帮助。对于更有趣的数据集,可能确实需要保护数据隐私,但这可能会以抑制潜在的解决方案或创新为代价。
我想知道来自购物中心可用性的 SpaceOut 等应用程序的数据是否类似于停车场可用性,其中可以披露购物中心的顾客数量(也许可以披露各个购物中心的“总顾客数”,因为有一个“最大”值)。这可能有助于分析消费者情绪,并成为各种利益相关者(如购物中心股票的预期和现有股东)的经济增长的代理变量和指标。
通过谷歌的统计 Datacommons 访问公共数据集
原文:https://towardsdatascience.com/exploring-datacommons-the-api-powering-google-search-afc366ec242b?source=collection_archive---------37-----------------------
一种查询公共数据集的新范式
有没有想过谷歌是如何对诸如“旧金山的平均收入是多少?”这样的问题给出如此准确的回答的?

作者图片
谷歌搜索上的统计查询由 DataCommons 提供支持,这是一个公开可用数据集的开源存储库。
DataCommons 提供了一个中央 API 来处理来自世界银行、美国劳工统计局、疾病控制中心等不同的公共数据集。这种工具意味着您不必花费大部分时间来查找、清理和连接这些数据(我们不喜欢谈论的 70%的数据分析工作)。这是一个开发人员友好的开放标准,意味着任何人都可以从他们喜欢的工具中访问它,并且在以下领域非常有用:
- 科学研究
- 新闻工作
- 卡格尔比赛
- 市场调查
- 学问
让我们看看当它与我们最喜欢的 Python 可视化库结合时能做什么!
热身的例子
想象一下,我们是一名记者,正在撰写一篇专题文章,比较美国不同州变老的情况。首先,我们想知道年龄与收入、健康、空气污染、人口密度等其他变量之间的关系。我们可以通过查看美国 500 个最大城市的数据立即开始:
关于 DataCommons,要记住的最重要的事情是所有东西都是图中的一个节点。在这种情况下,我们从查询特殊的组节点CDC500_City开始,并询问它的members,即链接到它的其他节点。这为我们提供了 CDC500 数据集中城市的 data commons id(dcids)。Google 已经做了一些非常复杂的实体解析,所以这些位置在不同的数据源中是标准化的。接下来,我们将这些 id 加上一个统计变量列表传递到一个dc.build_multivariate_dataframe查询中,并获得一个熊猫数据框架,其中包含来自 CDC、劳工统计局和人口普查局的数据,所有这些数据都被神奇地清理并按城市分组。
我无法强调这有多酷!想象一下,如果你要从头开始做这件事,要花多长时间:用不兼容格式的数据在不同的网站上爬行,清理和标准化数据,为实体建立一个通用名称列表,并将其全部合并在一起。
运行该代码,加上下面的可视化结果,我们得到了以下结果:
这是一个很好的开始,看起来中值年龄和收入之间存在微弱的正相关,但是我们不知道这些城市的实际名称。为此,我们需要丰富数据。
丰富数据
我们希望获得数据集中每个位置的城市和州名。通常,这需要几个小时,因为我们必须找到另一个数据源,标准化名称,进行合并,然后花很长时间调试略有不同的输出。在 DataCommons 中,我们需要做的就是使用他们的 API 从图中获取更多的属性:
使用dc.get_property_values很容易获得关于每个节点及其关系的附加信息。我们传递参数containedInPlace来查找父节点,传递参数name来查找正确的名称。
数据如下所示:
太好了,一些更清晰的趋势出现了!我们可以看到大多数最富有的城市在加利福尼亚,大多数最古老的城市在佛罗里达。根据人口统计数据也更容易看出纽约和洛杉矶等大城市的发展趋势。
在地图上显示
最后,假设我们希望帮助读者在地图上可视化这些数据。DataCommons 有一个方便的内置地图浏览器工具,我们可以在其中选择统计变量和地理级别。如果我们想要可视化一些更复杂的东西,例如,65 岁以上的家庭年收入达到或超过 20 万美元的比例是多少,我们可以轻松地重新提取数据,并将其插入 geoviz 工具,如 Folium:
在这段代码中,我们按州重新提取数据,下载一个 GeoJson 对象,然后使用key_on=feature.properties.name连接州名。添加一点语法糖来显示悬停标签,我们就完成了。有了干净的数据,你就会平静!
我过了一会儿才意识到,最富裕的退休者实际上是在夏威夷,65 岁以上的家庭中有近 10%年收入在 20 万美元或以上😱。
摘要
在大约 100 行代码中,我从几个不同的来源提取了数据,进行了分析,并在不同的绘图库中构建了几个可视化程序。有了 DataCommons,你的大部分精力可以用于实际分析和可视化数据,而不是清理、清理和管理数据。
从这里开始,您可以探索收集在 DataCommons 中的数千个统计变量中的一些,或者尝试他们的图形浏览器。你能回答的问题没有限制!
参见 Github 上本教程的代码,以及下面的最终数据集:
使用 Graph 和 GraphQL 探索分散的土地市场销售
原文:https://towardsdatascience.com/exploring-decentraland-marketplace-sales-with-thegraph-and-graphql-2f5e8e7199b5?source=collection_archive---------31-----------------------

迈克尔·泽兹奇在 Unsplash 上的照片
最近 NFTs 风靡一时。或者他们是?
我想通过探索游戏《分散的土地》中可穿戴物品的销售来找到答案。
分散的土地是一个位于区块链的虚拟世界,在这个世界中,玩家可以购买土地,创建场景,举办虚拟活动,并在点对点市场中买卖可穿戴商品。
尤其令我好奇的是,市场上出售可穿戴物品有多受欢迎。
可穿戴物品是简单的物品,你可以在市场上给你的游戏角色穿上这样的衣服。

作者图片
幸运的是,通过他们在 TheGraph.com 的 GraphQL API 提供了一种访问市场数据的便捷方式。
使用这个 API,您可以访问关于商品是什么、谁购买了它、价格是多少以及何时购买的信息。
访问数据的第一步是使用 TheGraph.com 上的 playground 环境来确定要返回的数据的格式。
API 游乐场
操场看起来像这样,有三个窗户,我在下面做了标记:

作者图片
第一个窗口(标记为 1)是您的查询编辑器,您可以在其中根据希望数据返回的方式来构建查询。
第二个窗口(标记为 2)是您的输出窗格,您可以在其中查看查询结果。
第三个窗口(标记为 3)是您可以请求的字段及其数据类型的参考指南。
默认情况下,该查询返回 100 个项目,如果您想要收集一个全面的数据集,这将带来一点限制。
为了解决这个问题,我需要找到一种迭代查询的方法,通过输入一些最后的值作为检索下一个结果的起点。为此,我根据更新日期对查询进行排序,并通过 updatedAt_gt 参数动态填充更新日期的起点。
我的 GraphQL 查询最终看起来像这样:
{orders (first: 1000 orderBy: updatedAt, orderDirection: asc where: { status:sold category:wearable updatedAt_gt:1}) {
category
nftAddress
price
status
id
updatedAt
blockNumber
nft {
owner {
id
}
name
tokenURI
owner {
id
}
}
}
}
我不打算讨论 GraphQL 中过滤或窗口的细节,而是提供下面的几个链接,作为格式化查询的良好参考。
您可以在本页查看查询 Graph 的具体语法示例,并在此学习 GraphQL 查询语言。
输入 Python
一旦我格式化了我的查询,就该利用 python 来遍历结果,并将它们组合在一个 pandas 数据帧中以供进一步分析。
为此,按照 GraphQL.org 网站上的官方文档安装石墨烯库是有帮助的。
下面是我必须导入的库和使用 python 查询分散式 API 端点所需的设置步骤。
对于像我这样的新手,有一点需要注意,GraphQL 查询格式中花括号({})太多,这在传递动态变量时会产生一些问题。为了避免\n 错误,我不得不将所有的花括号折叠起来,并在字符串中对我的日期变量使用语法“{0}”。
我的查询字符串最终看起来像这样:
下一步是设置一个循环来查询前 1000 笔销售,然后将我的前 1000 笔销售的最后销售日期作为获取下一笔 1000 笔销售的起点。完整的查询循环如下所示:
探索数据
随着数据完全加载到数据帧中,我们现在可以浏览数据。
按月来看,我们可以看到,游戏在 2020 年 2 月推出时销量最高,但此后销量逐渐下降。现在每月大约有 100 台。

作者图片
平均售价怎么样?我找不到 API 中 price 字段引用的描述,所以我做了一点假设,价格是以 MANA——本地令牌货币——给出的。
我绘制了每月售出商品的平均价格。在撰写本文时,1 MANA 大约值 30 美分,因此 2021 年 2 月售出的可穿戴设备的平均价格在 600-1200 美元之间!

作者图片
哇,也许飙升的货币和天价的商品是销售额下降的原因。
从 2020 年 2 月到 2021 年 2 月,我的数据中共有 8435 笔销售。售出的独特产品总数为 281 件,其中 231 件的销售量超过 1 件。
销售额最高的 15 种产品如下:

作者图片
结论
“分散的土地”无疑仍处于发展的先锋阶段,为了推动可穿戴设备销售的增长,它还有很多工作要做。也许较低的商品价格会有助于推动月销售额的增长。
如果你是一个区块链和数据爱好者,并且对非中心化土地或非功能性土地有其他的结论,我很乐意在评论中听到你的意见。
使用 PyCaret 的快速建模管道探索 Covid 期间房地产的中断
原文:https://towardsdatascience.com/exploring-disruptions-in-real-estate-during-covid-using-pycarets-rapid-modeling-pipeline-7e8583bd266b?source=collection_archive---------31-----------------------
房地产市场经历了疯狂的一年,正如我用一系列不同市场的 Case-Shiller 房价指数绘制的图表所示。看看 2020 年中期开始的峰值!

作者图片
PyCaret 是一个优秀的库,用于使用机器学习来探索房地产等商业市场的中断。它的低代码实现允许您快速预处理数据,一次单击即可训练多个模型,比较模型结果,并在易于初始化和管理的集成管道中进行微调。
创作者 Moez Ali 的教程也很优秀,我都强烈推荐。事实上,他的教程“使用 PyCaret 回归模块进行时间序列预测”直接启发了我在这里探索新冠肺炎疫情期间房地产市场变化的方法。
关于这篇 TLDR 文章的简短说明:大多数 PyCaret 教程正确地强调了它的简单代码和快速建模。在这里,我想花额外的时间介绍一些有用的可选特性和该库的一些重要细微差别,希望对 PyCaret 的新用户有所帮助。
本次演示使用的数据集和笔记本是这里。
建模应该是 EDA 的一部分
PyCaret 的一个仍被低估的优点是它适合通过建模来执行增强的 EDA。传统上,在数据科学学院,我们被告知,一旦我们保护了数据集,正确的做法是花费大量时间进行探索性数据分析。在(也只有在)我们花了大量时间绘制我们的特征并查看直方图之后,我们是否可以继续进行特征工程、训练和测试模型、验证等等…
然而,建模也能有助于(并加速)EDA。观察不同的模型如何在数据集上成功或失败,可以获得关于数据中关系的重要见解。这些见解可以告知我们如何设置问题,并实际上驱动我们项目流程中的过程,如特征工程和模型开发,从而加快我们的最终项目交付。
因为 PyCaret 的简单、低代码管道针对多个模型的快速迭代进行了优化,所以它非常适合通过建模来执行 EDA。为了证明这一点,我将通过一个示例来说明我们如何使用 PyCaret 的回归模块来开发多个验证集上的时间序列预测,这有助于我们更好地理解新冠肺炎疫情期间芝加哥房地产市场的中断。
获取芝加哥的 Zillow 房地产数据
在这个演示中,我将使用芝加哥的中值标价时间序列,这是我从 Zillow 上的“标价和销售价格”数据集在 excel 中创建的。这是一个很棒的数据集,它跟踪了美国几个主要城市的月度房地产市场指标。包含所有月度指标和市场的完整 Zillow 数据集在这里。或者你可以下载我创建的的芝加哥子集。
正在安装 PyCaret
安装 PyCaret 很简单。然而,强烈建议在使用 PyCaret 时设置一个新的虚拟环境,以避免与其他库的潜在冲突,因为 PyCaret 是建立在与其他库的依赖关系上的。我所在的项目团队中,有人试图将 PyCaret 直接安装到他们的全球环境中,并遇到了问题,但虚拟环境运行得非常好。
另一个很好的选择是使用 Google Colab,我正在使用它。Colab 的优势是不需要建立虚拟环境,我没有看到任何依赖问题,而且你可以从谷歌免费快速的云处理中受益,其功能与 Jupyter 笔记本非常相似。
首次安装:
然后,在 Colab 中,启用 PyCaret,然后导入回归模块,如下所示:
准备数据集
安装完成后,让我们在导入原始数据后看看它:

作者图片
接下来,我们需要做一些简单的数据准备,确保我们的日期列是一个正确的 datetime 对象。

作者图片
看起来不错!现在,让我们用 12 个月的滚动平均值来绘制时间序列,这将有助于我们可视化季节性模式和趋势。

作者图片
从这个图中,我们可以预见我们的回归模型将很难模拟这个时间序列的 V 形。让我们从 2013 年(房地产市场走出大衰退的开始)开始,选择这个时间序列中更具线性的子集。一旦数据是子集,我们将运行相同的绘图。

作者图片
好多了!我们可以用这个!
回归与预测?
通常,不建议使用回归来生成时间序列预测。更传统的方法是使用统计模型,如 ARIMA 或萨里玛。
然而,通过将我们的时间序列转换为回归就绪数据集,并利用 PyCaret 易于实现的预处理函数中一些强大的可选参数,我们可以轻松快速地使用回归开始生成预测。
总的来说,下面的方法是将我们的日期列解析成可以回归的特性。年变成了一个数字变量来帮助我们的模型分析趋势,而月变成了一个分类变量来帮助我们的模型分析季节性。

作者图片
既然我们已经将时间序列转换为 PyCaret 可以轻松进行回归预处理的数据框架(稍后,我将演示 PyCaret 如何将 month 列一次性编码为模型可以回归的 12 个不同的特性),那么让我们创建一个测试序列分割和一些不同的验证集。

如您所见,我在数据中创建了几个拆分,我将使用它们来实现不同的目的:
训练数据:模型将只根据 2013 年到 2017 年的数据进行训练。
测试数据:我们将只根据 2018 年的数据测试我们的模型。
然后,我们将在综合测试和训练装置上改装/最终确定我们选择的模型(2013 年至 2018 年)。
这就是使用建模来执行 EDA 的地方。然后,我们将在三个不同的数据集上验证最终模型:
验证数据集 1: 2019
验证数据集 2: 2020
验证数据集 3:2021 年 1 月至 5 月(即“当前日期”)
在多个验证集上尝试我们的最终模型将让我们了解随着时间的推移我们的预测做得有多好(或不好)。分析结果可以为我们制定 2021 年剩余时间和 2022 年全年的预测方法提供信息,我们也将这样做。
预处理
接下来,我们将使用 PyCaret 的 setup()函数来预处理我们的训练和测试数据集,这是在 PyCaret 中训练任何模型之前所必需的。
要运行 setup(),只需传入一个数据框并指定目标列的名称。通常,当您传递数据时,setup()会使用随机方式自动创建训练/测试分割。
然而,这对时间序列数据不起作用,因为我们不希望我们的模型在更近的日期进行训练(即“泄漏”)。此外,我们还需要将默认折叠策略从随机 k-fold 交叉验证调整为时间序列 k-fold 验证,以在每个折叠中保持正确的日期序列。
setup()函数非常简单。但是,值得探究许多可选参数,这些参数会根据您的数据显著影响模型性能。我在要点中添加了一些额外的注释,只是为了说明一些我发现特别有用或重要的参数设置。
当我们初始化 setup()时,我们得到一个输出,它为我们提供了大量关于我们处理的数据的有用信息。以下是一瞥:

作者图片
如果我们调用上面实例化的 setup“s”变量,我们可以看到 setup()函数是如何预处理数据并自动将 month 列(它识别为分类列)一次性编码为 12 个不同的特征,模型可以根据这些特征进行回归。非常酷 :

作者图片
这将有助于回归模型捕捉数据中的季节性模式。
模特狂欢。
这是事情变得更加有趣的地方。compare_models()函数一次单击即可训练多个回归模型。我们预处理过的训练和测试数据已经加载到 PyCaret 的虚拟管道中,因此不需要向该函数传递任何数据。n_select 参数指定了我们希望存储在“最佳”变量中的前 K 个模型,我们以后可以使用这些模型。
一旦 compare_models()被初始化,它就开始训练 PyCaret 在其回归库中的所有模型。在训练模型时,输出将它们从最佳性能到最差性能进行排序(您可以指定模型排序所依据的度量,默认为 R 平方)。
下面是输出结果:

作者图片
此外,我们将性能最好的 3 个模型存储在一个方便的“最佳”变量中,如果我们想尝试混合或堆叠最好的模型等技术,可以将该变量传递给其他函数。

compare_models()函数为我们提供了大量的模型和一个很好的基线。从这里,我们可以调用我们想要进一步检查的单个模型,调整它们,集成它们,或者将它们与其他模型结合以提高预测性能。
选择和调整模型
create_model()函数只是调用在 compare_models()中训练的单个模型。我选择使用“山脊”模型,它在所有训练过的模型中排名最高。这不一定总是最好的方法,因为在训练数据上表现很好的模型可能会过拟合,无法很好地推广到新数据。
尝试一种排名稍微靠后但仍有不错得分的模式是很值得的。那个健壮的模型可能就在眼前,所以我推荐尝试几个。记住:建模=探索!
另外,请注意,create_model()中没有发生任何重新训练。我所说的“岭”模型与上面 compare_models()输出中的岭模型具有完全相同的平均分数。此外,我们还可以在输出中看到每个 k 倍的分数。

作者图片
从这里开始,我们可以尝试几种技术来提高我们的模型性能。一种标准的技术是在单个模型上使用 tune_model()函数。默认情况下,tune_model()会经历 10 次随机网格搜索迭代,从预定义的超参数网格中随机选择一个值。使用 n_iter 参数增加迭代次数通常会提高模型性能,但也会增加计算时间。此外,它还有产生过度拟合模型的风险。
在这种情况下,我将 n_iter 增加到 50,我们确实看到了训练数据的性能提高。

作者图片
PyCaret 还有一个 ensemble_model()函数,您可以使用 boosting 或 bagging 来尝试提高模型性能。您可以传入您的原始模型或调整后的模型并比较结果。在这种情况下,我正在我的调优岭模型上尝试装袋和增压。
关于装袋和增压还有很多要说的,但是,一般来说,如果你认为你的模型过拟合,你可以尝试装袋,如果你认为你的模型欠拟合,你可以尝试增压。
当对观测值相对较少的月度时间序列进行这种类型的回归时,我通常发现 bagging 比 boosting 更有助于模型泛化,但当然这高度依赖于您的数据。以下是装袋和增压输出:

作者图片

作者图片
看起来我们的优化模型的增强版本和打包版本在训练数据上表现相似,并且比我们的优化模型稍差。
还有一个混合模型函数,您可以在其中传递一个列表或(在我们的例子中)一个包含多个模型的变量(还记得我们在初始化 compare_models()函数时定义了一个“最佳”变量来存储前 3 个模型)。
混合器将对各个模型预测进行平均,以形成最终预测。对于时间序列的这种类型的回归,我使用混合的结果导致了混合到更差的性能,但对于某些数据集来说,这可能是一个很好的选择,并且非常容易实现。

作者图片
PyCaret 还有一个 stack_models()函数,与 blending 略有不同。在堆叠中,训练多个模型来预测结果,并且创建元模型,该元模型使用来自这些模型的预测作为输入以及原始特征。在我尝试的几次中,堆叠器似乎产生了更多的噪音并削弱了性能,但它肯定会对不同的问题有所帮助。
绘制模型结果
PyCaret 还有一个简单的绘图功能,您可以在其中检查特征重要性、残差和预测误差。以下是几个例子:

作者图片

作者图片
您可能已经注意到,上述残差图(. 902)中的训练 R 平方与我们的袋装调谐岭模型的 create_models()输出(. 7588)中的 R 平方不匹配。我对此做了一些调查,但我仍然不完全确定为什么会这样。文档称“[调用绘图函数]的过程在某些情况下可能需要重新训练模型”,这可以解释为什么训练的 R 平方不同。如果有人有答案,我很乐意对此有任何见解!
我还喜欢看预测误差图,它给出了测试数据的 R 平方,并将其与 create_models()输出的训练数据的 R 平方进行比较。我认为从 0.7588 下降到 0.672(下降 11.4 %)对年和月输入的回归来说是可以的。

作者图片
根据测试数据进行预测
在检查了我们所有版本的岭模型的预测误差图之后——调整的、增强的、袋装的、混合的——我坚持使用具有最佳性能的调整的袋装岭模型。我们可以使用 predict_model()调用测试集上的预测。只需将选择的模型和数据集传递到函数中,它将使用数据集上的模型输出预测。
如果我们没有传入数据集,该函数只是对我们传入 setup()函数的原始测试集执行预测,该测试集仍在建模管道中。请注意,我们的输出与我们在测试数据中定义的日期范围(2018 年)相同:

作者图片
输出为我们提供了我们已经在图中看到的测试 R-squared、其他评分指标的列表、包含所有功能的测试数据集以及实际和预测列。我们可以在我们训练的任何其他模型上使用 predict_model()函数,看看它们是否能更好地概括我们 2018 年的测试数据。
模型是一种探索工具
现在我们有了一个模型,我们可以用它来对我们的芝加哥房地产中值标价数据执行一些有趣的 EDA。
在我们继续改装/最终确定模型并在我们的验证集上尝试之前,我们可以将我们的整个数据集(2013 年 1 月-2021 年 5 月)传递到 predict_model()函数中,并可视化预测值如何符合我们的实际值。
请注意,在这里,我们是而不是重新训练或改装我们整个数据集的模型。我只是将整个数据集和静态估计值传递给 predict_model()函数,这样我们就可以直观地看到整个时间序列的预测值和实际值。这将允许我们对我们的模型行为和芝加哥不同年份的房地产行为进行一些分析。
我们开始吧:

作者图片
通过检查我们的图,我们可以看到,我们训练的模型非常适合 2018 年和 2019 年,但拟合在 2020 年中期开始严重偏离实际情况,甚至在 2021 年也不在同一范围内。这有助于澄清相对于历史模式而言,疫情期间发生的房地产动荡的性质。
由此,我们可以假设我们的模型在 2019 年的验证数据上表现良好,在 2020 年的验证数据上表现较差,在 2021 年的验证数据上表现差得多。
最终确定模型
当我们使用 finalize_model()函数时,我们的模型引用我们在管道开始时传递给 setup()函数的训练和测试数据。这确保了我们不会将测试数据中包含的任何预测能力留在桌面上。
提醒一下,我们的综合训练和测试数据日期范围是 2013 年到 2018 年。
最终确定很简单:
和以前一样,模型存储在一个变量中,这样我们可以很容易地将它传递给其他函数:

作者图片
在验证集上部署模型
现在,我将在我创建的不同验证集上部署我们的模型。请记住,我创建了 3 个验证集,一个针对 2019 年、2020 年和 2021 年上半年。
让我们来看看我们的模型在每个模型上的表现吧!
我将只展示生成 2019 年预测和分数的代码,因为其他年份的工作方式完全相同。
下面是我们如何通过将最终模型和 2019 年验证数据传递到 predict_model()函数来创建 2019 年预测:
这是我们的输出:

作者图片
接下来,我们可以使用 PyCaret 的 check_metric()函数来检查我们的预测分数。此函数的参数是目标列(我们的 2019 年实际价格)和预测值(我们的 2019 年标签列)。

那些分数相当不错!在这种情况下,我们根据 2013 年至 2018 年的数据改装的最终模型在 2019 年的验证数据上产生了强劲的结果。
基于回归的预测似乎在未来 12 个月表现良好。但这种良好的表现会持续下去吗?我们猜测它不会。因此,让我们检查来自我们的其他验证集的分数。
2020:

2021 年 1 月至 5 月:

正如预期的那样,我们的模型在 2020 年验证数据上的性能下降得相当快。在 2021 年,我们在负 R 平方的情况下做得非常差一点,这基本上意味着我们的模型做得比仅仅使用每月目标值的平均值进行预测更差。
但是,还记得我们之前是如何使用我们的模型执行 EDA 的吗?我们预测这种破坏会发生,这种结果可能不是由于我们的模型中的任何基本缺陷。从历史角度来看,2020 年第一季度(covid 在美国开始)之后的一切都很不寻常。
那么这个模型告诉我们什么呢?
我认为我们可以假设,排除柯维德疫情一生一次的商业周期中断,我们基于回归的预测实际上做得很好,基本上捕捉到了 2013 年至 2018 年的趋势和季节模式,足以对 2019 年做出准确的预测。即使该模型未能在这些模式经历巨大破坏的市场中推广,如果我们预计破坏会过去,事情会回到疫情之前的轨迹,这并不令人惊讶,也可能不是一个问题。
追踪我们的模型在 2019 年、2020 年和 2021 年的表现给了我们这些见解。因此,PyCaret 的快速建模管道在探索房地产市场的这些模式中断方面非常有用。
预测未来
使用我们在 2013 年至 2018 年数据上训练的最终模型,我们现在将预测 2021 年下半年和 2022 年全年。
首先,我们将生成模型预测所基于的未来值:

作者图片
接下来,我们将把最终模型和包含未来值的数据框传递给 predict 函数。

作者图片
最后,我们将把这个未来预测的数据框连接到我们的原始数据集,这样我们就可以绘制原始数据和我们的预测。

作者图片
从这个图中,我们可以看到我们的模型正在将季节性模式强加到 2021 年 6 月的预测上,尽管 2021 年 5 月的峰值要高得多。但是,如果我们使用高度科学的方法,把我们的拇指放在大约从 2020 年 5 月到 2021 年 5 月的部分,我们会看到一个相当一致的季节模式和趋势。这种(高度简化的)方法可能不是预测疫情之后房地产市场的最糟糕方式。
后续步骤
如果我们想假设 2020 年和 2021 年是异常值,下一步可能是再次改装模型添加 2019 年的数据,并使用改装后的模型预测 2022 年甚至 2023 年。以这种方式做预测可以让我们对未来的房地产市场有一个概念假设事情在 2022 年的某个时候恢复正常模式。
PyCaret 最后几个有用的特性:
您可以轻松地保存您的模型以备后用(保存在 pickle 文件中),并将其加载回笔记本中:
从我们加载的模型的输出来看,我们可以看到我们的整个管道仍然存在:

作者图片
最后,PyCaret 记录我们的整个建模会话,生成各种丰富的元数据。我们可以简单地使用 get_logs()函数在一个数据帧中输出整个会话日志。

作者图片
我们还可以通过 MLflow 中用户友好的 GUI 访问这个日志和其他特性,ml flow 与 PyCaret 配合得很好,但这超出了我们今天的范围。
结论
PyCaret 在一个极其易于激活和管理的管道中提供了丰富的功能。由于它的速度和可定制性,用户能够训练、测试和快速迭代模型。这导致了一个复杂的工作流程来探索业务问题。使用 PyCaret 建模来进行探索可以将洞察引入数据集,并帮助推动项目战略,加速最终模型的开发和交付。
最后,我给你们留下一个有趣的图片,这是我在 PyCaret 狂热的痛苦中在清晰图表中制作的。快乐造型!

作者图片
本次演示使用的数据集是这里的。
笔记本的完整代码是这里。
使用 MedCAT 和 Neo4j 探索电子健康记录
原文:https://towardsdatascience.com/exploring-electronic-health-records-with-medcat-and-neo4j-f376c03d8eef?source=collection_archive---------13-----------------------

图 1:通过文档(粉色)显示与两名患者(蓝色)相关的疾病、症状、药物和程序。作者图片
关于使从自由文本中提取的生物医学概念易于被临床医生和研究人员使用的实践指南
生物医学 NER+L 致力于从电子健康记录(EHRs)中的自由文本中提取概念,并将它们链接到大型生物医学数据库,如 SNOMED-CT 和 UMLS。
在本帖中,我们将关注从自由文本中提取概念后该做什么,换句话说,如何让它们对临床医生和其他研究人员有用。
先决条件:无,除非你想复制结果,那么你需要能够使用 MedCAT 和 Neo4j。
介绍
假设我们访问了一家医院,并在 SNOMED 概念的电子健康记录中注释了所有自由文本。如果我们使用了 MedCAT,那么输出被存储到一个.json文件中。我们的目标是将提取的概念移入一个数据库,该数据库将允许任何人编写简单的查询并利用注释,例如:
- 返回所有患有糖尿病和腿痛的患者
- 返回病人 X 的所有疾病或 返回特定单据中的所有疾病
- 返回所有出现呕吐症状且正在服用药物 valpam 的患者
- 返回所有没有阿尔茨海默氏症但有两次或更多次癫痫发作的男性患者。
- 返回所有患有痴呆或任何其他疾病的患者,这些疾病是 SNOMED 本体论中痴呆概念的直接子代。
虽然关系数据库可以完成这项工作(有些曲折),但是图形数据库更适合。为什么?第一个原因是,我们希望导入 SNOMED/UMLS 本体(基本上是一个有向图),这样我们就可以轻松完成上面例子中的最后一个查询。第二,我们的注释可以很容易地表示为一个图形:病人-[HAS]- >文档-[HAS]- >概念。换句话说:一个病人可以有一个或多个文档,一个文档可以有一个或多个概念的提及(例如疾病)。
在某种程度上,这篇文章的其余部分将是一个教程(伴随着一个 Jupyter 笔记本)。它假设您在 localhost:7474 上安装了一个 Neo4j 数据库。由于我们无法使用真实数据,我从 mtsamples 中创建了两个虚拟文件:
patients.csv包含所有患者的基本信息documents.csv包含文档文本+一个文档 ID
注释文档
在我们做任何事情之前,我们需要为documents.csv中可用的自由文本生成注释。如果您已经用 MedCAT 或任何其他 NER+L 工具处理了文档,那么您可以跳过这一步。
from medcat.cat import CATdf_docs = pd.read_csv('./documents.csv')
# This would be a generator if we have a lot of docs
data = [(k,v) for k,v in df_docs[['documentId', 'text']].values]# You can find the model in the medcat repository
cat = CAT.load_model_pack('./medmen_wstatus_2021_oct.zip')
docs = cat.multiprocessing(data, nproc=10)
json.dump(docs, open("./annotations.csv", 'w'))
填充 Neo4j
创建索引以加速数据接收和搜索(因为数据集可能非常大)
from medcat.neo.data_preparation import *
from medcat.neo.neo_connector import NeoConnector
import pandas as pd# Helper for sending requests to neo
neo = NeoConnector('bolt://localhost:7687/', user='neo4j')# Indexes are pre-defined in the data_preparation helper
for ind in get_index_queries():
try:
neo.execute(ind)
except Exception as e:
print(e)
接下来,开始导入。
# Import Patients
df_pts = pd.read_csv('./patients.csv')
# If you cannot write to this output_dir save somewhere else and copy
q = create_patients_csv(df_pts, output_dir='/var/lib/neo4j/import/')
# Run the query for import
neo.execute(q)
在 neo4j 浏览器(localhost:7474)中,现在将有 182 个带有标签患者的新节点。对于概念、文档以及最后的注释,将重复同样的操作。详细信息在随附的 Jupyter 笔记本中,这里我们跳过并假设所有内容都是导入的。
下面的查询现在应该可以工作并返回数据样本:
MATCH (p:Patient)-->(d:Document)-->(c:Concept) RETURN p, d, c LIMIT 10
输出图(差异是可能的,重要的是取回所有三种节点类型):

作者图片
查询 Neo4j
我们现在可以很容易地将上面展示的每个自然语言查询翻译成 Cypher(neo4j 查询语言)。但是,如果我们不知道 Cypher,MedCAT 有一个小类可以用来查询 Neo4j:
# Get all patients that have the concepts (Fever and Sleep Apnea). #We will set the ignore_meta flag as the medmentions model does not
#have all required meta annotations.patients, q = neo.get_all_patients(concepts=['C0520679', 'C0015967'], limit=10, ignore_meta=True)
或者将所有疾病分配给患者或文档:
# Get all concepts from one patient
stream, q = neo.get_all_concepts_from(patient_id='32', bucket_size_seconds=10**10)
结果可以显示为数据帧:

结束
就这样,medcat.neo 库将随着时间的推移,使用新的查询和导入/导出功能进行更新。
感谢您的阅读。
用 Python 探索 Google Analytics 实时数据
原文:https://towardsdatascience.com/exploring-google-analytics-realtime-data-with-python-8625849c7d7a?source=collection_archive---------13-----------------------
使用 REST API 和 Python 充分利用所有 Google Analytics 特性和数据

马库斯·温克勒在 Unsplash 上的照片
谷歌分析可以提供很多关于流量和用户访问你的网站的信息。在 web 控制台中,这些数据中有很多都是以很好的格式提供的,但是如果您想要构建自己的图表和可视化,进一步处理数据或者只是以编程方式处理数据,该怎么办呢?这就是 Google Analytics API 可以帮助您的地方,在本文中,我们将了解如何使用它来查询和处理 Python 的实时分析数据。
探索 API
在开始使用某些特定的 Google API 之前,先试用其中一些可能是个好主意。使用谷歌的 API explorer,你可以找到哪个 API 对你最有用,它也可以帮助你确定在谷歌云控制台中启用哪个 API。
我们将从实时报告 API 开始,因为我们对实时分析数据感兴趣,其 API explorer 在此处可用。要找到其他有趣的 API,请查看报告登录页面,从这里您可以导航到其他 API 及其浏览器。
为了让这个特定的 API 工作,我们至少需要提供两个值— ids和metrics。第一个是所谓的表 ID ,它是您的分析配置文件的 ID。要找到它,请转到您的分析仪表板,点击左下角的管理,然后选择视图设置,您将在视图 ID 字段中找到 ID。对于这个 API,您需要提供格式为ga:<TABLE_ID>的 ID。
您需要的另一个值是一个度量。您可以在这里从指标栏中选择一个。对于实时 API,您可能需要rt:activeUsers或rt:pageviews。
设置好这些值后,我们可以单击 execute 并浏览数据。如果数据看起来不错,并且您确定这是您需要的 API,那么是时候启用它并为它设置项目了…
安装
为了能够访问 API,我们需要首先在 Google Cloud 中创建一个项目。为此,请转到云资源管理器并点击创建项目。或者,你也可以通过命令行界面,用gcloud projects create $PROJECT_ID来完成。几秒钟后,你会在列表中看到新项目。
接下来,我们需要为这个项目启用 API。你可以在 API 库中找到所有可用的 API。我们感兴趣的是谷歌分析报告 API——可以在这里找到。
API 现在可以使用了,但是我们需要凭证来访问它。根据应用程序的类型,有几种不同类型的凭据。其中大多数都适合需要用户同意的应用程序,比如客户端或 Android/iOS 应用程序。用于我们的用例(查询数据和本地处理)的是使用服务帐户。
要创建服务帐户,请进入凭证页面,点击创建凭证并选择服务帐户。给它起个名字,记下服务帐户 ID(第二个字段),我们马上就要用到它。点击创建并继续(无需给予服务账户访问或权限)。
接下来,在服务帐户页面选择您新创建的服务帐户,并转到密钥选项卡。点击添加密钥和创建新密钥。选择 JSON 格式下载。请确保将其安全存储,因为它可用于在 Google Cloud 帐户中访问您的项目。
完成后,我们现在有了启用了 API 项目和具有以编程方式访问它的凭证的服务帐户。然而,该服务帐户无法访问您的 Google Analytics 视图,因此无法查询您的数据。要解决这个问题,您需要在 Google Analytics 中添加前面提到的服务帐户 ID ( XXXX@some-project-name.iam.gserviceaccount.com)作为用户,并具有读取&分析访问权限-添加用户的指南可在此处找到。
最后,我们需要安装 Python 客户端库来使用 API。我们需要两个,一个用于认证,一个用于实际的 Google APIs:
基本查询
说完这些,让我们编写第一个查询:
我们首先使用我们的服务帐户的 JSON 凭证(之前下载的)对 API 进行身份验证,并将凭证的范围仅限于只读分析 API。之后,我们构建一个用于查询 API 的服务——build函数获取 API 的名称、版本和之前创建的凭证对象。如果您想访问不同的 API,请参见此列表以了解可用的名称和版本。
最后,我们可以查询 API——我们设置了ids、metrics和可选的dimensions,就像我们之前在 API explorer 中做的那样。你可能想知道我在哪里找到了service object ( .data().realtime().get(...))的方法——它们都记录在这里。
当我们运行上面的代码时,print(...)将向我们展示如下内容(为了可读性而进行了调整):
这是可行的,但是考虑到结果是 dictionary,您可能希望访问结果的单个字段:
前面的例子展示了 API 的realtime()方法的用法,但是我们还可以利用另外两个方法。首先是ga():
该方法从 Google Analytics 返回历史(非实时)数据,并且有更多参数可用于指定时间范围、采样级别、分段等。这个 API 还有额外的必填字段— start_date和end_date。
您可能还注意到这个方法的度量和维度有点不同——这是因为每个 API 都有自己的度量和维度集。这些总是以 API 的名称为前缀—在本例中是ga:,而不是前面的rt:。
第三种可用的方法.mcf()是针对多通道漏斗数据,这超出了本文的范围。如果这听起来对你有用,看看文档。
谈到基本查询,最后要提到的是分页。如果构建返回大量数据的查询,可能会耗尽查询限制和配额,或者在一次处理所有数据时出现问题。为了避免这种情况,您可以使用分页:
在上面的代码片段中,我们添加了start_index='1'和max_results='2'来强制分页。这使得previousLink和nextLink被填充,分别用于请求上一页和下一页。然而,这对于使用realtime()方法的实时分析并不适用,因为它缺少所需的参数。
度量和维度
API 本身非常简单。可定制性很强的部分是metrics和dimensions等参数。所以,让我们更好地看看所有的参数及其可能的值,看看我们如何才能充分利用这个 API。
从指标开始—有 3 个最重要的值可供选择— rt:activeUsers、rt:pageviews和rt:screenViews:
rt:activeUsers为您提供当前浏览您网站的用户数量及其属性rt:pageviews告诉您用户正在查看哪些页面rt:screenViews-与页面浏览量相同,但仅与应用程序相关,如 Android 或 iOS
对于每个指标,可以使用一组维度来分解数据。有太多的方法可以在这里列出,所以让我们看看一些指标和维度的组合,你可以插入到上面的例子中,以获得一些关于你的网站访问者的有趣信息:
metrics='rt:activeUsers', dimensions='rt:userType'-根据当前活跃用户是新用户还是老用户来区分他们。metrics='rt:pageviews', dimensions='rt:pagePath'-按路径细分的当前页面浏览量。metrics='rt:pageviews', dimensions='rt:medium,rt:trafficType'-按媒介(如电子邮件)和流量类型(如有机)分类的页面浏览量。metrics='rt:pageviews', dimensions='rt:browser,rt:operatingSystem'-按浏览器和操作系统细分的页面访问量。metrics='rt:pageviews', dimensions='rt:country,rt:city'-按国家和城市细分的页面浏览量。
如您所见,有很多数据可以查询,由于数量庞大,可能有必要对其进行过滤。要过滤结果,可以使用filters参数。语法非常灵活,支持算术和逻辑运算符以及正则表达式查询。让我们看一些例子:
rt:medium==ORGANIC-仅显示有机搜索的页面访问量rt:pageviews>2-仅显示页面浏览量超过 2 次的结果rt:country=~United.*,ga:country==Canada-仅显示来自以“United”(英国、美国)或加拿大开头的国家的访问(,充当OR操作符,AND使用;)。
有关过滤器的完整文件,请参见本页。
最后,为了使结果更具可读性或更容易处理,您还可以使用sort参数对它们进行排序。对于升序排序,您可以使用sort=rt:pagePath,对于降序排序,您可以在前面加上-,例如sort=-rt:pageTitle。
超越实时 API
如果你找不到一些数据,或者你错过了实时分析 API 中的一些功能,那么你可以尝试探索其他谷歌分析 API。其中之一可能是报告 API v4 ,它比旧的 API 有一些改进。
然而,它在构建查询时也有一些不同的方法,所以让我们看一个例子来帮助您开始:
如您所见,这个 API 没有提供大量您可以填充的参数,相反,它只有一个body参数,该参数将我们之前看到的所有值放入请求体中。
如果你想更深入地了解它,那么你应该查看文档中的示例,它们给出了它的特性的完整概述。
结束语
尽管本文只展示了分析 API 的用法,但它应该能让你大致了解如何将所有 Google APIs 用于 Python,因为客户端库中的所有 API 都使用相同的通用设计。此外,前面展示的身份验证可以应用于任何 API,您需要更改的只是范围。
虽然本文使用了google-api-python-client库,但谷歌也在https://github.com/googleapis/google-cloud-python为个人服务和 API 提供了轻量级库。在撰写本文时,用于分析的特定库仍处于测试阶段,缺乏文档,但当它正式发布(或更稳定)时,您可能应该考虑探索它。
本文最初发布于martinheinz . dev
探索图嵌入:DeepWalk 和 Node2Vec
原文:https://towardsdatascience.com/exploring-graph-embeddings-deepwalk-and-node2vec-ee12c4c0d26d?source=collection_archive---------2-----------------------
探索 DeepWalk 和 Node2Vec 从图中提取嵌入
几个月前,我的 Twitter 时间轴开始向我显示一些关于研究人员谈论“新”深度学习革命的推文。这场新的革命是深度学习在图形上的应用。这个新领域对我来说似乎很新,这就是为什么我借此机会了解更多的原因。
在这一系列的文章中,我将分享我对这个新领域的了解。从头开始:获得节点的密集表示,类似于我们在文本字段中对单词嵌入所做的。但是首先,让我们后退一步,让我解释一下什么是图。
什么是图?
一个图可以定义为 G = (V,E)其中 V 是一组节点,E 是一列边。边是两个节点之间的连接,例如,节点 A 和 D 有一条边。此外,重要的是要注意一个图可以是有向的,也可以是无向的。例如,下图是无向图,因为 A 与 D 相连,D 与 A 相连。还有一点,图可以获得不同的节点属性,也可以获得边属性,但对于我们的目的来说,今天并不重要。

无向图的例子
现在我们或多或少知道了什么是图,我们可以尝试从图中提取节点嵌入。正如我之前说过的,这两种方法有点类似于托马斯·米科洛夫用 Word2Vec 提出的方法,但是首先…
为什么我们需要节点嵌入?🧐
让我们假设您需要解决如下场景:
- 我们有社交网络中用户的交互,我们需要预测两个用户何时连接。节点代表用户,边代表两个用户是“朋友”的时候。(链接预测任务)
- 我们有一个研究出版物的引用网络,我们需要预测每个出版物的主题。节点代表出版物,边是从一个出版物到另一个出版物的引用。(节点预测任务)
- 我们有一套蛋白质,分为酶类或非酶类。节点代表氨基酸,如果两个节点之间的距离小于 6 埃,则这两个节点由一条边连接。(图形分类任务)
对于所有提到的任务,我们需要有一个节点的表示。因此,如果我们需要运行我们的机器学习算法,我们需要将我们的图形结构转换到向量空间。这里我们可以找到两个主要的方法 DeepWalk 和 Node2Vec。
深度行走
深走是由石溪大学的研究人员在论文“深走:社交表征的在线学习”(2014)中提出的。
它第一次为嵌入生成引入了随机游走的概念。基本上,随机行走是一种将图转换为节点序列的方法,然后用于训练 Word2Vec 模型。基本上,对于图中的每个节点,该模型生成一个随机的节点连接路径。一旦我们有了这些节点的随机路径,它就训练一个 Word2Vec (skip-gram)模型来获得节点嵌入。
出于学习目的,请在下面找到该算法的实现,请注意,该代码还不适合大规模应用,可以进行一些并行化和内存改进。
Node2Vec
Node2Vec 是由斯坦福大学的研究人员在论文中提出的:“ node2vec:网络的可扩展特征学习”(2016)。
该算法使用了 Deepwalk 提出的一些想法,但更进了一步。它使用算法 DFS 和 BFS 的组合来提取随机漫步。这种算法组合由两个参数 P(返回参数)和 Q(输入输出参数)控制。
基本上,如果 P 很大,随机漫步也会很大,所以它进行探索,如果 P 很小,我们停留在局部。类似但相反的行为发生在 Q 上,如果 Q 小,它将进行探索,如果 Q 大,它将停留在本地。更多细节可以在原论文中找到。
我们可以使用 PyTorch geometric 来测试 Node2Vec。这个库实现了一系列图形神经网络架构和方法来加速 GNN 的工作。为了测试它,我将使用 Pytorch geometric 上提出的教程的一小部分。为此,他们使用了 Cora 数据集。Cora 数据集包括 2708 份科学出版物,分为七类。引文网络由 5429 个链接组成。数据集中的每个出版物由 0/1 值的词向量来描述,该词向量指示字典中相应词的存在与否。这部词典由 1433 个独特的单词组成。
一旦模型被训练,我们将对图中的每个节点进行嵌入,并且每个嵌入将是 128 维的。训练之后,我们可以保存嵌入,并在嵌入投影仪中查看表示与标签相比有多“好”。为此,我使用 T-SNE 算法将 128 维数据缩减为 3 维数据,这样我们就可以绘制它了。

投影到三维空间的节点嵌入
正如我们所看到的,具有相似主题的节点一起出现在表示中。请注意,我们正在进行大规模降维,以便在视觉空间中绘图,因此可能有些样本并不完美。
现在我们有了节点嵌入,我们可以训练机器学习算法来预测主题。所以,我们不需要图表。)
并非所有闪光的都是金子🏅
正如我们所看到的,我们可以为我们的图构建节点嵌入,但是,这些节点嵌入有几个问题,其中一些是:
- 嵌入依赖于图,所以如果一个新的节点出现,我们需要重新计算嵌入。这没什么大不了的,我们可以做些改进来解决这个问题,但这肯定是需要考虑的事情,尤其是如果图表会不时发生变化的话。
- 我们只获取节点之间的连接信息,但不使用节点或边的特征。那么,考虑到节点和边的信息或特征,我们能生成更好的嵌入吗?是的,使用图形神经网络和消息传递框架,这是另一个帖子…
结论📚
在这篇文章中,我解释了如何为我们的图训练节点嵌入。一旦我们有了这些嵌入,我们就可以像往常一样运行机器学习模型。此外,我专注于实际问题,但这项技术背后有一些有趣的数学。我发现这个视频真的很有趣,可以了解更多模型背后的数学知识。我希望你觉得有趣。
关于作者
Marcos Esteve 是 Ravenpack 的机器学习工程师。马科斯在工作中为各种各样的自然语言处理任务开发了机器和深度学习模型。他对多模态任务和构建数据科学应用非常感兴趣。在 Linkedin 或 Twitter 上联系他
使用 BigQuery 和 Data Studio 探索冰雹报告
原文:https://towardsdatascience.com/exploring-hail-reports-with-bigquery-and-data-studio-dd6f903b624b?source=collection_archive---------26-----------------------
在 Data Studio 中使用新的地理空间功能
让我们通过探索 NOAA 风暴预测中心冰雹风暴报告的公共数据集,来体验一下 Data Studio 中新的地理空间功能。请跟随我——有一个免费的 BigQuery 沙箱可供您使用。Data Studio 是一款免费产品。
在 BigQuery 中查询
转到位于https://console.cloud.google.com/bigquery的 BigQuery web 控制台,键入以下查询:
SELECT
FORMAT_DATE('%B', event_begin_time) AS month,
magnitude AS hail_inches,
**event_point**
FROM `bigquery-public-data.noaa_historic_severe_storms.storms_2020`
WHERE event_type = 'hail'
在上面的查询中需要注意的重要一点是 event_point 是一个地理类型。如果您的表有纬度和经度列,那么使用以下命令从这些列创建一个地理类型:
ST_GeogPoint(longitude, latitude) as event_point
关于加载地理数据的更多信息,请查阅 BigQuery docs 。
Data Studio 中的地理空间数据
然后使用 BigQuery web 控制台中的下拉菜单“Explore Data ”,选择 Data Studio。
在 Data Studio 中,将图表更改为地图:

然后拖动可用字段来设置图表,如下所示:
- 拖动 hail_inches 字段作为位置维度。根据冰雹的大小,圆圈会变大或变小。
- 将 event_point 字段拖动到地理空间字段。它将设置圆圈的位置。
- 移除颜色度量。
这将是它的样子:

这是大量的数据。让我们允许用户过滤东西。
过滤
将“月份”字段拖到顶部显示“过滤器”的位置。这将允许用户按月过滤图表。
如果观众只选择一月和二月,他们会得到:

BigQuery 中的聚合
我们当然可以使用 BigQuery 来聚集数据,然后在 Data Studio 中可视化聚集的数据。例如,让我们按月找到质心并显示它们,而不是显示每个月的所有冰雹报告。
现在的问题是:
SELECT
FORMAT_DATE('%B', event_begin_time) AS month,
COUNT(*) AS num_events,
ST_CENTROID_AGG(event_point) AS centroid
FROM `bigquery-public-data.noaa_historic_severe_storms.storms_2020`
WHERE event_type = 'hail'
GROUP BY month
按如下方式设置 Data Studio:
- 将质心设置为地理空间字段
- 将月份设置为颜色维度(这样不同的月份会有不同的颜色)
您将获得以下关于形心逐月变化的视图:

结合两种方法
我们还可以通过以下方式将这两种方法(总量+点数)结合起来:
WITH hail AS (
SELECT
FORMAT_DATE('%B', event_begin_time) AS month,
MAX(magnitude) AS max_hail,
COUNT(*) AS num_events,
**ST_UNION_AGG**(event_point) AS **points**,
ST_CENTROID_AGG(event_point) AS **centroid**
FROM `bigquery-public-data.noaa_historic_severe_storms.storms_2020`
WHERE event_type = 'hail'
GROUP BY month
)SELECT
*,
**ST_CONVEXHULL**(points) AS **hull**
FROM hail
现在我们既有单个的点,也有像质心和外壳这样的集合。
这是一月份的船体:

相对于单个点:

共享报告
要共享报告,请单击共享按钮。选择创建新报告。
然后,选择“添加控件”,选择下拉列表,并将控件字段设置为“月”。类似地,添加另一个控件,选择 Slider 并将控件字段设置为 hail_inches。该报告现在将如下所示:

将报告的名称更改为 2020 年冰雹月报告。然后单击“查看”,查看与您共享报告的人会看到什么。注意他们如何使用月份和滑块来探索。例如,这是所有 1.7 英寸以上冰雹的视图。地图会自动缩放至感兴趣的区域。

要将报告嵌入为 iframe,您可以通过选择“Share”下的下拉菜单来获得嵌入的 html 代码。
稍后自定义报告
你可以通过导航到https://datastudio.google.com/找到你正在处理的报告。数据的来源将在“数据源”中,以防您想要定制查询。
尽情享受吧!
探索 K-最近邻分类
原文:https://towardsdatascience.com/exploring-k-nearest-neighbour-classification-514d09175248?source=collection_archive---------37-----------------------
利用从 Taarifa 和坦桑尼亚水利部数据汇编的坦桑尼亚水泵数据集,勘探 KNN 并预测坦桑尼亚水井的功能。

Shutterstock 图像(标准许可)
数据科学有许多精彩的应用,其中之一是它对社会公益的用途——分析和寻找复杂问题的创新、数据驱动的解决方案。
这一分析展示了一个这样的例子。通过数据预处理、清理、探索性数据分析和建模阶段,我将探索在坦桑尼亚水泵功能的三级分类分析中使用 K 近邻。具体来说,预测它们是有功能的、无功能的还是有功能但需要修复的。
这个案子的背景
坦桑尼亚是一个东非国家,今天是世界上经历严重水危机的几个国家之一,尽管该国拥有丰富的自然水资源,但仍有数百万人因无法获得安全、清洁的水和卫生设施而遭受苦难。
在总人口为 5800 万的情况下,这场危机目前影响了约 2460 万人,占总人口的 40%以上,令人震惊,并导致了广泛的严重后果,特别是霍乱、伤寒和其他水传播感染和疾病。联合国儿童基金会估计,坦桑尼亚大约 70%的卫生预算用于可预防的疾病,这些疾病与普遍缺乏清洁水和改善的卫生设施直接相关。
因此,本分析的目的是深入探讨这一问题,重点分析水泵的功能,因为水泵仍然是农村地区大部分人口获取水的主要来源,并使用 K-最近邻法建立分类模型,以确定可以探索的改善数百万坦桑尼亚人状况的可能途径。
k 近邻(KNN)是一种有效且简单的监督学习算法,可用于分类和回归问题。它通过计算呈现的每个数据点之间的距离来发挥作用,并通过取 K 个最近点的公共类来生成预测。
下面是所用数据集的摘录。

观察数据框的形状,有 59,400 个数据条目(行)和 40 个独立变量(列)。快速运行以总结重复的条目可以确认每个条目都是唯一的。
预处理数据
清理数据时,我通常采取的第一步是检查空值,因为,假设我对保留大多数或所有列感兴趣,考虑如何处理丢失的值可以让我更好地理解数据集,并知道哪些列可能会被删除。

数据清理通常会占用数据科学家大量的工作时间。它通常可以被描述为最乏味的步骤,因为我们大多数人都迫不及待地开始修补模型并创建可视化,以查看我们是否可以为我们试图解决的问题提取任何有用的见解。
然而,我一直认为数据清理是一种艺术形式,尤其是在处理缺失数据时,因为所采取的决策将直接影响建模阶段。为了最大限度地保存数据集,您可以做出哪些最佳决策?
在这种情况下,我决定用以下方式处理它(查看 Github 的完整代码):
- 子村 —该列有许多独特的类别,因此用新的“未知”类别替换 371 个缺失值
- 公开会议 —这个专栏似乎可以有某种程度的解释,但绝大多数条目都是“真实的”(51011 v. 5055)。假设解释为水泵所在的区域是公共集会的场所,则在大多数情况下,空值也可能被假定为“真”。
- 许可 —在 3065 个缺失值中,有 2424 个来自未知的安装者和/或资助者,所以对于这些,我假设他们没有许可。但剩下的 370 家——它们的经营者或管理者要么是半国营企业,要么是水务局,在这种情况下,鉴于与当局的联系,许可证被认为是“真实的”。
- 出资方&安装方 —按照以下方式比较两列:

由于在更多情况下,资金提供者和安装者的条目不同(当条目都不为空时为 36,762 行),因此用新的“未知”类别填充两列的缺失值更安全。
- 管理&方案管理 —这两列的条目总体上看起来相似,对比如下:

管理列没有空条目,因此,用管理列中的相应值替换 scheme_management 中缺少的 3,877 个值并不太牵强。
- 方案名称 —许多缺失和完整的唯一值,因此最好删除此列,因为它不太可能在分析中提供任何值。
在下一步中,我选择了一些要删除的附加列,因为它们显示了相同的信息(例如,“付款类型”和“付款”)。
最后,数据预处理结束。
建模准备
- 定义因变量和自变量
准备数据的第一步是定义自变量和目标变量 X 和 y。由于模型旨在预测泵的功能,目标变量 y 为“status_group”。

2.地址分类变量
坦桑尼亚水数据集有许多分类变量,需要在建模前进行处理和虚拟化。然而,这些变量中的一些有太多的唯一值,包括“资助者”(1,897)、“安装者”(1,935)、“子村”(19,288)和“受监护人”(2,092)。
保留这些变量并创建它们的虚拟对象会导致一个非常复杂的数据集,有数千个维度。因此,出于分析的目的,我决定删除这些列。

3.最终确定数据框架
在为所有变量及其各自的唯一值创建虚拟变量之后,最后一步是通过将分类特征(现在是具有 240 列的数据框架)和连续特征(以前是 6 个确定的列)放回到单个数据框架 X 中来最终确定用于建模的 X。

它的最终形状是 59,400 行和 246 列。数据已准备好用于建模。
使用 KNN 的数据建模
现在,数据预处理清理和准备工作已经完成,建模数据就像实例化模型一样简单,将它安装在列车集合上,然后进行预测。

在这种情况下,我使用默认参数进行实例化,其中 k (n_neighbors) = 5。有关可用参数的全部细节,请查看 scikit-learn KNN 文档。
此外,通过定义一个函数来比较模型在训练集和测试集上的准确性分数,可以更好地了解该模型与数据的拟合程度。

在这种情况下,与测试集上的 76.32%相比,训练集上的准确率为 82.94%,这表明了某种程度的过拟合。减少过度拟合的一个可能途径是增加 k 值。
否则,看来具有最低精确度、召回率和 f1 分数的类别是“功能性需要修复”。当考虑每个类别之间的分布时,该类在数据集中的事例数是最少的,如下图所示,因此它是有意义的。

结论
KNN 是一种可用于分类问题的通用模型,但是数据集越大,所需的计算量就越大,这使得计算复杂性成为选择该模型时要考虑的一个重要因素。
总的来说,这个模型表现得还不错,特别是在预测水井的非功能性时。因此,这可以为坦桑尼亚当局提供关键信息,说明水和卫生发展项目应优先考虑哪些领域,以便为用水有限的社区提供便利。
在这种情况下,考虑到功能正常但需要维修的水泵的精度和召回率,这种模式可能会受益于功能性和非功能性之间的二元划分,以便对该国最需要改善供水的地区进行分类。
使用 Python 的 Matplotlib 探索折线图
原文:https://towardsdatascience.com/exploring-line-charts-with-pythons-matplotlib-4b911cf6b4bc?source=collection_archive---------15-----------------------
次轴、插值、连接散点图等

折线图—图片由作者提供
折线图绝对是数据可视化领域的明星,大多数受众都很熟悉,其直接的格式使见解、趋势和模式易于识别。
尽管它们很容易画出来,但还是有一些灰色区域很难导航,有时,我们可能会为了方便或一致性而试图避开它们。
本文将探讨折线图以及可视化数据时实验的重要性。
在下面的例子中,我们将使用一些虚拟数据。
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
import pandas as pd
from scipy import interpolateurl = '[https://gist.githubusercontent.com/Thiagobc23/4ccb4ea1c612d9d68921bf990ce28855/raw/6225824a6b7d5d273019c09c25cbbaa5b82009bc/dummy_data.csv'](https://gist.githubusercontent.com/Thiagobc23/4ccb4ea1c612d9d68921bf990ce28855/raw/6225824a6b7d5d273019c09c25cbbaa5b82009bc/dummy_data.csv')df = pd.read_csv(url, index_col='ID')
df.head()

虚拟数据—作者提供的图片
让我们从一个简单的折线图开始,使用一些自定义颜色并添加网格线。
# figure
fig, ax = plt.subplots(1, figsize=(12,4), facecolor='#293952')
ax.set_facecolor('#293952')# data
price = df['Price']# plot
plt.plot(df.Date, price, marker='o', markersize=4, color='#FDAC53', linewidth=2.5)# ticks n title
ax.tick_params(axis='both', colors='w')
plt.xticks(df.Date[::3])
plt.title('Price\n', loc='left', color='w', fontsize=16)# spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_color('w')
ax.spines['bottom'].set_color('w')# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='#FDAC53', linestyle='dashed', alpha=0.5)plt.show()

简单折线图—图片由作者提供
基数零
绘制折线图的常见做法是从零开始绘制 y 轴,这通常被描述为数据可视化的规则,而不是建议。
没有锁定在零点的图表有夸大变化和误导观众的名声,在我看来,这并不总是正确的。

无论是否从零开始,这都是你的图表——作者提供的图片
如果 y 轴以精确的刻度正确显示,并且用户假设它从零开始而没有读取它,那么这是一个数据读写问题,而不是设计问题。
不是从零开始的图表更好地显示变化和趋势,而从零开始的图表更好地描绘这些变化和趋势的影响。
# figure
fig, ax = plt.subplots(1, figsize=(12,4), facecolor='#293952')
ax.set_facecolor('#293952')# data
price = df['Price']# plot
plt.plot(df.Date, price, marker='o', markersize=4, color='#FDAC53', linewidth=2.5)# ticks n title
ax.tick_params(axis='both', colors='w')
plt.xticks(df.Date[::3])
plt.title('Price\n', loc='left', color='w', fontsize=16)# spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_color('w')
ax.spines['bottom'].set_color('w')# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='#FDAC53', linestyle='dashed', alpha=0.5)# limits
plt.ylim(0,) # or -> ax.set_ylim(0,)plt.show()

折线图—作者提供的图片
插值
线条流畅的看起来更好,但也仅此而已。除了美观之外,平滑图表的线条没有任何其他好处,这种微小的改进有可能会歪曲数据。
然而,我们可以用它做实验,自己决定什么时候合适。

平滑或笔直,这是你的图表——作者的图像
在上面的例子中,差异可以忽略不计,我怀疑有人会因为插值而对变化和趋势做出不同的解释。尽管如此,这是有风险的,应该谨慎使用。
# figure
fig, ax = plt.subplots(1, figsize=(12,4), facecolor='#293952')
ax.set_facecolor('#293952')# data
price = df['Price']# interpolate
x = np.arange(0, len(price))
y = priceinter = interpolate.interp1d(x, y, kind = 'cubic')
new_x = np.arange(0, len(y)-1, 0.01)
new_y = inter(new_x)# interpolated lines
plt.plot(new_x, new_y, color='#FDAC53', linewidth=1.5)
# data points
plt.scatter(x, price, color='#FDAC53', s=25, zorder=3)# ticks and title
ax.tick_params(axis='both', colors='w')
plt.xticks(x[::3], df.Date[::3])
plt.title('Price\n', loc='left', color='w', fontsize=16)# spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_color('w')
ax.spines['bottom'].set_color('w')# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='#FDAC53', linestyle='dashed', alpha=0.5)# limit
plt.ylim(0,)plt.show()

折线图—图片作者| 代码
双轴
让我们从绘制汇率和价格开始。
# figure
fig, ax = plt.subplots(1, figsize=(12,4), facecolor='#293952')
ax.set_facecolor('#293952')# data
price = df['Price']
rate = df['Exchange Rate']# plots
plt.plot(df.Date, price, marker='o', markersize=4, color='#FDAC53', linewidth=2.5)
plt.plot(df.Date, rate, marker='o', markersize=4, color='#55A380', linewidth=2.5)# ticks n title
plt.title('Price and Exchange Rate\n', loc='left', color='w', fontsize=16)
ax.tick_params(axis='both', colors='w')
plt.xticks(df.Date[::3])# spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_color('w')
ax.spines['bottom'].set_color('w')# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='#FDAC53', linestyle='dashed', alpha=0.5)# legend
legend = plt.legend(['Price', 'Rate'], frameon=False, ncol=2)
plt.setp(legend.get_texts(), color='w')plt.show()

多线图表—作者提供的图片
我们之前看到的价格变化在这里没有得到充分体现。
假设我们有第三个变量,即成本,它是价格和费率的乘积。当我们使用上面的图表来理解这一成本的构成时,我们会假设利率的上升会比价格的下降更加显著。但在现实中,这不是真的。

价格*价格—作者图片
用一个副轴来理解这个变量的组成就更简单了。
# figure
fig, ax = plt.subplots(1, figsize=(12,4), facecolor='#293952')
ax.set_facecolor('#293952')# data
dt = df.Date
price = df['Price']
rate = df['Exchange Rate']# plot
plt.plot(dt, price, marker='o', markersize=4, color='#FDAC53', linewidth=2.5)
ax2 = ax.twinx()
ax2.plot(dt, rate, marker='o', markersize=4, color='#55A380', linewidth=2.5)# ticks
ax.tick_params(axis='both', colors='w')
ax2.tick_params(axis='both', colors='w')
plt.xticks(dt[::3])# spines
for i in ['top', 'bottom']:
ax.spines[i].set_visible(False)
ax2.spines[i].set_visible(False)
ax2.spines['left'].set_color('#FDAC53')
ax2.spines['left'].set_linewidth(2)
ax2.spines['right'].set_color('#55A380')
ax2.spines['right'].set_linewidth(2)# grid
ax.set_axisbelow(True)
ax.xaxis.grid(color='w', linestyle='dashed', alpha=0.5)# labels n title
ax.set_ylabel('Price', color='w', fontsize=12)
ax2.set_ylabel('Exchange Rate', color='w', fontsize=12)
plt.title('Price and Exchange Rate\n', loc='left', color='w', fontsize=16)# limits
ax.set_ylim(0,)
ax2.set_ylim(0,)plt.show()

价格和费率双轴—图片作者| 代码

价格*价格—作者图片
连通散点图
x 轴不一定需要代表时间,连接数据点的线已经完成了这项工作。这意味着我们可以使用第二个变量的 x 轴,将可视化集中在这两个变量随时间的相关性上。
箭头,标签,甚至颜色都可以用这种类型显示时间流动的方向。线条使用渐变颜色也有助于区分重叠部分。
# data
x = df['Price']
y = df['Exchange Rate']# points n segments
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)# figure
fig, ax = plt.subplots(1, figsize=(12,7), facecolor='#293952')
ax.set_facecolor('#293952')# create a collection of lines
lc = LineCollection(segments, cmap='Wistia' , alpha=0.9)# set the data that'll define the colors
lc.set_array(df.index) # try changing df.index for x or y
lc.set_linewidth(2.5)# plot line collection
ax.add_collection(lc)# date labels left
labels_df_l = df[df['Date'].isin(['2019-01', '2020-04', '2020-07'])].copy()
for key, record in labels_df_l.iterrows():
ax.text(record['Price'] + 0.2, record['Exchange Rate'], record['Date'], color='w')
# date labels right
labels_df_r = df[df['Date'].isin(['2019-03', '2019-06', '2019-12', '2021-01', '2021-06'])].copy()
for key, record in labels_df_r.iterrows():
ax.text(record['Price'] - 0.25, record['Exchange Rate'], record['Date'], color='w', ha='right')
# labels n title
ax.set_xlabel('Price', color='w')
ax.set_ylabel('Exchange Rate', color='w')
ax.set_title('Price and Exchange Rate - Product XYZ\n', loc='left', color='w', fontsize=18)# spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_color('w')
ax.spines['bottom'].set_color('w')# tick colors
ax.tick_params(axis='both', colors='w')# grids
ax.set_axisbelow(True)
ax.yaxis.grid(color='#FDAC53', linestyle='dashed', alpha=0.3)
ax.xaxis.grid(color='#FDAC53', linestyle='dashed', alpha=0.3)# limits
ax.set_xlim(x.min() - 3, x.max() + 3)
ax.set_ylim(y.min() - 3, y.max() + 3)plt.show()

连通散点图—图片作者| 代码
关联散点图是一种图表,当它符合数据时,它是非常惊人的。不幸的是,对于大多数数据集来说,它往往会变得杂乱或像意大利面条图一样散开。
结尾部分
我们不应该拒绝需要观众付出努力的解决方案。
过分简化一个主题以适应小块的信息会使洞察力和模式更难识别,甚至更糟,使用户对呈现的主题失去兴趣。
指南和规则是产生高质量可视化的一个很好的方法,并且可以减少误解。但是实验可以产生真正伟大的图表,避免误导可视化的最好方法是保持观众的数据素养。
感谢阅读!在这里你可以找到更多这样的文章。
用 PyCaret & MLflow 探索模型超参数搜索空间
原文:https://towardsdatascience.com/exploring-model-hyperparameter-search-space-with-pycaret-mlflow-b6c630d71723?source=collection_archive---------20-----------------------
时间序列预测的简化回归模型

照片由 Serg Antonov 在 Unsplash 拍摄
📚介绍
您是否曾经查看过模型的超参数,并想知道它们对模型的性能有什么影响?你并不孤单。虽然有些影响可以直观地得出,但在其他情况下,结果却与直观相反。这些超参数不单独工作(单变量)而是相互结合(多变量)的事实加剧了这种情况。在决定网格搜索的搜索空间时,理解超参数对模型性能的影响变得更加重要。选择错误的搜索空间会导致浪费挂钟时间,而不会提高模型性能。
在本文中,我们将看到如何将 Pycaret 与 MLFlow 结合使用,以更好地理解模型超参数。这将有助于我们在执行网格搜索时选择一个好的搜索空间。
📖建议的先前阅读
在本文中,我们将以 PyCaret 的时间序列模块为例来探讨这个概念。具体来说,我们将研究用于预测的简化回归模型。如果你不熟悉这些模型,我推荐这篇短文。
👉用于时间序列预测的简化回归模型
另外,如果您不熟悉 PyCaret 和 MLFlow 是如何协同工作的,这是一篇很有帮助的短文。
👉【MLFlow 时序实验测井
1️⃣简化回归模型超参数
简化的回归模型有几个超参数,但是有几个重要的是sp和window_length。在上面的“上一次阅读”中,建议在将数据传递到回归器进行建模之前对其进行去季节性处理。sp表示用于消除数据季节性差异的季节周期。此外,建模者需要决定在训练过程中将多少先前的滞后馈入回归模型。这由window_length超参数控制。
***sp :*** *int, optional
Seasonality period used to deseasonalize, by default 1****window_length*** *: int, optional
Window Length used for the Reduced Forecaster, by default 10*
2️⃣超参数直觉
许多数据集表现出季节性模式。这意味着任何给定时间点的值将紧密跟随一个或多个季节的数据(由季节周期或sp确定)。因此,保持sp固定在季节周期可能是直观的(例如,可以从 ACF 图中获得)。类似地,人们可能希望将至少一个季度的数据传递给回归器,以便对自回归属性进行建模。因此,我们的直觉可能会告诉我们让window_length也等于季节周期。
PyCaret 的 3️⃣实验
现在,我们来看一个数据集,看看这些超参数有什么影响。在这个练习中,我们将使用经典的“航空公司”数据集。注意:本文末尾提供了一个 Jupyter 笔记本供参考。

航空数据集(图片由作者提供)
该数据集展示了 12 个月的季节性模式,这由 ACF 在 12 的倍数(12、24、36 等)处的峰值来指示。)

ACF:航空公司数据集(图片由作者提供)
尝试窗口长度
让我们创建一个pycaret实验,在这里我们改变window_length,并检查它对模型性能的影响。我们将在设置中启用 MLflow 日志记录。
exp.setup(
*data*=y, *fh*=12, *session_id*=42,
*log_experiment*=True,
*experiment_name*="my_exp_hyper_window",
*log_plots*=True
)*#### Create models with varying window lengths ----* for window_length in np.arange(1, 25):
exp.create_model("lr_cds_dt", *window_length*=window_length)
现在可以在 MLflow 中可视化该实验的结果。MLflow 提供了一个非常方便的功能来比较一个实验的多次运行。我们可以利用这个特性来看看window_length的影响。

MLflow 中可视化窗口长度的影响(图片由作者提供)
结果与我们的直觉相符,即在窗口中包含至少 12 个点提供了最佳性能。任何低于 11 分的数据都会降低性能,因为我们没有包括上一个赛季的数据。任何超过 12 个数据点的结果都相当稳定,接近最佳度量值。
对季节周期进行实验
接下来,我们对sp重复同样的操作。
*#### Setup experiment with MLFlow logging ----* exp.setup(
*data*=y, *fh*=12, *session_id*=42,
*log_experiment*=True,
*experiment_name*="my_exp_hyper_sp",
*log_plots*=True
)*#### Create a model with varying seasonal periods ----* for sp in np.arange(1, 25):
model = exp.create_model("lr_cds_dt", *sp*=sp)

MLflow 中显示的季节周期的影响(图片由作者提供)
这个实验的结果有点出乎意料。虽然我们在季节周期=12 时得到了好的结果,但在季节周期=3、4、6、8 和 9 时也得到好的结果。这可能是因为对于月度数据(如航空数据集),周期 3、6、9 分别代表 1、2 和 3 个季度。这些也可以是数据重复自身的有效周期。4 和 8 的季节周期有点难以直观地解释,但可能表示数据分别一年重复 3 次或两年重复 3 次(也不是完全没有问题,尽管可能性比其他周期小)。
无论如何,这带来了超参数直觉和调优的挑战。通常很难提前确定“最佳”超参数,但至少我们可以直观地将它们设置为一个合理的值。此外,如上所述,超参数不倾向于孤立工作(单变量)。它们相互作用,还会以其他非直观的方式影响模型性能。接下来让我们检查一个多变量的情况。
尝试窗口长度和季节周期(多元)
在这个实验中,我们同时考虑window_length和sp来探索搜索空间。
*#### Create a model with varying window_length & sp ----*runs = 50
window_lengths = np.random.randint(1, 25, runs)
sps = np.random.randint(1, 25, runs)for window_length, sp in zip(window_lengths, sps):
model = exp.create_model(
"lr_cds_dt", w*indow_length*=window_length, *sp*=sp
)
MLflow 还提供了使用等高线图可视化双变量数据的能力。在这种情况下,我们在 x 轴上绘制了window_length,在 y 轴上绘制了sp。颜色代表选择的度量标准(本例中为 MAE)。蓝色阴影越浅,误差越小,因此这种情况下超参数选择越好。

在 MLflow 中可视化的窗口长度和季节周期的影响(图片由作者提供)
我们可以从这些结果中看到,即使在多变量设置中,任何小于 12 个数据点的window_length都不会给出最佳结果(蓝色阴影)。任何高于 12 的值似乎都相当不错。对于季节周期,只要window_length大于 12,精确值似乎对双变量设置没有很大影响。(注意:如果您想知道,黑色区域是没有足够的样本点来构建轮廓的区域)。
🚀结论和下一步措施
我们现在可以使用这种直觉来指导我们未来遇到类似数据集时的超参数搜索空间。或者,查看下面的参考资料,了解pycaret如何以最佳和自动化的方式执行超参数调优。
👉py caret 中时间序列模型的基本超参数调整
pycaret还为用户提供了在超参数调整期间控制搜索空间的选项。用户可能希望像我们在本文中所做的那样,基于手动评估来这样做。查看下面的文章了解更多细节。
👉py caret 中时序模型的高级超参数调整
暂时就这样了。如果你想和我联系(我经常发布关于时间序列分析的文章),你可以通过下面的渠道找到我。下次见,预测快乐!
🔗领英
🐦推特
📘 GitHub
喜欢这篇文章吗?成为 中等会员 继续 无限制学习 。如果你使用下面的链接, ,我会收到你的一部分会员费,而不会对你产生额外的费用 。
https://ngupta13.medium.com/membership
📗资源
- Jupyter 笔记本 包含本文代码
使用卫星图像探索北加州正在衰退的巨藻森林
原文:https://towardsdatascience.com/exploring-northern-californias-declining-bull-kelp-forests-using-satellite-imagery-1cf4109b3e78?source=collection_archive---------39-----------------------

在加拿大不列颠哥伦比亚省巨藻丛中漫步的海獭。基兰·伍德在 Unsplash 上拍摄的照片
背景
加州沿海的海带森林一直在减少,巨藻[Nereocystis luetkeana]对气候变化和 2013 年海星消耗病导致的海胆过多特别敏感1。气候变化被认为是这些海藻森林退化的部分原因,但具体到什么程度还不得而知。研究人员观察到,从 2014 年到 2019 年,北加利福尼亚的海带持续减少,各种西海岸机构正在研究加强海带监测工作的方法3。本文的目的是利用公开的工具和数据调查被认为受到影响的海岸线子集。
作为一名前航空航天工程师、自然爱好者和总体数据极客,我越来越感兴趣的是,我们可以简化专业人士和爱好者对环境科学数据的访问,同时传播一个我认为非常重要的信息;我们的世界在变化,我们的工具也在进步,但我们如何使用这些工具将极大地决定我们在这个星球上的未来。
该项目使用 ArcMap 10.7 软件,探索如何使用公开的卫星图像来监测这些海藻森林的变化,这些海藻森林是各种海洋物种的重要栖息地和繁殖地。除了海星消耗病之外,由于失去了这种顶级捕食者,失去以海胆为食的食肉海獭可能会进一步影响生态系统的平衡。此外,在这些年里,观察到了大型海洋热浪事件,增加了巨藻林面临的压力清单。
更多关于 Landsat 8 卫星计划的背景资料,见 这里 。地理空间分析和遥感的一些知识对阅读本文很有帮助,在线资源也很丰富。
此项目的最终简化网络地图可在 此处 找到:
好的。让我们开始吧…
术语
下面列出了一些背景术语,但是对地理空间信息系统(GIS)的大致了解有助于理解本文的工作。可以在这里 和网络上的其他地方找到一个快速概览 。
缓冲区-在地理信息系统和空间分析中,缓冲区分析是指确定地理要素周围的区域,该区域包含位于该要素指定距离内的位置,即缓冲区
美国地质调查局和美国国家航空航天局的联合科学卫星,通过使用遥感技术研究和拍摄地球表面。有关 Landsat 8 卫星计划的背景和资源,请参见美国地质调查局的 本概述 。
RGB — RGB(红、绿、蓝)指的是一种在计算机显示器上显示颜色的系统。红色、绿色和蓝色可以以各种比例组合,以获得可见光谱中的任何颜色
GIS —地理信息系统是一个概念化的框架,能够捕获和分析空间和地理数据
NDVI——归一化差异植被指数(NDVI)是一个简单的图形指标,可用于分析遥感测量结果,通常来自空间平台,通过使用可见光和近红外波段的反射光来评估被观察的目标是否包含活的绿色植被。
栅格 —最简单的形式是,栅格由组织成行和列(或网格)的像元(或像素)矩阵组成,其中每个像元包含一个代表信息的值,如温度。光栅图像是数字航空照片、卫星图像、数字图片,甚至是扫描地图。
掩膜-掩膜是一种通过隐藏或屏蔽一个图层的要素和另一个图层的重叠要素来阐明密集或详细地图内容的技术。
设置
为了进行这一分析,从美国地质调查局https://earthexplorer.usgs.gov/检索了 2015-2019 年 9 月至 11 月海藻高峰期的 Landsat 8 图像(关于检索 Landsat 图像,请参见本指南;关于 Landsat 8 的栅格波段定义,请参见本指南https://www.usgs.gov/faqs/what-are-band-designations-landsat-satellites?qt-news_science_products=0)。在此期间,继 2014 年北加州发生严重的海带损失事件后,人们广泛观察到了持续的海带损失,希望能够详细探索该地区的一个子集。每个日期都使用了来自路径 45/行 33 的场景,如下图红色所示。****

图 1:通过美国地质勘探局地球探测器探测大型海藻的感兴趣位置
我选择这个地区是因为它局限于单一陆地卫星项目中的一个陆地卫星“场景”,主要包含巨藻[2]。保持在一个计划内,Landsat 8,消除了跨计划比较图像的需要,这本身就提出了挑战。Landsat 8 于 2013 年发射,但截至本文撰写之时,计划于 2021 年 9 月下旬发射 Landsat 9,这将在未来几年提供连续的地球成像。
历史上,巨藻因其季节性生长特性,对海胆贫瘠和气候变化更为敏感。海带生长高峰期(9 月至 11 月)的数据被平均化,以表征特定年份的海带数量。每个日期四个文件,每年三个日期导致总共 60 个文件。它们是根据低云量选择的,并遵循下面表 1 中列出的格式。

表 1:用于分析的 Landsat 8 文件格式示例。四个。TIF 为每个日期归档。
这些图像被缓冲并屏蔽了海岸线,只包括离岸 200 到 2000 米的水域,以帮助最大限度地减少海滩碎片、沙子、岩石和其他不是海藻或水的地形的海岸分类错误。
此外,使用波段 1 和 QA 栅格波段遮蔽了云和烟雾,以进一步减少分类时的数据误差。
方法
为简单起见,使用 归一化差异植被指数 (NDVI)来检测巨藻位置。我探索了其他海带分类方法,并在最后列出了一些相关的研究论文,供进一步阅读。
Landsat 8 影像的 NDVI 通常用于农田和陆地植被,并通过以下公式从可见红色波段(波段 4)和近红外波段(NIR 波段 5)获得:

这导致指数范围从-1.0 到+1.0,较高的值表示存在植被。像素质量(QA)和气溶胶(B1)栅格数据用于在可能的情况下从评估中移除云和烟雾像素。
使用 ArcGIS 栅格计算器,为每个感兴趣的栅格数据集计算 NDVI,水中的低正值假定为海带。
遮蔽云和烟雾后,结果在 ArcGIS 中与缓冲研究区域“相交”。作为示例,下图 2 显示了感兴趣区域内的初步 NDVI 地图,其中植被区域显示为白色。使用模型构建器将其进一步开发成 ArcGIS 模型,如图 4 所示。

图 ArcGIS Desktop 10.7 中研究区域内的 NDVI 示例
结果
尽管本分析中 2015–2019 年的总体趋势与生长高峰期秋季巨藻的减少一致,但这些结果是初步的,应与“地面实况”数据进行比较,即通过航空影像或潜水员调查验证的巨藻林位置。数据是跨秋季月份的平均值,但有几个因素没有考虑,这需要在未来进行研究。其中一些因素列在最后。
作为参考,一个美式足球场约为 1.3 英亩或 0.005 平方公里。

图 3:研究区域按年份划分的海带覆盖面积

图 4:我在 ArcGIS 模型构建器中构建的海带评估工具
讨论
这一分析的结果喜忧参半,需要进行更彻底的分析才能得出更明确的结论。未来的研究应该考虑下面列出的假设和注释,包括探索其他指数和云遮蔽技术。总的来说,2019 年检测到的海带总量明显下降,但尽管数据可能支持可检测的海带在调查的几年中下降的结论,但这很大程度上取决于人们查看的数据部分。
尽管如此,这些结果可能是为分析而选择的年份或月份的人为产物。调查日期的广泛间隔,虽然旨在捕捉长期的可变性,但可能反而是捕捉局部气候和海洋条件变化的快照-2014 年至 2016 年是厄尔尼诺年,除 2019 年之外的所有调查年份都被认为是加州干旱年。分析更多的日期有助于理清这些区别。

图 5:地球上方的陆地卫星 8 号插图。美国地质勘探局插图
另一个限制是空间(图像像素区域)和时间(时间)分辨率。Landsat 8 图像数据每 16 天采集一次,分辨率为 30 米,这意味着每个像素代表地面上 30 米 x 30m 米的区域。正如所料,巨藻植被很难在这个分辨率下捕捉到,它可能不适合这个任务。
另一方面,2A 哨兵拥有更高分辨率的图像(高达 10 米)。Sentinel 数据于 2015 年推出,随着时间的推移和更多数据的捕获,可能会提供更好的图像来估计这些类型的变化。这两颗卫星的图像波段之间的一些差异在 这里总结为 ,然而,对于研究的区域和日期,这一数据很难获得,需要进一步探索。更高分辨率的图像可从私人机构获得,如行星实验室的 PlanetScope 卫星(高达 3m),并正在作为替代方案进行研究。

图 6:北加州的一只紫海胆。照片由加利福尼亚州布拉格堡的诺约中心提供
如前所述,除了 NDVI 之外,还有许多方法正在西海岸机构进行研究,并在3中进行了很好的总结和列表。这些包括但不限于漂浮藻类指数(FAI)、比例藻类指数(SAI)、多端元光谱混合分析(MESMA)和归一化差异藻类指数(NDAI),不在本次初步探索的范围之内。
一般来说,这些分类方法是一致的,但是依赖于基本的假设和训练数据。使用这些替代方法对海藻栖息地进行数字化可能会产生更可靠的结果。加州大学洛杉矶分校 KelpWatch 项目[4]已经开发了一个网络地图,开发一个专门针对巨藻降解的类似地图将是这个项目的未来目标。
最终地图如下图 5 所示,突出显示了从加利福尼亚州加洛韦到 Point Arena Lighthouse 的感兴趣区域的子区域,该子区域在视觉检查后,很好地跟踪了来自 KelpWatch 的 2015–2019 年第三季度数据[4]。

图 ArcGIS Desktop 10.7 中 2015–2019 年北加州高峰月期间的海藻估算
这种探索性的分析只是触及了表面(双关语😜)和下面列出的注释和假设列表,应该在未来的研究中探索。
注释和假设
下面列出了遇到的一些挑战。
1.稀疏或沉水的海带,可能难以察觉3。根据[5],当在自然水环境中观察到马尾藻(褐藻)时,与离开水相比,马尾藻的可检测性降低了约 75%。在本研究中,对海带检测进行了类似的假设。
2.其他因素也可能影响可检测的海带数量,如水柱中的海带长度、潮汐高度、浊度/水流、测深和一般水条件。
3.每年的天气异常(厄尔尼诺、大旱年、海洋热浪)也会影响结果。NOAA 的高分辨率海面温度(SST)数据集可在 此处 获得。
4.拍摄传感器图像的时间的可变性会影响所捕捉的反射光。
5.研究区域中浮游植物的存在可能会被混淆为海带。对于二月和九月之间的数据来说尤其如此。
6.需要地面真实数据来验证这项研究。历史上,这是通过空中图像或潜水员在水中的调查来完成的。
7.单个像素(30 米 x 30m 米)在这里不是“未混合的”,如果海藻在 30 米像素的 15%以下,则很难检测到。MESMA 就是这样一种技术[2]。
现在怎么办?
在这个项目中,我学到了很多东西,除了已经讨论过的科学和技术挑战,我希望在未来能看到许多事情发生,以传播海带森林处于危险之中的信息,同时增加研究人员和爱好者对海带数据的访问。
从研究的角度来看,定期修订越来越多的海带评估方法和标准,如3,可以帮助巩固和简化各研究机构监控海带的方法。GIS 软件许可可能是一个资源挑战,但幸运的是,现在存在开源软件,如 QGIS,而 R 和 Python 中的 GIS 包和库可以处理地理空间数据。我通过参加地理空间分析课程获得了 ArcGIS 许可。
探索数据的另一个挑战是大地卫星图像 TIF 文件数据的庞大。分析大量高分辨率图像会使本地机器负担过重,从而限制了没有良好计算能力和存储的个人。
从面向公众的角度来看,对于那些不住在海岸附近或不在海洋中度过时间的人来说,吸引公众关注海带始终是一个挑战。海滩清理、海藻森林集体浮潜游或海岸徒步旅行只是帮助公众参与和提高社区意识的几种方式。
继续发展和培养研究机构、非营利组织和社区之间的伙伴关系对于监测我们的海藻森林至关重要。一个已经在进行的例子是大法拉隆斯海藻恢复项目 ,这是大法拉隆斯协会、大自然保护协会和其他组织之间的联合项目,旨在保护和恢复北加州海岸线的海藻森林。

图 Point Arena 灯塔标志着研究区域的北端。照片由布鲁诺·沃尔夫在 Unsplash 上拍摄
如前所述, KelpWatch 项目[4]有一张地图,提供了过去几十年中海带数量下降的概览,是公众了解 1984 年以来海带数量下降的有用工具。这张地图可以作为向公众展示有关气候变化影响的科学数据的一个例子,并且可以应用于任何生态系统或物种。
最后,通过强调传统的“流行”主题,让学生或爱好者通过免费的在线研讨会或培训来玩空间分析工具,有助于吸引更多的公众。这可能包括鲸鱼和鲨鱼的迁徙、入侵的狮子鱼追踪、全球珊瑚产卵事件等等。对于地理空间分析,可能性是无限的!
我希望,随着技术的进步和我们今天拥有的开源工具和数据的民主化,我们将能够为我们的未来做出更好的决定!
进一步研究的参考
1海洋侦探。"海星消耗综合症现在记录在温哥华岛东北部."http://themarine detective . com/2013/12/21/sea-star-washing-syndrome-now-documented-on-ne-Vancouver-island/。
[2]丹尼斯 J.I .芬格,梅雷迪思 l .麦克弗森,亨利 f .豪斯基普,拉斐尔 m .库德拉,2021。“利用 Landsat 绘制北加利福尼亚巨藻冠层图,以便进行长期监测”。https://doi.org/10.1016/j.rse.2020.112243。
3莎拉·b·施罗德 a,科琳·杜邦,莉安娜·博耶,弗朗西斯·胡安内斯,2019。“巨藻(Nereocystis**luetkeana)被动遥感制图技术”:技术回顾与区域案例研究。https://doi.org/10.1016/j. gecco . 2019 . e 00683。**
[4] Kelpwatch 2021。https://kelp.codefornature.org/。
[5]迪尔森,H.M .,施卢斯,a .,拉塞尔,b .,2015。“利用航空遥感对大佛罗里达湾沿海水域海草和大型海藻马尾藻浮垫的高光谱识别”。遥感环境。167,247e258。https://doi.org/10.1016/j.rse.2015.01.027。
6汉密尔顿、S. L .、贝尔、T. W .、沃森、J. R .、格罗鲁德‐科尔维特、K. A .、门格、B. A. (2020 年)。“遥感:为评估气候变化的影响生成长期海藻床数据集”。生态学,e03031。https://esa journals . online library . Wiley . com/doi/ABS/10.1002/ecy . 3031。
关于作者

Nikhil Das 是一名自由职业工程师、数据分析师和作家,热爱大自然。作为一名前航空工程师,他发现在太空中仰望天空和星星时很容易感到自己很渺小。然而,最近他通过向下凝视地球,对自己的存在有了更深的欣赏,无论是在山区徒步旅行,在广阔的海洋潜水,还是现在探索鸟瞰我们星球的数字图像。他总是在寻找有趣的项目、机会或者只是交流自然故事,可以通过电子邮件联系到 nikd29@gmail.com。
探索 Neo4j 中实用的推荐系统
原文:https://towardsdatascience.com/exploring-practical-recommendation-engines-in-neo4j-ff09fe767782?source=collection_archive---------7-----------------------
利用图形数据科学和机器学习

作者图片
在本帖中,我们将探讨如何在 graph 中开始使用实用且可扩展的推荐。我们将在一个包含 1750 万点击事件和大约 75 万用户的数据集上演示一个带有新闻推荐的基本示例。我们将利用 Neo4j 和图形数据科学(GDS)库根据用户偏好快速预测类似新闻,并实现亚秒级、排序、个性化的推荐查询。
所有重现这一分析的代码以及从源数据设置图表的资源都可以在 这个 GitHub 资源库 中找到。
推荐系统是一个丰富的研究领域,我们在这里仅仅触及皮毛。如果你对任何领域感兴趣——无论是其他推荐技术、评估性能、优化,包括更多输入、如何进一步扩大规模,还是任何其他基于图表的推荐者,请在评论中留下注释。
这篇文章的结构如下:首先,我们将简要地定义推荐系统。接下来,我们将查看将要使用的源数据集和图表,以及如何查询基本的分析统计信息,以帮助我们理解图表并更好地为分析做准备。接下来,我们将讨论一种叫做协同过滤(CF)的技术,这将是我们在这篇文章中的推荐机制。之后,我们将使用 Cypher 查询语言和图形数据科学(GDS)库,利用节点嵌入和称为 K-最近邻(KNN)的 ML 技术,深入应用 CF。最后,我们将讨论后续步骤和跟进资源。
什么是推荐系统?
简而言之,推荐系统是一种信息过滤系统,它寻求为用户可能感兴趣的项目生成有意义的推荐。
在推荐系统的上下文中,“项目”是一个通用术语,可以指面向用户销售或定向的任何东西,包括在线零售店中的产品、诸如书面文章、视频和/或音乐之类的内容,或者社交媒体平台上潜在的联系或关注的人。
不言而喻,在竞争日益激烈的在线环境中,推荐系统对于提高用户满意度和加速业务增长至关重要。
今天的数据集:微软思维
在本帖中,我们将使用MIcrosoftNEWSDataset(MIND)大型数据集探索新闻推荐,该数据集是从微软新闻网站1收集的 100 万匿名用户及其点击行为的样本。它包括大约 1500 万个印象日志和大约 16 万篇英语新闻文章。
我格式化了数据集,并使用下面的模式将其加载到一个图表中:
//visualize schema in Neo4j Browser
neo4j$ CALL db.schema.visualization();

作者图片
我们看到,新闻文章被建模为节点,可以被用户CLICKED或HISTORICALLY CLICKED建模为节点。在这种情况下,CLICKED指的是从我们样本的时间间隔(大约 2019 年 11 月 9 日至 2019 年 11 月 15 日)内发生的印象记录中解析的点击动作。HISTORICALLY CLICKED指用户历史中的点击动作,发生在过去某个未知时间。
此外,新闻还有由微软注释的子类别和分类,以及微软使用 NLP 方法从新闻标题和摘要中提取的维基数据实体。我也用图中的节点和关系对它们进行了建模。我将在最后简要地谈到这些,它们对于扩展分析是有用的,但是我们不会在这篇文章中使用它们。我们在这里的主要兴趣是从用户和他们的点击事件开始,以获得推荐。
技术分析
展望未来,我们将通过数据集揭示新闻推荐的见解,并沿途显示代码片段。下面所有代码来自笔记本这里。虽然我认为代码片段有助于将这些点连接起来,但为了简洁起见,我在这里不重复笔记本中的一些配置和功能定义。如果您有兴趣完全复制 Python 中的分析,请使用笔记本作为源代码。
图形数据分析
在开始分析之前,检查图表的一些高级统计数据对更好地理解大小和连通性是有帮助的。
总计数
让我们从高节点和关系计数开始。这些函数假设 APOC 安装在您的 Neo4j DB 上。
gds **=** GraphDataScience(HOST, auth**=**(USERNAME, PASSWORD), aura_ds**=True**)*# total node counts*
gds**.**run_cypher( '''
CALL apoc.meta.stats()
YIELD labels AS nodeCounts
UNWIND keys(nodeCounts) AS label
WITH label, nodeCounts[label] AS nodeCount
WHERE label IN ['User','News']
RETURN label, nodeCount
''')

作者图片
*# total relationship counts*
gds**.**run_cypher( '''
CALL apoc.meta.stats()
YIELD relTypesCount as relationshipCounts
UNWIND keys(relationshipCounts) AS type
WITH type, relationshipCounts[type] AS relationshipCount
WHERE type IN ['CLICKED','HISTORICALLY_CLICKED']
RETURN type, relationshipCount
''')

作者图片
就推荐系统而言,这是一个相对较小的图表,只有大约 75 万用户、10 万篇新闻文章和大约 1750 万次总点击事件。这些数字小于 MIND Large 报告的总数,因为一些原始数据集被微软留作竞争评估的测试集,因此没有完整的印象信息。那部分数据被排除在这张图表之外。
单击事件分布
接下来,我们可以看看每个用户的点击量分布。检查这种分布有助于确保:
- 该图是相当好连接的,因为我们即将推出的推荐技术的质量将取决于一个相当好连接的图。
- 我们没有任何大的超节点,即具有非常高数量关系的节点。超节点的定义因用例而异。对于这一点,我会关注点击量成千上万的用户。
all_clicks_df = degree_counts('User', 'CLICKED|HISTORICALLY_CLICKED', 'OUT')recent_clicks_df **=** degree_counts('User', 'CLICKED', 'OUT')f, axs = plt.subplots(1,2,figsize=(16,5))axs[0].bar(all_clicks_df.degree[:80], all_clicks_df.degreeCount[:80], width=1)
axs[0].set_title('User Clicks Distribution')
axs[0].set_ylabel('User Count')
axs[0].set_xlabel('Number of Total Clicks')
plt.figtext(0.4, 0.5, get_percentiles(all_clicks_df).to_string())axs[1].bar(recent_clicks_df.degree[:80], recent_clicks_df.degreeCount[:80], width=1)
axs[1].set_title('User Recent Clicks Distribution')
axs[1].set_ylabel('User Count')
axs[1].set_xlabel('Number of Recent Clicks')
plt.figtext(0.83, 0.5, get_percentiles(recent_clicks_df).to_string())plt.show()

作者图片
上图显示了用户的总点击量(CLICKED和HISTORICALLY_CLICKED)以及最近点击量(仅CLICKED)的分布。我们看到这些分布有很大的左尾,表明用户之间的活动分布并不均匀——相反,有相对小部分的用户占了大量的点击。这多少有些意料之中。重要的是,每个用户至少有一次最近的点击事件,我们没有看到用户有上万次点击事件。
我们可以对下面的新闻做类似的细分。
all_clicks_df **=** degree_counts('News', 'CLICKED|HISTORICALLY_CLICKED', 'IN')recent_clicks_df **=** degree_counts('News', 'CLICKED', 'IN')f, axs = plt.subplots(1,2,figsize=(16,5))axs[0].bar(all_clicks_df.degree[:80], all_clicks_df.degreeCount[:80], width=1)
axs[0].set_title('News Total Clicks Distribution')
axs[0].set_ylabel('News Count')
axs[0].set_xlabel('Number of Total Clicks')
plt.figtext(0.4, 0.5, get_percentiles(all_clicks_df).to_string())axs[1].bar(recent_clicks_df.degree[:80], recent_clicks_df.degreeCount[:80], width=1)
axs[1].set_title('News Recent Clicks Distribution')
axs[1].set_ylabel('News Count')
axs[1].set_xlabel('Number of Recent Clicks')
plt.figtext(0.83, 0.5, get_percentiles(recent_clicks_df).to_string())plt.show()

作者图片
虽然新闻的总点击数看起来还可以,但只有一小部分新闻(< 25%)是最近被点击的,也就是说,在我们样本的时间窗口内,实际上是被用户点击的。在该时间窗口内未被点击的新闻在这种情况下可能不是推荐的好候选。我们将在下面处理这个问题。
标记最近的新闻
为了确保 CF 的推荐是相关的,我们需要筛选出一部分新闻文章来考虑。
- 新闻在最近的时候往往最相关,但随着时间的推移,可能会很快失去相关性。一般来说,好消息推荐者应该避免陈旧和/或过时的消息。在这种情况下,我们可以使用 Microsoft impressions 作为代理,并且只考虑包含在我们的样本时间窗口内的印象中的新闻文章。为了做到这一点,我们可以使用一个名为
approxTime的节点属性来反映新闻文章的最小印象时间。我在将源数据加载到 Neo4j 时计算了这个属性。长话短说,approxTime只对我们样本中至少有一个印象的新闻文章为非空。 - CF 的一个局限是只能推荐有用户反馈的内容。因此,没有用户点击的新闻文章不能用于 CF。在应用中,这有时被称为冷启动问题。它可以通过基于内容的推荐和其他混合方法来解决。我们可能会在单独的帖子中涉及这一点,但对于这个示例,我们只考虑至少有一个用户点击的新闻文章,即至少有一个
**CLICKED**或**HISTORICALLY_CLICKED**关系。
我们可以分配第二个节点标签RecentNews,以便在 Cypher 查询和 GDS 预测中轻松过滤出符合上述标准的新闻。记住 Neo4j 允许一个节点有多个标签,所以原来的News标签还是会被保留。
gds**.**run_cypher('''
MATCH(n:News)<-[:CLICKED|HISTORICALLY_CLICKED]-()
WHERE n.approxTime IS NOT NULL
SET n:RecentNews
RETURN count(DISTINCT n)
''')

我们确实看到新闻文章的数量显著减少,从 104K 减少到大约 22K。但是由于这些新闻更近,联系也更紧密,因此我们应该能够通过将焦点缩小到这些新闻文章来改进我们的推荐器。我们还可以在点击分布中看到这些改进。
all_clicks_df **=** degree_counts('RecentNews', 'CLICKED|HISTORICALLY_CLICKED', 'IN')recent_clicks_df **=** degree_counts('RecentNews', 'CLICKED', 'IN')
f, axs **=** plt**.**subplots(1,2,figsize**=**(16,5))
axs[0]**.**bar(all_clicks_df**.**degree[:80], all_clicks_df**.**degreeCount[:80], width**=**1)
axs[0]**.**set_title('Total Clicks Distribution')
axs[0]**.**set_ylabel('News Count')
axs[0]**.**set_xlabel('Number of Total Clicks')
plt**.**figtext(0.4, 0.5, get_percentiles(all_clicks_df)**.**to_string())
axs[1]**.**bar(recent_clicks_df**.**degree[:80], recent_clicks_df**.**degreeCount[:80], width**=**1)
axs[1]**.**set_title('Recent Clicks Distribution')
axs[1]**.**set_ylabel('News Count')
axs[1]**.**set_xlabel('Number of Recent Clicks')
plt**.**figtext(0.83, 0.5, get_percentiles(recent_clicks_df)**.**to_string())
plt**.**show()

作者图片
我们现在可以看到,每篇新闻文章都至少被点击过一次,至少 75%的新闻文章是最近被点击过的。
协同过滤(CF)
有许多不同类型的推荐系统。在本帖中,我们将应用一种叫做协同过滤(CF)的技术,这是一种基于相似用户的行为自动预测用户偏好的实践。

来自:https://en.wikipedia.org/wiki/Collaborative_filtering:“这张图片展示了一个使用协同过滤来预测用户评分的例子。起初,人们会对不同的项目进行评分(比如视频、图片、游戏)。在那之后,系统对用户还没有评价的项目进行预测。这些预测基于其他用户的现有评级,这些用户与活动用户具有相似的评级。例如,在我们的案例中,系统已经做出了预测,即活跃用户不会喜欢该视频。”:图片由莫沙宁,CC BY-SA 3.0<https://creativecommons.org/licenses/by-sa/3.0>,转自维基共享
基于用户和项目的 CF
粗略地说,协作过滤主要有两类:
- 基于用户,侧重于根据用户与项目的交互直接计算用户之间的相似性
- 基于项目的,其基于相关的用户活动来测量项目对之间的相似性,例如相同的用户喜欢、查看、评级或以其他方式类似地与项目交互
我们今天的方法将集中于后来的基于项目的 CF。对于许多问题域,基于项目的 CF 将比基于用户的 CF 更易于管理和扩展。这有几个原因。
首先,因为在大多数应用情况下,项目比用户少得多,所以要比较的实体较少。第二,也许更重要的是,用户偏好也是动态的,会随着时间的推移而改变。例如,在电影的例子中,用户可能对科幻电影最感兴趣,但经历了西部片的短暂停留,并且向历史上观看过相同内容的其他科幻迷推荐西部片可能不是最好的事情。通常,用户当前兴趣的最佳指标是他们最近的活动,基于项目的过滤使您能够更灵活地根据最近的项目点击、查看、评级等来找到相似性。
隐式协同过滤
您通常会在明确的用户反馈环境中听说协作过滤,例如在线商店中对电影或产品的评论。在本帖中,我们将只对新闻点击活动应用协同过滤,即 1 表示点击,意味着用户“喜欢”某个内容,否则为 0。这是“隐式协同过滤”的一种基本形式,其中用户的偏好是基于他们的活动而非显式反馈来暗示的。隐式 CF 的需求经常出现,因为许多现实世界的场景没有显式评级,或者即使有,也非常稀少。在生产设置中,您可能还想合并其他用户活动数据点,以帮助权衡图形关系,如读取/查看时间、滚动深度等。在 MIND 数据集中是不公开的。您拥有的信息越多,您就越能准确地模拟用户偏好。
CF 的基本密码查询
从这里开始,我们可以尝试使用 Cypher 来完成基本的协同过滤。例如,以下面的用户和他们点击的新闻为例。你可以看到人们对汽车、金融、美国新闻和其他几个类别的兴趣混杂在一起。
USER_ID = 'U218584'gds**.**run_cypher('''
MATCH (u1:User {userId: $userId})
-[r1:CLICKED|HISTORICALLY_CLICKED]->(n1:RecentNews)
RETURN n1.newsId AS newsId,
n1.title AS title,
n1.abstract AS abstract,
n1.category AS category,
n1.subcategory As subcategory,
r1.impressionTime AS impressionTime,
type(r1) AS clickType
ORDER BY clickType, impressionTime DESC
''', params**=**{'userId': USER_ID})

作者图片
假设我们可以通过经常点击的新闻文章来测量用户兴趣的相似性,我们可以进行三跳查询,以基于点击与U218584相同的新闻的用户的活动来为用户U218584找到潜在的推荐。
通过下面的查询,我们可以获得需要遍历以获得建议的节点总数。为了简单起见,我们先不担心HISTORICALLY CLICKED关系。
gds**.**run_cypher('''
MATCH (u1:User {userId: $userId})
-[r1:CLICKED]->(n1:RecentNews)
<-[r2:CLICKED]-(u2:User)
-[r3:CLICKED]->(n2:RecentNews)
RETURN u1.userId AS userId,
count(DISTINCT n1) AS clickedNews,
count(DISTINCT u2) AS likeUsers,
count(DISTINCT n2) AS potentialRecommendations
''', params**=**{'userId': USER_ID})

作者图片
虽然上述内容在某些情况下可以很好地工作,而且它肯定是连接 SQL 表或遍历文档存储库的一个巨大改进,但是请注意,我们得到了许多潜在的建议(几乎 11K),并且必须遍历许多用户节点(超过 63K)。这只是整个微软数据集的一个样本,我们根本没有利用HISTORICALLY CLICKED信息。
对于需要频繁查询推荐的生产用例,随着用户数量、内容量和/或观察到的参与度的增长,这种方法将难以扩展。我们需要一些其他的策略来帮助缩小结果范围。有几种不同的方法可以实现这一点,但一种健壮且可扩展的方法是使用 Neo4j 图形数据科学(GDS)库。
通过 FastRP 节点嵌入和 KNN 扩展 CF
通过 GDS,我们可以使用 FastRP 节点嵌入来降低问题的维度,然后使用一种名为K-最近邻(KNN) 的无监督机器学习技术来识别具有相似/接近嵌入的新闻并从中得出建议。因为 FastRP 嵌入是基于图结构的,所以具有相似嵌入的新闻也应该通过被相同和相似的用户点击而在图中相对连接。
图形投影
我们将从利用User和News节点的图形投影开始。在嵌入步骤的投影中包含所有的News并在 KNN 步骤中过滤掉RecentNews可能会有所帮助。User陈旧新闻上的点击活动仍然构成图结构的重要部分,并且同样可以帮助在 KNN 步骤中为更近的新闻提供相似性推断。
我们还将包括历史和最近的印象点击,但我们将给予历史点击较少的权重,以支持更近期的用户活动。最后,我们将使用一个UNDIRECTED方向,这样 FastRP 可以双向遍历图形。
*# Putting an index on a 'weight' attribute allows us to assign default values in the projection below*
gds**.**run_cypher('''
CREATE INDEX weight_index IF NOT EXISTS FOR ()-[r:CLICKED]-()
ON (r.weight)
''')# Projecting the graph
g0, res = gds.graph.project('embedding-projection', ['User', 'News'], {
'CLICKED':{
'orientation':'UNDIRECTED',
'properties': {
'weight': {
'property': 'confidence',
'defaultValue': 1.0
}
}
},
'HISTORICALLY_CLICKED':{
'orientation':'UNDIRECTED',
'properties': {
'weight': {
'property': 'confidence',
'defaultValue': 0.1
}
}
}
})
res

FastRP
当运行 FastRP 时,我们将确保包含关系权重属性。
gds.fastRP.mutate(g0, mutateProperty='embedding',
embeddingDimension=256, randomSeed=7474,
relationshipWeightProperty='weight');

虽然我们应该能够在一个投影中完成所有这些工作,但取决于您使用的 GDS 版本,我发现最简单的方法是将嵌入内容写回数据库,并为 KNN 创建一个单独的投影。
gds.graph.writeNodeProperties(g0, ["embedding"], ["News"])

如果你对这些嵌入看起来像什么感到好奇,它们只是浮点数的向量。在这种情况下,它们的长度为 256 个数字,如上面的embeddingDimension参数所示。
gds.run_cypher('''
MATCH(n:RecentNews)
RETURN n.newsId, n.embedding LIMIT 3
''')

k 近邻(KNN)
我们现在可以运行 KNN 来估计最近的新闻文章之间的相似性关系,并将它们写回到图表中。
#graph projection for knn
g1, res = gds.graph.project('cf-projection',
{'RecentNews':{'properties':['embedding']}},'*')
res

#run knn
knn_stats_df = gds.knn.write(g1, nodeProperties=['embedding'],
writeRelationshipType='USERS_ALSO_LIKED',
writeProperty='score',
sampleRate=1.0,
maxIterations=1000);
下面我们可以看到与算法收敛、计算和写入时间、比较的节点数量、考虑的节点对以及写入的关系相关的统计数据。KNN 实际上在 GDS 得到了很好的优化,并使用复杂的采样和并行化技术来提高计算效率和高度可伸缩性。如有兴趣,请参见文档了解更多详情。
knn_stats_df[['didConverge',
'ranIterations',
'computeMillis',
'writeMillis',
'nodesCompared',
'nodePairsConsidered',
'relationshipsWritten']]

仅当在节点对之间发现正相似性时,才写入 KNN 关系,在这种情况下,正相似性基于每个节点的nodeWeightProperty值之间的余弦相似性。在这里,我们使用在CLICKED和HISTORICALLY CLICKED关系上计算的 FastRP 嵌入作为nodeWeightProperty。我们可以在下面看到这些相似性分数的分布。
knn_stats_df.similarityDistribution

这是一个有助于检查的分布。在这种情况下,它看起来相当健康。如果你看到很多 0 进入更高的百分位数,这是一个信号,KNN 无法找到许多相似之处。百分位数之间的分数缺乏差异也表明节点权重属性缺乏差异。
CF 与 KNN 的关系
现在我们可以为用户U218584构建一个协同过滤查询,但是使用:
- 更精确的结果,
- 使用较少的遍历步骤,以及
- 根据 KNN 的评分,我们可以根据总体相似度对结果进行排序。
gds.run_cypher( '''
MATCH(u:User {userId: $userId})-[:CLICKED]->(n:RecentNews)
WITH collect(id(n)) AS clickedNewsIds//get similar News according to KNN and exclude previously clicked news
MATCH (clickedNews)-[s:USERS_ALSO_LIKED]->(similarNews:News)
WHERE id(clickedNews) IN clickedNewsIds AND NOT id(similarNews) IN clickedNewsIds//aggregate and return ranked results
RETURN DISTINCT similarNews.newsId as newsId,
similarNews.title AS title,
similarNews.category AS category,
similarNews.subcategory As subcategory,
sum(s.score) AS totalScore ORDER BY totalScore DESC
''', params={'userId': USER_ID})

作者图片
我们从之前的近 11K 的纯 Cypher 中削减到 59 个结果。
基于最新观看内容的推荐
使用这种基于条目的 CF 和 graph 的一个好处是,很容易改变推荐的新闻文章的范围。
例如,如果用户兴趣随时间动态变化,使用整个用户历史来生成推荐可能并不理想。在内容推荐的某些情况下,当前或最近观看的内容可能是下一步推荐什么的最佳信号。如果我们想以这种方式集中我们的推荐,我们可以很容易地调整我们的 Cypher 查询来做到这一点。
我们可以为用户抓取最新点击的新闻文章,如下所示:
gds.run_cypher('''
MATCH (u:User {userId:$userId})-[r:CLICKED]->(:RecentNews)
WITH u, max(r.impressionTime) AS maxImpressionTime
MATCH (u)-[r:CLICKED]->(n:RecentNews)
WHERE r.impressionTime = maxImpressionTime
RETURN n.newsId as newsId,
n.title AS title,
n.category AS category,
n.subcategory As subcategory,
r.impressionTime AS impressionTime
''', params={'userId': USER_ID})

作者图片
并将逻辑添加到我们的单个 CF 查询中,以获得基于最后点击的文章的推荐。
gds.run_cypher('''
MATCH (u:User {userId:$userId})-[r:CLICKED]->(:RecentNews)
WITH u, max(r.impressionTime) AS maxImpressionTime
MATCH (u)-[r:CLICKED]->(n:RecentNews)
WHERE r.impressionTime = maxImpressionTime
WITH n
MATCH(n)-[s:USERS_ALSO_LIKED]->(similarNews:News)
RETURN DISTINCT similarNews.newsId as newsId,
similarNews.title AS title,
similarNews.abstract AS abstract,
similarNews.category AS category,
similarNews.subcategory As subcategory,
sum(s.score) AS totalScore
ORDER BY totalScore DESC
''', params = {'userId': USER_ID})

作者图片
请注意,这些更狭隘地集中于汽车,尤其是福特 GT500。
CF 推荐并不总是与文章中的类别、子类别和实体保持一致,因为我们没有直接利用这些属性——至少现在还没有。相反,这种 CF 是基于相关的用户兴趣,如果你进一步研究这个数据集,你会发现一些类别/子类别比其他类别/子类别在用户兴趣上更相关。
下一步是什么?
在这篇文章中,我们探讨了如何使用 Neo4j 和 GDS,通过强大的图表分析来快速开发和扩展推荐。
这里有一个 GitHub 存储库,应该有重现我们刚刚走过的例子所需的一切,包括一个用于此分析的笔记本和另一个用于检索、格式化和加载源数据到 Neo4j 的笔记本。
如果你是 Neo4j 的新手,我推荐在这个例子中使用 Neo4j 桌面和 GDS 。为了兼容,请使用 2.0 版或更高版本。
正如在引言中提到的,在这篇文章中,我们仅仅触及了基于图的推荐系统的表面。仅在 CF 中,我们就有许多不同的方法来扩展我们的分析,以及我们可以优化它以提高预测性能的不同方法,例如这里的所示的fast RP 优化。我们甚至没有提到基于内容的过滤,以及其他混合推荐系统,它们对于提高新内容或用户反馈稀少的内容的性能和推荐特别有帮助。除了其他新闻文章属性之外,还可以使用 category、subcategory 和 wikiData 节点在当前图表中探索这一点。如何正确评价推荐系统也是一个值得在图的背景下研究的重要课题。如果你有兴趣更详细地探索这些或其他图表推荐相关的主题,请在评论中告诉我。
在此之前,我希望这个推荐系统的图表演示是有帮助的!
感谢 CJ Sullivan 和Angela Zimmerman帮助审阅这篇博文的内容。
1:吴、应巧、陈俊红、吴楚汉、、连建勋、刘丹阳、谢兴、高剑锋、吴婉妮和。MIND:用于新闻推荐的大规模数据集。ACL 2020。
从统计包中探索 Python 数学统计函数
原文:https://towardsdatascience.com/exploring-python-mathematical-statistical-functions-from-the-statistics-package-6dafeabf24fb?source=collection_archive---------17-----------------------
你知道 Python 数学统计函数吗?

由 Pakata Goh 在 Unsplash 上拍摄的照片
如果您喜欢我的内容,并希望获得更多关于数据或数据科学家日常生活的深入知识,请考虑在此订阅我的简讯。
Python 是目前许多数据专业人员普遍使用的编程语言。许多人不得不通过 Python 开始他们的编程语言教育来建立职业生涯,这成为在线课程的一个普遍趋势。这就是为什么数据专业人员的未来显然会与 Python 语言联系在一起,而且学习 Python 提供的一切也没有坏处。
当你安装 Python 语言时,一开始可用的内置包之一就是统计包——一个数理统计数值计算的包。该软件包用于简单计算,无需安装额外的软件包。
那么,这个包能做什么呢?让我们开始吧。
1.集中趋势测量
在统计学中,集中趋势的度量是用来度量数据集的中心点的概念。大多数人已经通过他们常用的方法知道了这个概念;均值、中值、和模式。
在统计软件包中,它们提供了一些功能来测量典型的集中趋势测量值。我会使用类别逐一探索它们,使探索更容易。
的意思是
有四个函数可用于测量平均集中趋势;mean、fmean、geometric_mean和harmonic_mean。在我解释这些函数的作用之前,让我们先尝试使用这些函数。
from statistics import mean, fmean, geometric_mean, harmonic_meannumbers = [1,2,3,4,5,6,7,8,9]print('Mean:', mean(numbers))
print('Fmean:',fmean(numbers))
print('Geometric Mean:', geometric_mean(numbers))
print('Harmonic Mean:',harmonic_mean(numbers))

作者图片
首先,趋势的常见度量**mean**是一个函数,通过将数据中的所有数字相加,然后除以数据点的数量来获取数据集的平均值。**fmean**与mean类似,但速度更快,数据类型被转换为浮点型。
函数**geometric_mean**是获取几何平均值的函数,与mean不同。几何平均值的计算方法是将数据集中的数字相乘并取 n 次方根。如果我们以上面的例子为例,它将是:
(1 ×2 × 3 × 4 × 5 × 6 × 7 × 8 × 9)的 9 次方根
另一个均值函数是**harmonic_mean**,用于获取数据集的调和均值。调和平均值通过倒数平均值的倒数计算,即数据点数除以倒数。使用上面的例子,它将是:
9/(1/1 + 1/2 + 1/3 +… +1/9)
中位数
中位数是数据集中的中间等级,不同于平均值,后者不需要位置信息;Median 需要在产生结果之前对数据进行排序。关于中位数,统计包中有四个函数,分别是median、median_low、median_high和median_grouped。让我们用样本数据来试试这些函数。
from statistics import median, median_low, median_high, median_groupednumbers = [1,2,3,4,5,6,7,8,9,10]print('Median:', median(numbers))
print('Median Low:',median_low(numbers))
print('Median High:', median_high(numbers))
print('Median Grouped:',median_grouped(numbers))

作者图片
以上函数是中值函数,结果不同。首先,让我们讨论一下**median** —一个函数,用于获取数据集 50%位置的中位数或数据。从上面的结果,你可以看到结果是 5.5。这是因为我们的样本数据中有两个中间值— 5 和 6(甚至数据点也包含两个中间值)。然后,该函数将从这两个数据点中取平均值,得到 5.5。
函数**median_low**和**median_high** 分别返回中值的最小值和中值的最大值。在上面的示例中,median_low返回值 5,median_high返回值 6。
至于**median_grouped** 它会取根据区间分组的数据集中的值的中值。我可以给你看一个相关的例子。
numbers = [1,1,2,2,3]print('Median Grouped Interval 1:',median_grouped(numbers,1))
print('Median Grouped Interval 2:',median_grouped(numbers,2))
print('Median Grouped Interval 3:',median_grouped(numbers,3))
print('Median Grouped Interval 4:',median_grouped(numbers,4))

作者图片
从上面的结果可以看出,不同的间隔会产生不同的结果。如果我们正在使用median函数,它将产生结果 2;但是,使用**median_grouped**功能,会导致不同的结果。分组的中值将取决于间隔内分组的数据点之间的间隔,并且中值结果将通过中值间隔内的插值来计算。
说到中位数,我们还可以提到统计学中的quantiles函数,因为它是相关的(两种方法都涉及数据点排名)。quantile函数将根据数据分割概率返回数据切割点。让我们使用示例数据。
from statistics import quantilesnumbers = [1,2,3,4,5,6,7,8,9]
print('Quantiles:', quantiles(numbers, n=4))

作者图片
从上面的结果中,我们可以看到有 3 个数据分界点:2.5、5.0 和 7.5。当我们把数据集分成 4 部分(n = 4)时,这个数据点就是数据集被分割的点。如果我们改变 n 参数,数据切割点也将改变(结果数据切割点将是 n-1)。
模式
模式是对数据集中最频繁出现的数据的度量。在统计软件包中,这些测量值下有两个函数— mode和multimode。这些功能有什么区别?让我们用一个样本数据集来尝试一下。
from statistics import mode, multimodenumbers = [1,2,3,4,5,6,7,8,9]
print('Mode:', mode(numbers))
print('Multi Mode:', multimode(numbers))

作者图片
正如我们在上面的结果中看到的,**mode**将返回第一个出现最频繁的数据,而**multimode**将返回所有最频繁数据的列表。
2。扩散措施
如果集中趋势的测量值计算数据集的中心点,则扩散概念的测量值将测量与中心点的偏差。偏差将取决于如何传播数据和数据的规模。
只有两个函数用于测量分布计算-统计包中的方差和标准差。然而,函数也按数据集划分,无论是总体数据还是样本数据。这意味着我们有四种函数:总体标准差和方差(**pstdev**和**pvariance**)和样本标准差和方差(**stdev**和**variance**))。
让我们退一步理解什么是方差和标准差。方差是数据集的可变性度量,用于了解我们的数据分布有多大。人口方差方程如下图所示。

人口差异(图片来自作者)
样本方差如下图所示。

方差的计算基于每个数据点( Xi )减去平均值( x̄ )并除以数据点数(对于总体)或数据点数减 1(对于样本)。为什么样本减一?因为这是一个样本数据点,我们有一个比总体更大的传播不确定性。
如果我们有一个小的方差,这表明我们的数据接近平均值,反之亦然。
标准差怎么样?好吧,让我们先来看看人口和样本的等式。

人口标准差(图片由作者提供)

样本标准差(图片由作者提供)
从上图可以看出,标准差方程就是方差的平方根。在统计学中,统计学家喜欢用方程的平方根,因为可解释性更容易。差异很难解释,因为结果不是直观的;然而,标准差会给我们一个数字来衡量价差。通俗地说,标准差就是平均值的正负( x̄ 𝑠 )。
让我们试试这个函数,让我们更好地理解这些概念。
from statistics import pvariance, pstdev, variance, stdevnumbers = [1,2,3,4,5,6,7,8,9]
print('Population Variance:', pvariance(numbers))
print('Population Standard Deviation:', pstdev(numbers))
print('Sample Variance:', variance(numbers))
print('Sample Standard Deviation:', stdev(numbers))

作者图片
我们上面的实验表明,总体和样本函数有不同的结果(由于不确定性,样本更大)。你的结果可以解释为我上面解释过的。
结论
作为现代数据专家,我们会在日常工作中使用编程语言,而当前的趋势是使用 Python 语言。我觉得每个人都需要了解的一个软件包是统计软件包,因为它是基础知识,易于用于复杂的程序。
在统计包中,包内有几个函数:
1。集中趋势的度量:
- 均值(
mean、fmean、geometric_mean、harmonic_mean) - 中位数(
median、median_low、median_high、median_grouped) - 模式(
mode、multimode) - 分位数(
quantiles)
2.扩散度:
- 方差(
pvariance和variance) - 标准偏差(
psttdev和stdev)
希望有帮助!
在我的 LinkedIn 或 Twitter 上访问我。
如果您没有订阅为中等会员,请考虑通过我的推荐订阅。
探索 Reddit WallStreetBets 帖子数据
原文:https://towardsdatascience.com/exploring-reddit-wallstreetbets-posts-data-bed613386029?source=collection_archive---------35-----------------------
EDA 与 Reddit 数据的情感分析

照片由energepic.com在像素上拍摄
Reddit wall street Bets Posts是 Kaggle 网站上的一个数据集,包含华尔街 bet 的信息。WallStreetBets 是一个用于讨论股票和期权交易的子网站。WallStreetBets 因其在 GameStop 卖空交易中的角色而闻名,该交易导致美国公司空头头寸损失 700 亿美元。在这篇文章中,我们将探索 python 中的 Reddit WallStreetBets 帖子。按照 Reddit 关于 API 使用的规则,使用 python Reddit API 包装器(PRAW)收集数据。数据可以在这里找到。
我们开始吧!
首先,让我们将数据读入熊猫数据框:
import pandas as pd
df = pd.read_csv('reddit_wsb.csv')
接下来,我们将打印该数据中可用的列:
print(list(df.columns))

现在,让我们放宽行数和列数的显示限制:
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
接下来,让我们使用' head()'方法打印前五行数据:
print(df.head())

我们可以根据标题是否包含 GameStop ticker (GME)来标记我们的数据:
import numpy as np
df['GME_title'] = np.where(df['title'].str.contains('GME'), 1, 0)
print(df[['title','GME_title']].head())

我们还可以基于文本正文创建一个列:
df['GME_body'] = np.where(df['body'].str.contains('GME'), 1, 0)
print(df[['title','GME_body']].head())

接下来,让我们继续处理文本标题。让我们定义一个将数据框、列名和限制作为输入的函数。当被调用时,它打印分类值的字典以及它们出现的频率。让我们看看‘GME _ 标题’中的分布:
def return_counter(data_frame, column_name):
from collections import Counter print(dict(Counter(data_frame[column_name].values)))print(return_counter(df, 'GME_title')

让我们定义一个名为“ticker”的列,其值为“GME ”,其中“GME 标题”为 1,而“其他”,其中“GME 标题”为 0:
df['ticker'] = np.where(df['GME_title'] ==1, 'GME', 'Other')
我们还可以过滤我们的数据,只包括在文本标题中包含 GME 的帖子:
df_GME = df[df['GME_title']==1]
print(df_GME.head())

接下来,我们将使用箱线图来显示基于最小值、最大值、中值、第一个四分位数和第三个四分位数的数值分布。如果您对它们不熟悉,可以看看文章了解 Boxplots 。
与汇总统计函数类似,此函数采用数据框、分类列和数值列,并根据限制显示最常见类别的箱线图:
def get_boxplot_of_categories(data_frame, categorical_column, numerical_column):
import seaborn as sns
from collections import Counter
keys = []
for i in dict(Counter(df[categorical_column].values)):
keys.append(i)
print(keys)
df_new = df[df[categorical_column].isin(keys)]
sns.set()
sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column])
让我们为“ticker”类别中的“score”生成箱线图:
get_boxplot_of_categories(df, 'ticker', 'score')

最后,让我们定义一个函数,它将数据框和数字列作为输入,并显示一个直方图:
def get_histogram(data_frame, numerical_column):
df_new = data_frame
df_new[numerical_column].hist(bins=100)
让我们用数据框调用函数,并从“分数”生成一个直方图:
get_histogram(df, 'score')

我们也可以得到每个帖子的情感得分。为此,我们需要导入一个名为 textblob 的 python 包。textblob 的文档可以在这里找到。要安装 textblob,请打开命令行并键入:
pip install textblob
下一步导入 textblob:
from textblob import TextBlob
我们将使用极性分数作为衡量积极或消极情绪的标准。极性分数是一个浮点数,其值从-1 到+1。
让我们定义一个函数,它将从文本标题中生成一列情感:
def get_sentiment():
df['sentiment'] = df['title'].apply(lambda title: TextBlob(title).sentiment.polarity)
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Posts", len(df_pos))
print("Number of Negative Posts", len(df_neg))
如果我们调用这个函数,我们会得到:
get_sentiment()

我们可以将其应用于 GME 滤波器数据帧。让我们修改我们的函数,使其以一个数据帧作为输入。让我们返回新的数据帧:
def get_sentiment(df):
df['sentiment'] = df['title'].apply(lambda title: TextBlob(title).sentiment.polarity)
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Posts", len(df_pos))
print("Number of Negative Posts", len(df_neg))
return df df = get_sentiment(df_GME)print(df.head())


最后,让我们修改我们的函数,这样我们就可以用 seaborn 和 matplotlib 可视化情感得分:
import matplotlib.pyplot as plt
import seaborn as sns
def get_sentiment(df):
df['sentiment'] = df['title'].apply(lambda title: TextBlob(title).sentiment.polarity)
df_pos = df[df['sentiment'] > 0.0]
df_neg = df[df['sentiment'] < 0.0]
print("Number of Positive Posts", len(df_pos))
print("Number of Negative Posts", len(df_neg))
sns.set()
labels = ['Postive', 'Negative']
heights = [len(df_pos), len(df_neg)]
plt.bar(labels, heights, color = 'navy')
plt.title('GME Posts Sentiment')
return df
df = get_sentiment(df_GME)

我就讲到这里,但请随意摆弄数据和代码。
结论
概括地说,我们讨论了几种分析Reddit wallstreet bets post数据集的方法。这包括定义用于生成分类值计数的函数、用于使用箱线图和直方图可视化数据的函数以及用于从帖子文本标题生成情感得分的函数。了解分类值中的分布,就像我们生成的自动收报机标签一样,可以洞察数据集在类别/标签方面的平衡程度。这对于开发用于分类的机器学习模型非常有用。此外,箱线图和分布图揭示了数值列的值的分布,并提供了对异常值存在的洞察。最后,情感评分有助于理解文本中是否表达了积极或消极的情感。这类数据可用于预测股票价格变化的方向等任务。我希望你觉得这篇文章很有趣。该帖子的代码可在 GitHub 上找到。谢谢你的阅读!
探索印度次大陆周围植被覆盖的季节变化
原文:https://towardsdatascience.com/exploring-seasonal-variations-in-vegetation-cover-around-indian-subcontinent-dd9885edbf4?source=collection_archive---------41-----------------------
如何使用 Python 中的免费和开源工具创建植被季节变化的动画 GIF

植被覆盖的季节变化:作者图片
1.介绍
植被在维持水、氧、碳、氮等各种生物地球化学循环的平衡中起着至关重要的作用。它强烈影响土壤特性,如土壤体积、化学性质和质地,并防止风和水对土壤的侵蚀。它是不可或缺的野生动物栖息地,也是地球上众多动物物种的能量来源。在这篇博文中,我将展示我们如何使用 Python 中的免费开源工具,只用几行代码就能直观地感受到印度次大陆周围植被覆盖的季节变化。
2.工作流程
- 导入库并初始化 Google 地球引擎(GEE)
- 定义感兴趣的区域
- 定义包含将用于动画帧的感兴趣区域的区域边界
- 访问 MODIS Terra 植被 16 天指数。通过取中间值来减少同一个月的图像。
- 将缩小的每月图像绘制成动画 GIF 的帧。
我们将从 python 设置和 GEE 初始化开始。
import geemap
import eetry:
ee.Initialize()
except:
ee.Authenticate()
ee.Initialize()
3.数据处理
正如工作流中所讨论的,我们现在定义分析和可视化的区域。我们将使用联合国粮食及农业组织(FAO)的FeatureCollection,其中包含各国国际边界的 GIS 信息。
# We use the country boundaries from Global Administrative Unit Layers (2015) provided by FAO(UN)
india = ee.FeatureCollection("FAO/GAUL/2015/level0") \
.filter(ee.Filter.inList('ADM0_NAME', ['India', 'Jammu and Kashmir','Arunachal Pradesh',
'China/India','Aksai Chin']));# Define the regional bounds for animation frames.
region = india.geometry().bounds();
在我们继续下一步之前,让我们进一步了解一下 MODIS 产品。MODIS 代表中分辨率成像光谱仪。它由两个任务下的卫星组成——Aqua 和 Terra,并通过许多光谱带提供有关海洋、陆地和大气的丰富信息。这些卫星在 1-2 天内绕地球一周。MODIS 图像的粗略分辨率在 250 米到 1000 米之间。它们可用于通过植被指数(如归一化差异植被指数(NDVI)、增强植被指数(EVI))和水分指数(如归一化差异水分指数(NDWI)、蒸散量(ET))了解植被的健康状况。MODIS 通过测量海洋表面温度、海洋颜色和透明度来监测海洋。它还可用于监测森林火灾、土地利用变化、地表温度等。在本文中,我们将使用 16 天一次的 MODIS 植被指数。这些指数是从日常反射率数据中检索的,这些数据经过合成处理以去除低质量像素,因为大部分日常 MODIS 图像经常充满云,从而妨碍了其可用性。
有了这个背景,让我们从停止的地方继续。我们过滤了 2020 日历年的 MODIS 图像集合。我们将通过取中间值对同一个月的图像进行分组。这将确保我们每个月有一个图像,全年有 12 个图像。
# We use MODIS Terra Vegetation Indices 16-Day Global 1km distributed by NASA's Land Processes Distributed Active Archive Center (LP DAAC)col = ee.ImageCollection('MODIS/006/MOD13A2').select('NDVI')\
.filterDate('2020-01-01','2020-12-31');months = ee.List.sequence(1, 12);def groupbyMonth(m):
img = col.filter(ee.Filter.calendarRange(m,m, 'month')).median()
return img;colByMonth = ee.ImageCollection.fromImages(months.map(groupbyMonth))
使用col.size().getInfo()和colByMonth.size().getInfo()可以检查到,原始采集和每月采集中分别有 23 和 12 幅图像。
4.测绘
下一步是可视化这 12 张图片,它们是colByMonth图片集的一部分。我们使用 GEE 目录中提供的相同调色板来设置可视化 NDVI 栅格的参数。作为参数提供的调色板在最小和最大 NDVI 值之间线性插值颜色。
# We use the NDVI visualisation parameters from GEE catalogue
gifParams = {
'region': region,
'dimensions': 600,
'crs': 'EPSG:3857',
'framesPerSecond': 1,
'min': 0.0,
'max': 9000.0,
'palette': ['FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
'66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
'012E01', '011D01', '011301'],
};gif_fname = 'C:\\Users\\ADMIN\\Desktop\\satellite\\geemap\\ndvi.gif'geemap.download_ee_video(colByMonth, gifParams, gif_fname)
一旦动画 GIF 准备就绪,我们将在 GIF 的每一帧上添加月份信息。这可以使用geemap包的add_text_to_gif功能轻松完成。
# Add the months information on each frame of GIF
text = ['Jan-20','Feb-20','Mar-20','Apr-20','May-20','Jun-20','Jul-20','Aug-20','Sep-20','Oct-20','Nov-20','Dec-20']out_gif = 'C:\\Users\\ADMIN\\Desktop\\satellite\\geemap\\ndvi_out.gif'geemap.add_text_to_gif(gif_fname, out_gif, xy = ('40%', '0%'), text_sequence = text, font_size=30, font_color = '#000000',duration = 1000)
您可以使用geemap包的show_image功能在 Jupyter Notebook 中可视化带注释的 GIF。
geemap.show_image(out_gif)
我希望您喜欢阅读这篇文章,并在您的地理空间项目中使用它。请在下面的评论部分分享您对 MODIS 产品其他有趣使用案例的想法。
免责声明:博客中表达的观点是个人观点。
利用 R 探索美国警察枪击事件的重要因素
原文:https://towardsdatascience.com/exploring-significant-factors-in-u-s-police-shootings-utilizing-r-50f048d5df61?source=collection_archive---------41-----------------------
以下报告是我在杜克大学量化管理硕士项目中的数据科学课程的个人项目。
附带的 R 代码可在 查看 https://github . com/malikamohan 01/significant-police-factors-R/blob/main/code appendix。
在过去的几个月里,自 2020 年 5 月一名警察杀害乔治·弗洛伊德https://www.nytimes.com/article/george-floyd.html以来,我们已经看到一场重大的种族正义清算正在全国各地发生。这一事件引发了人们对在警察手中失去的类似生命的关注,导致人们呼吁重新评估警察的作用和权力。活动家团体、名人、政治家和普通公民都签署了请愿书,联系了当地的政策制定者,并开始了趋势运动(#defundthepolice、#acab 等)。)在网上呼吁改善对警察的培训和潜在的减资。
为了让利益相关方(如行动主义团体和当地政策制定者)以最快、最有效的方式做出最大的改变,以响应这些呼吁,以下数据分析和报告力求回答几个问题。
首先,这些事件在哪里发生得最多?这将有助于像黑人的命也是命和变革之色这样的激进组织集中他们的努力,而不是在美国范围内模糊/广泛地呼吁解除资助。其次,事件高发区的重要因素是什么?最后,我们能否预测哪些因素可能导致事故发生的可能性更高?后两个问题将有助于武装政策制定者通过理解最能解释和预测警察枪击事件的因素来最好地响应活动家的变革呼吁,并为更强有力的政策和培训提供信息。
数据理解
为了回答提出的问题,使用了两个数据集。首先,一个数据集是从 Kaggle 获得的,它记录了过去五年中美国发生的枪击事件。共有 4851 项记录,包括以下变量:日期、死亡方式(受害者死亡方式)、年龄(受害者年龄)、性别(男/女)、种族、城市、州、精神疾病迹象(如果受害者出现精神疾病迹象,则为真/假)、威胁级别(攻击/其他)、逃离(如果受害者是否逃离现场)和人体照相机(如果警官在事件发生时佩戴人体照相机,则为真/假)。该数据集的一个限制可能是,如果没有收集到一组完整的数据,或者如果创建该数据集的研究人员在从司法局网站收集数据时遗漏了一些东西。
第二个数据集是通过将每个州的事件计数与州级政府数据相结合而获得的,这些数据包括州、事件计数、Pop 大小、每个 Pop 的事件百分比、白人百分比(非西班牙裔白人占州人口的百分比)、贫困率、HH 收入中位数、2020 年政党倾向(民主党/共和党)、人均警察支出和年龄中位数。这两个数据集被合并成一个综合数据集,描述具体的警察枪击事件和关于这些事件发生所在州的解释性数据。
数据准备和探索
数据清理:数据清理的过程包括标准化日期格式,重命名包含奇怪字符的列(例如,state 最初是..状态),修复数据中的拼写错误和不一致(例如,年龄列中的所有“37”在数字后都有额外的小数,而其他年龄没有),并检查列中是否没有空值。下一步是数据集之间的数据合并,使用 merge 函数将它们在州代码上连接起来。
数据探索:数据探索过程从对变量的一维分析开始,以便在开始创建直方图之前更好地理解它们的分布。关于这些事件的一些关键见解包括:大多数警察枪击事件涉及没有随身相机的警官,通常没有精神疾病的迹象,这令人惊讶,因为这是一些警察培训建议通常围绕的问题,受害者的性别绝大多数是男性,年龄分布偏左,平均值为 36.5(图 1)。
图一:

接下来,除了其余变量的直方图,对于二维分析,基于状态的计数图有助于更好地理解这些变量和应该关注的状态。我们最初看到加利福尼亚和得克萨斯等州的事件数较高,而中西部各州的事件数较低(图 2a),但是,在我们看到人口规模图(图 2b)中的类似模式后,这表明我们应该评估每个人口的事件百分比,以便将其标准化。这样做之后,我们看到枪击案发生频率最高的州是:阿拉斯加州、新墨西哥州、俄克拉荷马州、亚利桑那州、科罗拉多州、内华达州、蒙大拿州、西弗吉尼亚州、阿肯色州和怀俄明州(图 2c)。从地图中可以获得更多的信息,包括州内白人的百分比,白人人口最多的州包括路易斯安那州、犹他州、西弗吉尼亚州、内华达州和北卡罗来纳州,州贫困率(贫困率最高的州包括密西西比州、新墨西哥州、路易斯安那州、西弗吉尼亚州,最低的州包括新罕布什尔州、马里兰州和犹他州),以及人均警察支出(图 2d)。
2a:

2b:

2c:

2d:

人均警察支出很重要,我们可以看到,人均警察支出最高的州包括 DC、纽约州、阿拉斯加州、马里兰州、罗德岛州和内华达州,最低的州包括缅因州、印第安纳州和肯塔基州。平均而言,各州用于警察预算的人均支出为 311 美元(图 3)。在进入建模步骤之前,需要注意并牢记的一件重要事情是,人均警察支出和事件数不是正态分布的(图 4),因此需要对其进行转换。
图 3 &图 4:
****
接下来,通过相关矩阵、箱线图、散点图、独立性卡方检验和相互作用图,探究这两种类型数据(状态和事故相关数据)之间的相互作用和相互关系。数字变量中的相关矩阵(图 5)显示,事件最强的相关性是与人口规模(0.89)、贫困率(0.21)和人均警察支出(0.08)的正相关性,以及与 HH 收入中位数(-0.08)、年龄中位数(-0.17)和白人百分比(-0.26)的负相关性。
图 5:

由于相关性本身并不是一个足够强的相关性指标,为了评估人群中变量之间是否存在相关性,进行了下一步的卡方检验。结果向我们显示了统计上显著的关系(p 值< 0.05) included the relationship between incidents and race, age, signs of mental illness, flee, body camera, state pct white, state poverty rate, state party leaning and police spending per capita, positioning them as potential variables for our model. The boxplots below help us understand these relationships, like for example we can see there’s the highest count of Black and Hispanic victims and a higher count in Republican states (Figure 6a). We also visualized the relationship between pct white/count as negative, poverty rate/count as positive, and police spending/count as positive when seeing the scatter plot.
图 6a & 6b:
****
基于相关矩阵和卡方检验,还考虑了交互效应(当一个回归变量依赖于另一个回归变量时),并使用 R 的 interact_plot 函数绘制了交互效应图,我们可以看到,在一个州的贫困率/中等家庭收入、中等家庭收入/警察支出和每个人口/贫困率的百分比之间存在轻微的交互效应(图 7)。
图七:



数据转换:正如我们之前看到的,警察枪击事件的目标变量不是正态分布的,因此在进入分析的建模步骤之前,有必要评估何种转换可以使其正常化。这是通过 boxcox 完成的,它计算最佳功率转换。这给了我们 optimal_lambda 值 0.58 ,这表明平方根将是使用的最佳转换(图 8)。

数据建模
模型 1:第一个模型是使用相关性最高的变量创建的,这些变量在卡方检验中也具有统计学意义。这些是贫困率、人均警察支出、hh 收入中位数、pct 白人、种族、年龄、人体照相机和党派倾向,它们被用作线性回归 lm 模型中 sqrt(事件)目标变量的回归变量。为了测试模型,还使用 R 中的 traincontrol 和“cv”函数对模型进行了交叉验证。我们看到几乎所有的变量都是重要的,除了身体摄像机,所以为了得到以下结果,排除了:

模型 2: 第二个模型是 lasso 模型,这是一种回归分析方法,它比较变量的所有可能选择,将其标准化,并进行变量选择和正则化,以使预测精度更好。这是通过在 R 中使用 cv.glmnet 函数并计算 lambda 系数来完成的,结果如下。我们看到变量比模型 1 少。

模型 3: 接下来,我们使用了一种与 lasso 类似的岭回归,但与不使用 lasso 的岭相比,它使系数为 0(如果有大量参数,效果最好),并且有助于具有多重共线性的数据,正如我们通过交互效应看到的那样,它在这里可能适用,通过向回归估计值添加一定程度的偏差,并再次执行交叉验证。这产生了以下结果:

模型 4: 最后创建的模型是一个随机森林模型,它使用 500 棵回归树,在不同的随机选择样本/数据子集上随机选择不同的 3 个变量,以便对其进行训练和测试,并对所有树的输出进行平均,以输出预测的连续值。该模型通过显示包含这些特征的分割(不同的树)数量的总和,输出以下重要的预测值。我们在下面看到这些变量和它们的节点纯度,向我们表明,随着值的范围越大,杂质就越高(图 9)。**

模型评估和部署
为了评估哪个模型对预测分析最有用,可以在它们之间进行比较的度量标准是 R 平方(代表回归模型中变量解释的因变量的方差比例,因此越高越好)、RMSE(残差/预测误差的标准偏差,因此越低越好)和 MAE(目标变量和预测变量之间的绝对差之和,因此越低越好)。此外,考虑的要点是模型 1 包括 7 个预测器,2 包括 9 个预测器,3 包括 11 个预测器。图中显示了四个模型的这些指标的比较(图 10)。

基于这一比较,我们可以看到随机森林(模型 4)具有最高的 r 平方,但是由于它明显高于其他森林,这可能是一个值得关注的问题,也是潜在过度拟合的危险信号。模型 3,或岭回归,具有次高的 r 平方以及最低的误差项(RMSE 和梅)。因此,我们可能希望部署模型 3 或利用集成学习(组合多个预测模型以胜过任何单个模型),来训练和运行模型 3 和随机森林,并平均各个模型的预测。然而,此外,作为所有模型的结果,我们可以有更高的信心,即在不同模型中持续出现的变量——白人百分比、人均警察支出、贫困率、中等家庭收入、党派倾向、种族和武装是警察枪击事件的重要预测因素。
含义和要点
在我们的探索性数据分析和建模之后,我们现在可以更有信心地确认某些因素在警察枪击事件中更具预测性的假设,如受害者的种族(黑人在统计上最重要,男性在统计上最重要,这支持了黑人最有可能被警察射杀的运动信念), 它们更多地发生在贫困率较高、少数族裔比例较高(白人比例较低)、该国倾向于某个政党(共和党在统计上具有显著意义)、人均警察支出较高(为活动团体减少警察资金的主张提供了更多支持)、受害者可能持有武器以及警察可能没有佩戴人体摄像机的州。 此外,我们还确定了警察枪击事件发生率最高的州,如阿拉斯加州、新墨西哥州、俄克拉荷马州、亚利桑那州、科罗拉多州、内华达州和蒙大纳州,这对于活动组织和政策制定者来说是有用的,他们可以首先将工作重点放在这里,而不是在全国范围内模糊地呼吁变革。这些信息还可以帮助指导需要实施的新的或改革后的警察培训,包括反种族主义培训,如何在不诉诸开枪/杀人的情况下最好地对付持有不同类型武器的嫌疑人,以及强制广泛实施人体摄像机。
从社会政治的角度来看,这些发现也很重要,可以用来促进对贫困程度较高的州的高犯罪率以及这两个州之间可能发生的循环系统的进一步研究,共和党州与民主党州之间可能导致警察枪击事件发生率较高的动态,以及受害者与受害者之间的比较。当然,这种分析的范围是有限制的,因为有很多数据是数据库无法捕捉到的,比如不同的偏见、感知到的威胁、情况的严重性等等。因为这些都是更主观的信息,所以考虑这些很重要。为了继续预测未来几年的警察枪击事件,一旦某些属性或措施在政策方面发生变化(例如,人均警察支出的减少或强制性人体摄像头),该模型的最佳部署将是岭模型(模型 3)和随机森林模型(模型 4)的集成学习组合,如模型部署部分所述,以评估这些变化是否导致较低的预测。
探索空间:构建高级 NLP 产品的一站式图书馆
原文:https://towardsdatascience.com/exploring-spacy-your-one-stop-library-to-build-advanced-nlp-products-d242d8d753af?source=collection_archive---------41-----------------------
这是一个快速、无缝、先进的自然语言处理库

由内森·杜姆劳在 Unsplash 上拍摄的照片
随着自然语言处理(NLP)成为构建现代人工智能产品的主要工具,开源库被证明是建筑师的福音,因为它们有助于减少时间,并允许更大的灵活性和无缝集成。spaCy 就是这样一个用流行的 Python 语言编写的高级 NLP 库。今天,我们将探索 spaCy,它的特性,以及如何开始使用免费库无缝构建 NLP 产品。
什么是 spaCy?
spaCy 是一个免费的开源库,适合那些处理大量文本的人。它是为生产使用而设计的,允许您构建必须处理大量文本的应用程序。你可以使用 spaCy 来构建信息提取、自然语言理解或深度学习预处理文本的系统。
它提供什么?
spaCy 提供了许多特性和功能,从语言概念到机器学习功能。它的一些特性包括:
标记化
将文本分割成单词、标点符号等。
词性标注
将单词类型分配给标记,如动词或名词。
依存解析
分配句法依存标签,描述单个标记之间的关系,如主语或宾语。
词汇化
指定单词的基本形式。比如“was”的引理是“be”,“rats”的引理是“rat”。
【SBD】句子边界检测
寻找和分割单个句子。
【命名实体识别】(NER)
标记命名的“真实世界”对象,如人、公司或地点。
【实体链接(EL)】
消除文本实体与知识库中唯一标识符的歧义。
相似度
比较单词、文本跨度和文档以及它们之间的相似程度。
文本分类
为整个文档或部分文档指定类别或标签。
基于规则的匹配
根据文本和语言注释查找符号序列,类似于正则表达式。
训练
更新和改进统计模型的预测。
序列化
将对象保存到文件或字节字符串。
其他功能包括:
●支持 61 种以上的语言
●16 种语言的 46 种统计模型
●预训练的单词向量
●一流的速度
●轻松的深度学习集成
●句法驱动的句子分割
●内置语法和 NER 可视化工具
●方便的字符串到哈希映射
●导出到 numpy 数据阵列
●简单的模型打包和部署
●稳健、经过严格评估的精确度
…这还不是完整的列表。然而,作为数据科学家,我们也需要看看 spaCy 没有的所有东西。例如,它不是一个提供 SaaS 或 web 应用的 API 或平台。相反,它的库允许你构建 NLP 应用。它也不是为聊天机器人设计的。然而,它可以作为聊天机器人的底层技术提供文本处理能力。
斯帕西的建筑
spaCy 的核心架构包括 Doc 和 Vocab 对象,Doc 拥有令牌序列及其所有注释,Vocab 对象拥有一组查找表,这些表使得通用信息可以跨文档使用。这允许集中字符串、词向量和词汇属性,从而避免存储这些数据的多个副本,进而节省内存并确保只有一个真实的来源。
类似地,文本注释被设计成允许单一的真实来源。为此,Doc 对象拥有数据,并且 Span 和 Token 指向它。它由记号赋予器构造,然后由管道的组件就地修改。语言对象通过获取原始文本并通过管道发送来协调这些组件,管道返回一个带注释的文档。它还编排培训和序列化。
空间入门
许多人认为 spaCy 是 NLP 的 Ruby on Rails,它易于安装,API 简单高效。您可以通过首先安装 spaCy 及其英语语言模型来开始使用 spaCy。它与 64 位 CPython 2.7 / 3.5+兼容,可以在 Unix/Linux、macOS/OS X 和 Windows 上运行。
接下来,使用一个文本示例。在 spaCy 之后,导入 displaCy,它用于可视化 spaCy 的一些建模,以及一个英文停用词列表。然后,将英语语言模型作为语言对象加载,然后在示例文本中调用它。这将返回一个已处理的 Doc 对象。
这个经过处理的文档被分割成单个的单词并进行注释,但是它包含了原始文本的所有信息。我们可以将一个标记的偏移量放入到原始字符串中,或者通过连接标记和它们后面的空格来重构原始字符串。
您可以在 pip 和 conda 上安装最新的 spaCy 版本。我将试着展示一些用法的例子
在 python 中初始化空间
import spacy
nlp = spacy.load('en_core_web_sm')nlp("He went to play basketball")
使用空间的词性标注
import spacynlp = spacy.load('en_core_web_sm')doc = nlp("He went to play basketball")for token in doc:
print(token.text, "-->", token.pos_)
输出
He –> PRON
went –> VERB
to –> PART
play –> VERB
basketball –> NOUN
使用空间进行依存解析
# dependency parsingfor token in doc:
print(token.text, "-->", token.dep_)
输出
He –> nsubj
went –> ROOT
to –> aux
play –> advcl
basketball –> dobj
依存标记词根表示句子中的主要动词或动作。其他单词直接或间接地与句子的词根相连。您可以通过执行下面的代码来找出其他标记代表什么:
基于空间的命名实体识别
doc = nlp("Indians spent over $71 billion on clothes in 2018")for ent in doc.ents:
print(ent.text, ent.label_)
输出
Indians NORP
over $71 billion MONEY
2018 DATE
结论
总的来说,这个像 spaCy 一样的库抽象掉了使用方面的所有复杂性,开始使用它就像写几行代码一样简单。它真的能让像我这样的数据工程师快速上手,用它来处理非结构化的文本数据。
在 Linkedin 和 Twitter 上关注我
如果你对类似的内容感兴趣,请在 Twitter 和 Linkedin 上关注我
推特
探索堆栈和队列
原文:https://towardsdatascience.com/exploring-stacks-and-queues-b18243271d1c?source=collection_archive---------10-----------------------
小窍门
用两个非常有用的工具增强你的程序

照片由内森·杜姆劳在 Unsplash 上拍摄
在我们的上一篇文章中,我们讨论了数据结构,或者说编程语言在内存中存储数据的方式。我们提到了抽象数据类型,即通过数据结构实现的理论实体。“车辆”的概念可以被视为一种抽象的数据类型,例如,“自行车”就是一种数据结构。
在本文中,我们将探索两种常见的抽象数据类型:堆栈和队列。在用 Python 实现它们之前,我们将从这些抽象类型背后的理论开始。最后,我们将访问一些 Leetcode 问题,这些问题最初看起来很有挑战性,但是当使用堆栈或队列时,它们会被巧妙地分解成干净的解决方案。我们开始吧!
概观
堆栈和队列是类似数组的值的集合,像[1, 2, 3]或[a, b, c]。但是与数组不同,集合中的任何值都可以在 O(1)时间内访问,堆栈和队列有一个限制,即只有一个值立即可用:第一个元素(对于队列)或最后一个元素(对于堆栈)。对于堆栈和队列,值总是添加到末尾。
视觉可以帮助解释。下面,我们看到了从堆栈中添加和移除值的过程。块 C 被添加到堆栈中,然后弹出。堆栈遵循一种 后进先出 的模式:最后一个要添加到堆栈中的元素首先被移除。经典的类比是一堆盘子:最上面的盘子是最后添加的,也是第一个移除的。

作者图片
与此同时,一个队列是 先进先出 。下面,我们看到块 C 再次被添加到队列的末尾。但是这一次,块 A 离开了:它是第一个进来的,所以它是第一个出去的。一个常见的排队例子是杂货店的结账队伍——在所有排队的人中,最早到达的人将是下一个被看到的人(即“先来先服务”)。

作者图片
仅直接访问第一个或最后一个元素似乎是数组的一个主要缺点。然而,我们并不总是想要访问每个元素。我们处理元素的顺序通常是不同的,这意味着我们只关心对下一个元素的 O(1) 访问。
我们可以在栈和队列的主要用例中看到这一点。堆栈用于文本编辑器、编译器语法检查、执行递归函数调用、深度优先搜索以及其他我们关心最后执行的动作的情况下的 撤销和重做操作。
同时,队列用于异步 web 服务通信,调度 CPU 进程,跟踪 [N](https://stackoverflow.com/questions/5498865/size-limited-queue-that-holds-last-n-elements-in-java) 最近添加的元素,广度优先搜索,以及任何时候我们关心服务请求的接收顺序。
由于堆栈和队列不需要立即访问每个元素,它们通常用 链表 数据结构而不是数组来实现。这使得堆栈或队列可以无限增长,如果我们需要的话,还可以包含多种数据类型。
履行
创建链接列表
让我们构建利用链表的Stack和Queue Python 类。我们首先定义列表节点,它由一个值(self.val)和指向列表中下一个节点的指针(self.next)组成。我们还将添加一个__repr__魔法方法,使节点内容更容易可视化。
我们可以通过将ListNode实例链接在一起来创建链表。这里有一个简单的例子,用值1、2和3创建和可视化一个列表。
有了列表节点结构,我们就有了堆栈和队列类的中心构建块。

作者图片
创建堆栈
正如我们所见,栈的一个主要操作是添加或删除最近的元素,也称为 推送和弹出 。查看堆栈中的顶部元素而不立即移除它通常也是有帮助的,这一概念称为“扫视”
让我们开始吧。下面,我们用一个属性_stack定义一个Stack类,它包含我们的链表。该属性以下划线开头,向其他开发人员发出信号,该属性应被视为私有属性,不能在类外直接调用。
相反,我们将只通过我们的push、peek和pop方法与_stack交互。1为了在 O(1)时间内完成这些操作,我们将把栈中最近的元素放在我们的Stack类中链表的头处,这样它总是容易访问的。
我们的push方法为新值创建一个节点,将节点的next属性指向现有列表,然后将self._stack重新定义为新节点。
peek和pop需要一些控制流,以避免在空堆栈上调用它们时引发错误。两者都只允许我们在self._stack不为空时调用val和next属性,否则会抛出错误。
如果堆栈为空,我们的方法返回None,但是有一种方法显式声明堆栈是否有数据会很方便。因此,让我们添加一个is_empty方法。我们还将添加遍历列表的方法:一个确定堆栈是否包含请求的值(contains),一个打印列表内容(__repr__)。注意,遍历方法将在 O(n)时间内执行——列表越长,扫描或打印所有元素的时间就越长。[2]
(我们可以通过引用一个记录我们添加的每个值的实例数量的dict来为contains实现 O(1)时间,但是现在让事情简单一些。)
is_empty只是检查self._stack是否有任何值。contains和__repr__使用一个while循环迭代遍历列表,在检查节点的值是否等于我们正在搜索的值,或者将值追加到列表后,将node设置为其next属性。
下面,我们试验一下我们的类,并确认它如预期的那样工作。

作者图片
创建队列
与堆栈一样,我们的Queue类需要一种快速添加和移除元素的方法。但是当元素的添加和删除都发生在栈列表的头时,这些操作发生在队列列表的相对端。
那么链表的头应该是队列中最新的还是最老的元素呢?如果它是最新的,那么添加将花费 O(1)时间,而删除将花费 O(n)时间。如果头部是最老的元素,那么移除会很快,而添加会很慢。
这实际上是一个错误的二分法——如果我们存储指向列表头部和尾部的指针,我们可以在 O(1)时间内实现这两个目标。下面,我们用_head和_tail属性以及enqueue(添加)、peek和dequeue(移除)元素的方法开始我们的Queue类。
这些操作比我们的堆栈稍微复杂一些,尤其是当我们的_head和_tail指针指向同一个节点时。因此,当队列为空或者只有一个元素时,我们使用额外的逻辑来处理。
注意is_empty、contains和__repr__方法与我们的Stack类是多么的相同。一个不同之处是__repr__将按照队列接收的顺序打印我们的元素,而不是按照堆栈的相反顺序打印。在这两种情况下,它们都是按照移除的顺序打印的。
我们现在可以这样玩我们的Queue类:

作者图片
用例
上面,我们为栈和队列定义了 Python 类,使用链表来存储对象的内容。在这一节中,我们将通过解决几个 Leetcode 问题来展示这些抽象数据类型的威力。至少对我来说,当我第一次看到这些问题时,它们似乎是不可能的谜题。然而,一旦我理解了堆栈和队列的工作原理,这些难题就整齐地展开成了清晰的解决方案。希望我能在这篇文章中分享一些“啊哈”的感觉。
大量
堆栈非常有用的一个领域是代码验证。毫无疑问,如果你还在阅读这篇博文,你会知道,编程语言利用弯(( ))、方([ ])和弯({ } ) 括号来实现函数、索引、循环等等。每一个开括号都需要一个匹配的闭括号,你不能在开括号之前有一个闭括号。
你如何在不发疯的情况下跟踪所有的开括号和闭括号,以及它们是弯的、方的还是卷的?怎么才能自动分辨出左下方的括号是对的,而右边的是错的?

作者图片
这个问题, LC 20: 有效括号,没有栈就存在痛苦,有了栈就奇妙的简单。下面,我们将编写一个函数,它接受一串括号并返回一个括号是否有效的布尔值。
我们的解决方案在高层次上是这样工作的:每当我们看到一个开括号,我们就把它添加到堆栈中。每当我们看到一个右括号,我们就检查我们看到的最后一个左括号是否与右括号匹配。如果没有,我们知道这个字符串是无效的。如果是这样,我们就从堆栈中弹出那个开括号,继续扫描字符串。如果我们坚持到最后,最后的检查是确认堆栈是空的,即没有不匹配的开括号。
为了简单起见,我们使用一个内置的 Python list作为我们的堆栈,记住总是从末尾推入和弹出。我们还利用 Python dict来查找我们的右括号——每当我们看到右括号,我们就可以快速查找它对应的左括号。
我们跟踪两种情况,在这两种情况下我们可以退出for循环并声明字符串无效:1)如果我们遇到一个右括号并且堆栈为空,2)如果最后一个左括号与我们的右括号不匹配。最后的检查是确保当我们遍历完字符串后,堆栈是空的。
让我们来看看它的实际应用:
让我们试试这个问题的一个稍微难一点的变体。在 LC 1249: 最小删除以使括号有效,而不是输出一个简单的“是/否”布尔值来判断字符串是否有效,我们需要通过删除放错位置的括号使字符串有效。字符串也将包含字母和括号的混合,但作为一个小小的让步,我们只需要处理弯括号。在下图中,我们需要删除红色括号,使每个字符串有效。

作者图片
我们是这样做的。在高层次上,我们将遍历字符串,当我们看到一个左括号时添加到堆栈中,当我们看到一个右括号时从堆栈中删除。如果我们遇到一个没有开括号的闭括号,我们会立即删除它——没有开括号可以匹配这个闭括号。一旦我们遍历了字符串,我们就删除所有剩余的左括号,因为它们没有匹配的右括号。
让我们更仔细地看看代码。我们从将字符串转换成列表开始,因为在可变列表中修改元素要比不可变字符串容易得多。(我们每次都必须创建一个新的字符串,而不是就地修改列表。)
然后,我们实际上在堆栈中存储开括号的索引,而不是括号本身,因为我们需要知道在哪里删除放错位置的括号。当我们在字符串中移动时,我们处理放错位置的右括号,因为我们从空堆栈中知道,在字符串的前面没有匹配的左括号。对于左括号,我们需要遍历整个字符串,才能知道它们是否有匹配的右括号。
“移除”是通过用空字符串替换括号来实现的——因为我们正在跟踪放错位置的括号的索引,当我们在列表中移动时,移动现有元素的索引可能会导致巨大的麻烦。相反,在最后一步,当我们用''.join(s)将列表转换成字符串时,我们删除所有空字符串。
最后,让我们确保它有效:
行列
现在让我们将注意力转移到队列的用例上。队列有用的一个常见问题是树的层次顺序遍历。我们如何逐层打印树中每个节点的值呢?我们通常只得到根节点,所以我们事先不知道树是什么样子。因此,我们希望在探索过程中正确处理树。

作者图片
正如在我们的上一篇文章中一样,我们将使用下面的实现来实现一个树节点。这个节点将是一个二进制节点,因为它最多有两个子节点,但是我们的层次顺序遍历算法可以很容易地扩展到有任意数量子节点的节点。
定义了我们的TreeNode,让我们编写一个函数,在给定树的根节点的情况下,执行层次顺序遍历。如果使用队列,这个解决方案非常简短。
概括地说,我们从根节点入队开始。我们让节点出队,将其值附加到我们的答案中,然后让其子节点入队。然后我们简单地重复这个过程,直到我们处理完所有的节点。就是这样!
与我们的堆栈示例一样,我们再次使用内置的list,这一次记住总是在末尾排队(即.append)并从前面出列(即.pop(0))。因为新的子节点总是被添加到末尾,所以我们可以保证一个级别上的所有节点在来自较低级别的节点之前被处理。下面我们来看一下节点B的出队过程,将它的值添加到答案中,并将其子节点入队。

作者图片
让我们确认它的工作原理:
这个问题的一个更复杂的版本是只返回树中每一层最右边的节点。例如,在上图的树中,我们想要返回[A, C, E]。回答 LC 199: 二叉树右侧视图的一般结构类似于我们上面的答案,但是我们需要额外的逻辑来检查我们是否在最右边的节点。
注意,现在有两个循环:一个是while循环,遍历队列直到队列为空,另一个是for循环,处理给定级别的所有节点。这个问题的“诀窍”是认识到,如果我们在开始处理队列的节点(第 12 行)之前拍摄队列的快照(第 9 行),我们就能够看到给定级别的所有节点,从而允许我们识别最右边的节点。这个快照非常重要,因为我们在移动队列时会修改队列(第 21-24 行)。
下面,当我们的算法处理一棵更大的树(左)时,我们将队列(右)可视化。请注意,当一个队列包含一个级别中的所有节点时,快照是如何发生的。

作者图片
最后,让我们确认代码工作正常:
结论
在这篇文章中,我们深入探讨了两种最常见的抽象数据类型:堆栈和队列。在用 Python 实现它们并自己解决一些具有挑战性的问题之前,我们讨论了它们的各种用例。
如果你有兴趣了解更多,那么 优先队列 是一个很好的下一步。这种抽象数据类型为队列中的每个元素分配一个等级,允许我们将某些元素优先于其他元素。例如,当乘客通过金属探测器时,机场的安检线就像一个典型的先进先出队列。但是当有 TSA 预检的乘客到达时,他们可以跳到前面先被处理。乘客是否已经预检决定了他们在队列中的优先顺序。
最后,重要的是要重申,有许多方法可以让实现堆栈或队列。我们选择为我们的Stack和Queue类使用一个链表来反映这些类在 Java 或 C 等语言中是如何实现的,同时我们为我们的 Leetcode 问题使用了一个内置的 Python list来简化事情。但是没有什么可以阻止你使用树或者带图的队列来实现栈!转念一想,仅仅因为你可以用图实现队列并不意味着你应该 …
最好,
哑光
脚注
1.创建堆栈
都是荣誉代码,不会直接调用以下划线开头的方法和属性!Python 实际上并不强制执行这条规则;我们可以很容易地修改内容和肆虐。
在 Python 中实际上没有真正的方法来防止这种情况。我们能得到的最接近的方法是使用@property装饰器进行一点模糊处理。我们可以为用户如何访问_password的别名编写规则,比如通过阻止读访问或者要求输入在覆盖_password之前满足某些标准。但是如果用户直接请求_password,我们不能阻止他们查看或修改它。
给属性起一个疯狂的名字也没什么希望,因为攻击者可以使用vars命令来泄漏所有有趣的内容。虽然他们可能不会立即知道该属性的用途,但他们已经有了一组整洁的键值对可以使用。
最终,任何数据,即使是非常敏感的,也应该存储在具有严格安全要求的 API 后面。如果你的程序确实在 Python 类中存储敏感数据,也许是因为它在数据库或 API 之间传递,你应该确保没有办法从前端与这个类交互。
2.创建堆栈
重要的是要记住,大 O 符号量化了最差情况下的效率。在最坏的情况下,我们的搜索将需要扫描整个堆栈来找到我们要找的值(即所有的 n 值)。这不同于平均值的情况:如果我们搜索的值随机分布在整个堆栈中,我们应该期望找到中间(即 n/2 步)的平均值。
用数据探索泰国菜
原文:https://towardsdatascience.com/exploring-thai-food-with-data-fae0193851be?source=collection_archive---------27-----------------------
使用 R 和 Python 的端到端探索性数据项目

作者图片
概观
“我们点泰国菜吧。”
“太好了,你的拿手好菜是什么?”
"泰式炒面"
这困扰了我多年,也是这个项目的起源。
人们需要知道除了泰式炒面,他们还有其他选择。泰式炒面是 53 道独立菜肴之一,在那里停下来可能会错过至少 201 道共享泰式菜肴(来源:维基百科)。
这个项目是一个通过从维基百科中抓取表格来建立泰国菜数据集的机会。我们将使用 Python 进行 web 抓取,使用 R 进行可视化。网页抓取用Beautiful Soup (Python)完成,用dplyr进一步预处理,用ggplot2可视化。
此外,我们将使用 R 中的tidytext包来探索泰国菜的名称(英文),看看我们是否可以从文本数据中了解一些有趣的东西。
最后有机会做一个开源贡献。
项目回购这里是这里是。
探索性问题
这种分析的目的是提出问题。
因为探索性分析是迭代的,所以这些问题是在操纵和可视化数据的过程中产生的。我们可以用这些问题来组织文章的其余部分:
- 我们如何组织泰国菜?
- 组织不同菜肴的最好方法是什么?
- 哪种原材料最受欢迎?
- 哪些原材料最重要?
- 你能仅仅从菜名中了解泰国菜吗?
网页抓取
注意:维基百科向感兴趣的用户提供所有可用内容的免费副本。他们的数据库下载可以用于个人使用,离线使用或者数据库查询。我们在这里搜集的所有文本内容都是在知识共享署名-共享 3.0 (CC-BY-SA)下获得多项许可的。虽然我们可以在技术上查询数据,但我们在这里是出于教育目的。为了确保不会产生繁重的服务器工作负载,我们将会话超时设置为 10 秒(参见下面的代码)。另见他们关于通过机器人抓取网页的通知。
我们刮了 300 道泰国菜。每道菜,我们都有:
- 泰语名称
- 泰国文字
- 英文名
- 地区
- 描述
首先,我们将使用以下 Python 库/模块:
import requests
from bs4 import BeautifulSoup
import urllib.request
import urllib.parse
import urllib.error
import ssl
import pandas as pd# To avoid overloading Wikipedia servers, we set the timeout to 10 secondsurl = "https://en.wikipedia.org/wiki/List_of_Thai_dishes"
s = requests.Session()
response = s.get(url, timeout=10) ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONEhtml = urllib.request.urlopen(url, context=ctx).read()
soup = BeautifulSoup(html, 'html.parser')
soup.title.string
我们将使用requests向我们需要的维基百科 url 发送一个 HTTP 请求。我们将使用“安全套接字层”(SSL)访问网络套接字。然后我们将读入 html 数据,用美汤解析它。
在使用美汤之前,我们想了解我们要在浏览器上 inspect 元素下刮取的页面(和表格)的结构(注:我用的是 Chrome)。我们可以看到,我们需要标签table,以及维基可排序的标签class。
all_tables = soup.findAll('table', {"class": "wikitable sortable"})

作者图片
我们将从美汤中使用的主函数是findAll(),三个参数是th(HTML 表格中的表头单元格)、tr(HTML 表格中的行)和td(标准数据单元格)。
首先,我们将表格标题保存在一个列表中,我们将在创建一个空的dictionary来存储我们需要的数据时使用它。
header = [item.text.rstrip() for item in all_tables[0].findAll('th')]table = dict([(x, 0) for x in header])
最初,我们想要抓取一个表,知道我们需要对所有 16 个表重复这个过程。因此我们将使用一个嵌套循环。因为所有的表都有 6 列,所以我们想要创建 6 个空列表。
我们将遍历所有表格行tr并检查 6 个单元格(我们应该有 6 列),然后我们将将数据追加到我们创建的每个空列表中。
# loop through all 16 tables
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]# 6 empty list (for 6 columns) to store data
a1 = []
a2 = []
a3 = []
a4 = []
a5 = []
a6 = []# nested loop for looping through all 16 tables, then all tables individually
for i in a:
for row in all_tables[i].findAll('tr'):
cells = row.findAll('td')
if len(cells) == 6:
a1.append([string for string in cells[0].strings])
a2.append(cells[1].find(text=True))
a3.append(cells[2].find(text=True))
a4.append(cells[3].find(text=True))
a5.append(cells[4].find(text=True))
a6.append([string for string in cells[5].strings])
你会注意到a1和a6的代码略有不同。回想起来,我发现cells[0].find(text=True)没有而产生某些文本,特别是如果它们是链接的话,因此做了一点小小的调整。
字符串标签返回一个NavigableString类型对象,而文本返回一个unicode对象(参见堆栈溢出解释)。
在我们丢弃数据之后,我们需要在转换到data frame之前将数据存储在dictionary中:
# create dictionary
table = dict([(x, 0) for x in header])# append dictionary with corresponding data list
table['Thai name'] = a1
table['Thai script'] = a2
table['English name'] = a3
table['Image'] = a4
table['Region'] = a5
table['Description'] = a6# turn dict into dataframe
df_table = pd.DataFrame(table)
对于a1和a6,我们需要做一个额外的步骤将字符串连接在一起,所以我额外创建了两个对应的列,Thai name 2和Description2:
# Need to Flatten Two Columns: 'Thai name' and 'Description'
# Create two new columns
df_table['Thai name 2'] = ""
df_table['Description2'] = ""# join all words in the list for each of 328 rows and set to thai_dishes['Description2'] column
# automatically flatten the list
df_table['Description2'] = [
' '.join(cell) for cell in df_table['Description']]df_table['Thai name 2'] = [
' '.join(cell) for cell in df_table['Thai name']]
在我们废弃了所有的数据并从dictionary转换到data frame之后,我们将向 csv 写入,准备在 R 中进行数据清理(注:我将 CSV 保存为 thai _ dishes.csv,但你可以选择不同的名称)。
数据清理
数据清理通常是非线性的。
我们将操纵数据进行探索,了解关于数据的信息,并发现某些东西需要清理,或者在某些情况下,需要返回 Python 重新清理。由于在探测和清理过程中发现缺失数据,列a1和a6与其他列的刮除方式不同。
对于某些链接,使用.find(text=True)没有达到预期的效果,所以做了一点小小的调整。
对于本文来说,R是清理数据的首选工具。
以下是其他数据清理任务:
- 更改列名(蛇形)
# read data
df <- read_csv("thai_dishes.csv")# change column name
df <- df %>%
rename(
Thai_name = `Thai name`,
Thai_name_2 = `Thai name 2`,
Thai_script = `Thai script`,
English_name = `English name`
)
- 删除换行符转义序列(\n)
# remove \n from all columns ----
df$Thai_name <- gsub("[\n]", "", df$Thai_name)
df$Thai_name_2 <- gsub("[\n]", "", df$Thai_name_2)
df$Thai_script <- gsub("[\n]", "", df$Thai_script)
df$English_name <- gsub("[\n]", "", df$English_name)
df$Image <- gsub("[\n]", "", df$Image)
df$Region <- gsub("[\n]", "", df$Region)
df$Description <- gsub("[\n]", "", df$Description)
df$Description2 <- gsub("[\n]", "", df$Description2)
- 添加/变更新列(主要分组,次要分组):
# Add Major AND Minor Groupings ----
df <- df %>%
mutate(
major_grouping = as.character(NA),
minor_grouping = as.character(NA)
)
- 编辑 Thai_name 列中缺少数据的行:26,110,157,234–238,240,241,246
注意:这只在第一轮时需要,在我刮的方式a1和a6改变后,这一步不再需要:
# If necessary; may not need to do this after scraping a1 and a6 - see above
# Edit Rows for missing Thai_name
df[26,]$Thai_name <- "Khanom chin nam ngiao"
df[110,]$Thai_name <- "Lap Lanna"
df[157,]$Thai_name <- "Kai phat khing"
df[234,]$Thai_name <- "Nam chim chaeo"
df[235,]$Thai_name <- "Nam chim kai"
df[236,]$Thai_name <- "Nam chim paesa"
df[237,]$Thai_name <- "Nam chim sate"
df[238,]$Thai_name <- "Nam phrik i-ke"
df[240,]$Thai_name <- "Nam phrik kha"
df[241,]$Thai_name <- "Nam phrik khaep mu"
df[246,]$Thai_name <- "Nam phrik pla chi"
- 保存到“edit _ thai _ dishes.csv”
# Write new csv to save edits made to data frame
write_csv(df, "edit_thai_dishes.csv")
数据可视化
有几种方法可以将数据可视化。因为我们想要传达泰国菜的多样性,除了泰国菜之外,我们想要一个包含许多选项的可视化。
我选择了树状图。该图假设数据中存在层次结构,这适合我们的项目,因为我们可以按分组和子分组来组织菜肴。
我们如何组织泰国菜?
我们首先区分个人和共享菜肴,以表明泰式炒面根本不是最好的个人菜肴。事实上,更多的菜肴归入共享组。
为了避免在一个视图中塞进太多的数据,我们将为单独和共享的菜肴创建两个单独的视图。
这是第一张树状图,代表泰式炒面的 52 种不同菜式。

作者图片
创建一个树状图需要使用ggraph和igraph库。首先,我们将加载库,并通过过滤单个菜肴来子集化数据框:
df <- read_csv("edit_thai_dishes.csv")library(ggraph)
library(igraph)df %>%
select(major_grouping, minor_grouping, Thai_name, Thai_script) %>%
filter(major_grouping == 'Individual dishes') %>%
group_by(minor_grouping) %>%
count()
我们创建边和节点(即 from 和 to)来创建单个菜肴(即米饭、面条和其他)中的子分组:
# Individual Dishes ----# data: edge list
d1 <- data.frame(from="Individual dishes", to=c("Misc Indiv", "Noodle dishes", "Rice dishes"))d2 <- df %>%
select(minor_grouping, Thai_name) %>%
slice(1:53) %>%
rename(
from = minor_grouping,
to = Thai_name
) edges <- rbind(d1, d2)# plot dendrogram (idividual dishes)
indiv_dishes_graph <- graph_from_data_frame(edges)ggraph(indiv_dishes_graph, layout = "dendrogram", circular = FALSE) +
geom_edge_diagonal(aes(edge_colour = edges$from), label_dodge = NULL) +
geom_node_text(aes(label = name, filter = leaf, color = 'red'), hjust = 1.1, size = 3) +
geom_node_point(color = "whitesmoke") +
theme(
plot.background = element_rect(fill = '#343d46'),
panel.background = element_rect(fill = '#343d46'),
legend.position = 'none',
plot.title = element_text(colour = 'whitesmoke', face = 'bold', size = 25),
plot.subtitle = element_text(colour = 'whitesmoke', face = 'bold'),
plot.caption = element_text(color = 'whitesmoke', face = 'italic')
) +
labs(
title = '52 Alternatives to Pad Thai',
subtitle = 'Individual Thai Dishes',
caption = 'Data: Wikipedia | Graphic: @paulapivat'
) +
expand_limits(x = c(-1.5, 1.5), y = c(-0.8, 0.8)) +
coord_flip() +
annotate("text", x = 47, y = 1, label = "Miscellaneous (7)", color = "#7CAE00")+
annotate("text", x = 31, y = 1, label = "Noodle Dishes (24)", color = "#00C08B") +
annotate("text", x = 8, y = 1, label = "Rice Dishes (22)", color = "#C77CFF") +
annotate("text", x = 26, y = 2, label = "Individual\nDishes", color = "#F8766D")
组织不同菜肴的最好方法是什么?
大约有 4X 个共享个菜肴作为单个菜肴,所以树状图应该是圆形以适合一个图形中所有菜肴的名称。
对于这些类型的视觉效果,我经常使用的一个很棒的资源是 R 图库。在如何计算文本角度方面有一个小问题,所以我提交了一个 PR 来修复。
也许区分个人菜肴和共享菜肴过于粗略,在 201 种共享泰国菜的树状图中,我们可以看到进一步的细分,包括咖喱、酱/酱、蒸、烤、油炸、油炸和炒菜、沙拉、汤和其他杂项:

作者图片
# Shared Dishes ----
df %>%
select(major_grouping, minor_grouping, Thai_name, Thai_script) %>%
filter(major_grouping == 'Shared dishes') %>%
group_by(minor_grouping) %>%
count() %>%
arrange(desc(n))d3 <- data.frame(from="Shared dishes", to=c("Curries", "Soups", "Salads",
"Fried and stir-fried dishes", "Deep-fried dishes", "Grilled dishes",
"Steamed or blanched dishes", "Stewed dishes", "Dipping sauces and pastes", "Misc Shared")) d4 <- df %>%
select(minor_grouping, Thai_name) %>%
slice(54:254) %>%
rename(
from = minor_grouping,
to = Thai_name
)edges2 <- rbind(d3, d4)# create a vertices data.frame. One line per object of hierarchy
vertices = data.frame(
name = unique(c(as.character(edges2$from), as.character(edges2$to)))
)# add column with group of each name. Useful to later color points
vertices$group = edges2$from[ match(vertices$name, edges2$to)]# Add information concerning the label we are going to add: angle, horizontal adjustment and potential flip
# calculate the ANGLE of the labels
vertices$id=NA
myleaves=which(is.na(match(vertices$name, edges2$from)))
nleaves=length(myleaves)
vertices$id[myleaves] = seq(1:nleaves)
vertices$angle = 360 / nleaves * vertices$id + 90 # calculate the alignment of labels: right or left
vertices$hjust<-ifelse( vertices$angle < 275, 1, 0)# flip angle BY to make them readable
vertices$angle<-ifelse(vertices$angle < 275, vertices$angle+180, vertices$angle)# plot dendrogram (shared dishes)
shared_dishes_graph <- graph_from_data_frame(edges2)ggraph(shared_dishes_graph, layout = "dendrogram", circular = TRUE) +
geom_edge_diagonal(aes(edge_colour = edges2$from), label_dodge = NULL) +
geom_node_text(aes(x = x*1.15, y=y*1.15, filter = leaf, label=name, angle = vertices$angle, hjust= vertices$hjust, colour= vertices$group), size=2.7, alpha=1) +
geom_node_point(color = "whitesmoke") +
theme(
plot.background = element_rect(fill = '#343d46'),
panel.background = element_rect(fill = '#343d46'),
legend.position = 'none',
plot.title = element_text(colour = 'whitesmoke', face = 'bold', size = 25),
plot.subtitle = element_text(colour = 'whitesmoke', margin = margin(0,0,30,0), size = 20),
plot.caption = element_text(color = 'whitesmoke', face = 'italic')
) +
labs(
title = 'Thai Food is Best Shared',
subtitle = '201 Ways to Make Friends',
caption = 'Data: Wikipedia | Graphic: @paulapivat'
) +
#expand_limits(x = c(-1.5, 1.5), y = c(-0.8, 0.8)) +
expand_limits(x = c(-1.5, 1.5), y = c(-1.5, 1.5)) +
coord_flip() +
annotate("text", x = 0.4, y = 0.45, label = "Steamed", color = "#F564E3") +
annotate("text", x = 0.2, y = 0.5, label = "Grilled", color = "#00BA38") +
annotate("text", x = -0.2, y = 0.5, label = "Deep-Fried", color = "#DE8C00") +
annotate("text", x = -0.4, y = 0.1, label = "Fried &\n Stir-Fried", color = "#7CAE00") +
annotate("text", x = -0.3, y = -0.4, label = "Salads", color = "#00B4F0") +
annotate("text", x = -0.05, y = -0.5, label = "Soups", color = "#C77CFF") +
annotate("text", x = 0.3, y = -0.5, label = "Curries", color = "#F8766D") +
annotate("text", x = 0.5, y = -0.1, label = "Misc", color = "#00BFC4") +
annotate("text", x = 0.5, y = 0.1, label = "Sauces\nPastes", color = "#B79F00")
文本挖掘
哪种原材料最受欢迎?
回答这个问题的一个方法是使用文本挖掘,通过任一单词对进行标记化,并通过频率对单词进行计数,作为流行度的一种度量。
在下面的柱状图中,我们可以看到所有泰国菜的词频。 Mu (หมู)在泰语中的意思是猪肉,在所有菜式和子类别中出现频率最高。接下来我们吃แกง咖喱饭,意思是咖喱。(ผัด)名列第三,暗示“炒”是一种受欢迎的烹饪方式。
正如我们看到的不是所有的词都是指原材料,所以我们可能无法直接回答这个问题。

作者图片
library(tidytext)
library(scales)# new csv file after data cleaning (see above)
df <- read_csv("../web_scraping/edit_thai_dishes.csv")df %>%
select(Thai_name, Thai_script) %>%
# can substitute 'word' for ngrams, sentences, lines
unnest_tokens(ngrams, Thai_name) %>%
# to reference thai spelling: group_by(Thai_script)
group_by(ngrams) %>%
tally(sort = TRUE) %>% # alt: count(sort = TRUE)
filter(n > 9) %>%
# visualize
# pipe directly into ggplot2, because using tidytools
ggplot(aes(x = n, y = reorder(ngrams, n))) +
geom_col(aes(fill = ngrams)) +
scale_fill_manual(values = c(
"#c3d66b",
"#70290a",
"#2f1c0b",
"#ba9d8f",
"#dda37b",
"#8f5e23",
"#96b224",
"#dbcac9",
"#626817",
"#a67e5f",
"#be7825",
"#446206",
"#c8910b",
"#88821b",
"#313d5f",
"#73869a",
"#6f370f",
"#c0580d",
"#e0d639",
"#c9d0ce",
"#ebf1f0",
"#50607b"
)) +
theme_minimal() +
theme(legend.position = "none") +
labs(
x = "Frequency",
y = "Words",
title = "Frequency of Words in Thai Cuisine",
subtitle = "Words appearing at least 10 times in Individual or Shared Dishes",
caption = "Data: Wikipedia | Graphic: @paulapivat"
)
我们还可以看到个人菜肴和共享菜肴共有的词汇。我们还看到了其他一些词,如 nuea(牛肉)、phrik(辣椒)和 kaphrao(罗勒叶)。

作者图片
# frequency for Thai_dishes (Major Grouping) ----# comparing Individual and Shared Dishes (Major Grouping)
thai_name_freq <- df %>%
select(Thai_name, Thai_script, major_grouping) %>%
unnest_tokens(ngrams, Thai_name) %>%
count(ngrams, major_grouping) %>%
group_by(major_grouping) %>%
mutate(proportion = n / sum(n)) %>%
select(major_grouping, ngrams, proportion) %>%
spread(major_grouping, proportion) %>%
gather(major_grouping, proportion, c(`Shared dishes`)) %>%
select(ngrams, `Individual dishes`, major_grouping, proportion) # Expect warming message about missing values
ggplot(thai_name_freq, aes(x = proportion, y = `Individual dishes`,
color = abs(`Individual dishes` - proportion))) +
geom_abline(color = 'gray40', lty = 2) +
geom_jitter(alpha = 0.1, size = 2.5, width = 0.3, height = 0.3) +
geom_text(aes(label = ngrams), check_overlap = TRUE, vjust = 1.5) +
scale_x_log10(labels = percent_format()) +
scale_y_log10(labels = percent_format()) +
scale_color_gradient(limits = c(0, 0.01),
low = "red", high = "blue") + # low = "darkslategray4", high = "gray75"
theme_minimal() +
theme(legend.position = "none",
legend.text = element_text(angle = 45, hjust = 1)) +
labs(y = "Individual Dishes",
x = "Shared Dishes",
color = NULL,
title = "Comparing Word Frequencies in the names Thai Dishes",
subtitle = "Individual and Shared Dishes",
caption = "Data: Wikipedia | Graphics: @paulapivat")
哪些原材料最重要?
我们只能从频率中了解到这么多,因此文本挖掘从业者创造了术语频率—逆文档频率来更好地反映一个词在文档或语料库中的重要性(进一步的细节在此)。
还是那句话,不一定指的是原材料,所以这个问题在这里不能直接完全回答。

作者图片
你能仅仅从菜名中了解泰国菜吗?
简短的回答是“是的”。
我们仅从频率和“术语频率—逆文档频率”中了解到,不仅是最常见的单词,还有我们用tidytext标记的当前单词集中的相对重要性。这不仅告诉我们流行的原材料(猪肉),还告诉我们菜肴类型(咖喱)和其他流行的准备方式(炒菜)。
我们甚至可以检查单词之间的关系网的。深色箭头表示单词对之间的关系更强,例如“nam phrik”是一个强配对。这在泰语中是“辣椒酱”的意思,暗示了辣椒酱在许多菜肴中的重要作用。
我们在上面了解到“目”(猪肉)经常出现。现在我们看到“mu”和“krop”比其他配对关系更密切(注:“mu krop”的意思是“脆皮猪肉”)。我们在上面也看到了“khao”频繁出现在米饭菜肴中。这并不奇怪,因为“khao”在泰语中是米饭的意思,但我们在这里看到“khao phat”与炒饭(“khao phat”)密切相关,表明炒饭非常受欢迎。

作者图片
# Visualizing a network of Bi-grams with {ggraph} ----
library(igraph)
library(ggraph)
set.seed(2021)thai_dish_bigram_counts <- df %>%
select(Thai_name, minor_grouping) %>%
unnest_tokens(bigram, Thai_name, token = "ngrams", n = 2) %>%
separate(bigram, c("word1", "word2"), sep = " ") %>%
count(word1, word2, sort = TRUE) # filter for relatively common combinations (n > 2)
thai_dish_bigram_graph <- thai_dish_bigram_counts %>%
filter(n > 2) %>%
graph_from_data_frame() # polishing operations to make a better looking graph
a <- grid::arrow(type = "closed", length = unit(.15, "inches"))set.seed(2021)
ggraph(thai_dish_bigram_graph, layout = "fr") +
geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
arrow = a, end_cap = circle(.07, 'inches')) +
geom_node_point(color = "dodgerblue", size = 5, alpha = 0.7) +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
labs(
title = "Network of Relations between Word Pairs",
subtitle = "{ggraph}: common nodes in Thai food",
caption = "Data: Wikipedia | Graphics: @paulapivat"
) +
theme_void()
最后,我们可能会对道菜肴中的词汇关系感兴趣。
下图显示了一个中度到高度相关的词对网络。我们可以看到某些单词以相对较暗的线条聚集在一起:kaeng(咖喱)、pet(辣)、wan(甜)、khiao(绿咖喱)、phrik(辣椒)和 mu(猪肉)。这些词代表了通常组合在一起的配料、烹饪方式和描述的集合。

作者图片
set.seed(2021)# Individual Dishes
individual_dish_words <- df %>%
select(major_grouping, Thai_name) %>%
filter(major_grouping == 'Individual dishes') %>%
mutate(section = row_number() %/% 10) %>%
filter(section > 0) %>%
unnest_tokens(word, Thai_name) # assume no stop wordsindividual_dish_cors <- individual_dish_words %>%
group_by(word) %>%
filter(n() >= 2) %>% # looking for co-occuring words, so must be 2 or greater
pairwise_cor(word, section, sort = TRUE) individual_dish_cors %>%
filter(correlation < -0.40) %>%
graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(edge_alpha = correlation, size = correlation), show.legend = TRUE) +
geom_node_point(color = "green", size = 5, alpha = 0.5) +
geom_node_text(aes(label = name), repel = TRUE) +
labs(
title = "Word Pairs in Individual Dishes",
subtitle = "{ggraph}: Negatively correlated (r = -0.4)",
caption = "Data: Wikipedia | Graphics: @paulapivat"
) +
theme_void()
摘要
我们已经完成了一个探索性的数据项目,在这个项目中,我们使用 Python 和 r 的组合来搜集、清理、操作和可视化数据。我们还使用tidytext包来完成基本的文本挖掘任务,看看我们是否可以使用从维基百科上搜集的菜名中的单词来深入了解泰国美食。
欲了解更多关于数据科学、R、Python、SQL 等内容,在 Twitter 上找到我。
探索南亚的人口和权力部门
原文:https://towardsdatascience.com/exploring-the-demography-and-power-sector-of-south-asia-27cca720163c?source=collection_archive---------26-----------------------
使用 Python 中的 geopandas、matplotlib、networkx 和 Plotly 进行分析
假设你正在参加一个全球会议,与会者来自世界各地,目的是为了一个特殊的目标。如果你要在这个会议上与一个随机的人见面并互动,这个人很有可能来自南亚。根据统计,世界上每四个人中就有近一个来自南亚。该区域包括八个国家:阿富汗、孟加拉国、不丹、印度、马尔代夫、尼泊尔、巴基斯坦和斯里兰卡。这些国家共同组成了一个名为南亚区域合作联盟(SAARC) 的区域性政府间组织。
我来自这个地区的一个国家,一段时间以来,我一直好奇于分析这个地区的人口结构。有了能源系统的背景,我也有兴趣分析该地区国家的电力系统是什么样子,它们彼此之间有什么不同。因此,由于 Covid 相关的限制,这个假期没有旅行,我只是坐在我的笔记本电脑前,检查了一些公开来源的数据,并开始使用我最喜欢的编程语言 Python 处理它们。
事情是这样的。我将同时描述洞察力和代码。
人口统计
我仍然记得二十年前,当我在学校里得知世界人口是六十亿的时候。快进到今天,大概几个月或几年后,全球人口将达到 80 亿。

1990 年和 2019 年南亚国家的人口绘制为堆积条形图。条形之间的阴影区域代表两年间每个国家的人口增长。数据基于世界银行公开数据 2021 。
在南亚,2020 年印度占总人口的四分之三。其次是巴基斯坦和孟加拉国。该地区其余五个国家的人口加起来不到总人口的百分之五。在过去的三十年里,南亚的人口增长了 65%。这种增长的大部分可以归因于印度,因为它反映在上面图中两个堆叠柱之间的阴影区域中。然而,在此期间,该区域所有国家的人口都在稳步增长。
我从世界银行公开数据中获得了分析数据,并将其存储在df_pop数据框架中。

包含南亚八个国家人口数据的 df_pop 数据帧片段。数据基于世界银行公开数据 2021 。
获得上面的图的代码在下面的要点。在该图中,1990 年和 2020 年的刻度分别代表 x 轴上的 0 和 1 位置。因此,需要指定 x、y1 和 y2 的准确位置,以填充两个堆叠的列之间的区域,并指定相应的颜色来表示每个国家。
作者绘制的南盟国家 1990 年和 2020 年人口比较图。
按性别分列的人口分布
接下来,我想检查一下南亚的人口是如何基于性别分布的。我想在该地区的各个国家的地图上绘制一个条形图。
此过程的第一步是使用 geopandas 获取该区域的几何图形。本故事中提供了 geopandas 的介绍和应用。首先,我通过读取包的固有数据集生成了world地理数据框架。接下来,我取了一个名为saarc的world子集,它只包含八个南亚国家的几何图形。不幸的是,作为一个位于印度洋的小岛国,马尔代夫的几何数据在该数据集中不可用。
world **=** gpd**.**read_file(gpd**.**datasets**.**get_path('naturalearth_lowres'))saarc_countries = ["Nepal","India","Bangladesh","Bhutan","Afghanistan","Pakistan","Sri Lanka","Maldives"]#select only the countries in the list from worldsaarc **=** world[world**.**name**.**isin(saarc_countries)]
2020 年基于性别的人口数据来自df的世界银行公开数据,基于两者都有一个国家名称的公共列,被附加了saarc。看起来是这样的:
saarc = saarc.join(df)

saarc geopandas 数据框架的片段,其中男性和女性人口份额作为列附加在右侧。人口数据基于世界银行公开数据 2021 。
接下来,我通过将宽度和高度指定为parent_axes的百分比,为各个国家在parent_axes中创建了inset_axes。对于边界框的位置,我提供了每个国家的几何形心。然后,我在为每个国家创建的inset_axes中绘制了男性和女性人口份额的并列条形图。代码如下所示:
作者绘制的南亚性别人口分布图。
结果图如下所示:

在南亚各自国家的地图上,男性和女性人口比例以条形标绘。数据基于世界银行公开数据 2021 。
由此得出的图描绘了南亚地区性别人口分布不均的情况。2020 年,尼泊尔和斯里兰卡的女性人口比例高于男性。对孟加拉国来说,几乎是 50:50。在阿富汗、不丹、印度和巴基斯坦,男性人口的比例高于女性人口。
南亚的发电组合
我的研究兴趣在于了解不同国家的能源供需结构,更重要的是,这些国家如何满足他们的电力需求。本次分析的数据来源于我们的数据世界网站。
不用说,印度的总发电量和发电厂装机容量远远低于该地区任何其他国家,因为它幅员辽阔,人口众多。然而,我想比较一下各国的电力组合。为此,我使用了与上一个相同的方法来构建情节。

发电组合在南亚各国地图上绘制成饼图。数据基于 2021 年数据中的我们的世界。
该图表明,到 2020 年,不丹和尼泊尔几乎完全依赖水力发电,这是一种可再生的电力来源。这两个国家拥有利用发源于喜马拉雅山脉的河流进行水力发电的巨大潜力。该区域其他国家的电力组合中水电所占份额相对较低。
一个惊人的事实是,上面的图表描述了其他国家对化石燃料发电的严重依赖。例如,孟加拉国是 T2 地区人口最稠密的国家,几乎所有的电力都来自天然气或石油。印度四分之三的电力来自煤炭,这是碳密度最高的燃料。2020 年,只有印度和巴基斯坦有核电运行。
由于其地理位置,南亚国家全年都有充足的太阳辐射。有些地点也有良好的风势。为了对全球气候行动做出贡献,这些国家必须挖掘其可再生能源潜力,避免未来可能的碳锁定。
南亚的电力接入
仅在过去几十年里,由于政府、私营部门、公用事业公司、智囊团和国际发展合作的努力,南亚各国在向人民提供电力方面取得了显著进展。
为了比较现实中的进展,我更深入地研究了来自世界银行公开数据的电力接入数据,并将其与saarc地理数据框架相结合。基于数据可用性,我在每个国家的子图中使用 choropleth 地图绘制了该地区 2005 年和 2019 年的电力接入率。

2005 年和 2019 年南亚国家电力接入率。右边的颜色条是两个支线剧情共有的。数据基于世界银行公开数据 2021 。
该图显示,2005 年至 2019 年间,南亚的电力接入率显著提高。除巴基斯坦外,所有国家的电力供应率都达到了 90%以上。不丹和斯里兰卡已经达到了 100%的电气化率。在所有国家中,与农村人口相比,城市人口有更高的用电率。基础设施发展和技术能力不足是某些国家电力部门发展不佳的原因。
互联器
欧洲大陆同步电网覆盖了欧洲传输系统运营商网络(ENTSO-E) 的区域,连接欧洲部分或全部国家。这种类型的互连允许电力系统的灵活性,因为在一个高供应地区产生的低成本电力可以输送到另一个高需求地区。它不仅有助于在不同的空间和时间层次上维持供需平衡,而且它还确保了供应的安全性并有助于降低成本和排放。
目前,南亚国家之间现有和拟议的跨境互联互通有限,如不丹-印度、印度-尼泊尔、印度-孟加拉国等。虽然基础设施需要建立互联网络带来了自己的一系列成本和挑战,但一项研究表明,高收益成本比可能会超过挑战,这可能有助于该地区的长期发展。
我设想了一个该地区不同国家之间的假想互联,并试图在地图上绘制出来,仅供演示之用。我任意选择的假设interconnectors数据如下:

包含南亚电力系统间假设互联的互联数据帧片段。基于作者的任意选择(仅供演示)。
首先,我使用 Python 中的 networkx 包将它转换成一个有向图:
G = nx.from_pandas_edgelist(interconnectors, source = “From”, target = “To”, create_using = nx.DiGraph())
接下来,我通过指定墨卡托投影(一种圆柱形地图投影)和低分辨率创建了底图。我指定了经度和纬度,以便将所有国家都包含在底图中。
from mpl_toolkits.basemap import Basemapm = Basemap(projection = “merc”, #Mercator projection
llcrnrlon = 60, #longitude of lower left corner
llcrnrlat = 5, #latitude of lower left corner
urcrnrlon = 100, #longitude of upper right corner
urcrnrlat = 40, #latitude of upper right corner
lat_ts = 1, #latitude of true scale
resolution = “l”,
suppress_ticks = False)
我想在每个国家各自的几何形心中画出每个国家的节点。因此,我创建了一个positions字典,其中包括与底图大小成比例的节点位置。
positions = {}
for index, row in saarc.iterrows(): #Set positions on the Basemap in proportion to the size of Basemap
positions[index] = m(row.geometry.centroid.x, row.geometry.centroid.y)
我得到了下面的position字典:
{'India': (2178387.5972047374, 2063020.0475059198),
'Bangladesh': (3365125.8765982683, 2173785.0707132816),
'Bhutan': (3387861.379178301, 2616406.810286945),
'Nepal': (2669735.152648369, 2718429.0071621696),
'Pakistan': (1046628.9002186056, 2939091.5173554155),
'Afghanistan': (676705.6727856797, 3447841.160892035),
'Sri Lanka': (2297740.670615352, 302122.0644451928)}
最后,我通过指定节点位置、节点大小和其他参数绘制了有向图。国与国之间的边界宽度反映了我提供的假设容量。接下来,我还绘制了国家边界和海岸线,并填充了大陆,如下面的代码所示。
nx.draw_networkx(G, pos = positions,
node_shape = “o”,
node_color = “r”,
alpha = 0.8,
node_size = 100,
arrows = True,
width = [5, 5, 5, 10, 20, 20, 5, 5],
edge_color = “blue”)m.drawcountries(linewidth = 0.5)
#m.drawstates(linewidth = 1)
m.drawcoastlines(linewidth = 1)
m.fillcontinents(alpha = 0.5)plt.title(“Power system interconnections $(\itHypothetical)$”)plt.show()
最后得到了之前设想的国与国之间假设互联互通的情节。

南亚国家电力系统间的假想互联。红点代表节点,蓝线代表边。边缘的宽度反映了两国之间的互联能力。基于作者的任意选择(仅供演示)。
结论
随着世界正在经历全球能源转型阶段,研究、分析、建模和规划的作用比以往任何时候都更加重要。对人口统计和基础设施(如本文中描述的电力系统)的分析提供了见解,这对于确定发展需求、政策建议和投资缺口非常重要。这些数据和见解对于模拟一个国家或地区在任何部门的发展路径至关重要。

本故事中分析的人口和电力系统的可视化。图片作者。
这篇文章分析了南亚国家的人口统计和电力系统。这已经使用 Python 中的 geopandas、matplotlib、networkx 和 pandas 等包完成了。这个分析的笔记本可以在这个 GitHub 库中找到。
探索疫情对纽约市自行车共享使用的影响
原文:https://towardsdatascience.com/exploring-the-effects-of-the-pandemic-on-nyc-bike-share-usage-ab79f67ac2df?source=collection_archive---------38-----------------------
使用熊猫和 Seaborn 来比较 2019 年和 2020 年的乘坐次数和旅行持续时间,以了解使用情况如何变化。
通过比较 2019 年和 2020 年的骑行次数,我们可以看到疫情对纽约市自行车份额使用的影响。自行车共享系统 Citi Bike 在其网站上提供 tripdata 文件,以便任何人都可以分析该系统的使用情况。

2019/2020 年花旗自行车骑行计数-所有图片由作者提供
在我之前的文章探索纽约市自行车共享数据中,我经历了 2020 年 3 月的探索性数据分析过程,纽约市在这个月关闭。在一篇后续文章中,我使用了 2020 年 9 月的数据,当时乘客人数已经恢复,但我意识到我真的需要查看整整两年的数据才能理解疫情引起的变化。
当我在写这篇文章的时候,我收到了山姆·施瓦茨(纽约人称之为“僵局山姆”)的工作人员发来的一封电子邮件,他最近研究了花旗的自行车骑行情况。可在变速:花旗自行车骑行人数在冬季持续激增上找到,也有关于这个主题的早期文章的链接。然而,他们的图表是使用商业数据可视化工具制作的。
我想使用我喜欢的开源工具做一些分析:用于分析的 Pandas 和用于图形的 Seaborn 。然后任何人都可以复制和扩展我的分析。
下面显示的所有源代码都可以在 Github 上的 Jupyter 笔记本中作为 twoyears 获得。
下载并准备花旗自行车旅行数据
花旗自行车系统数据页面描述了花旗自行车提供的信息。每次乘坐的具体信息如下:
- 行程持续时间(秒)
- 开始和结束时间和日期
- 起点和终点站 ID 和名称
- 起点站和终点站的纬度和经度
- 自行车 ID
- 用户类型(客户=日票或单程票;订户=年度会员)
- 性别(零=未知;1 =男性;2 =女性)
- 出生年
这可以用一些派生列来扩充,以简化分析:
- 行程持续时间(分钟)
- 开始年份,一个月中的某一天,一天中的某个小时
- 周末指示器(布尔型)
- 年龄(省略 75 岁及以上的骑手,默认年份为 1969 年)
- 距离(使用哈弗辛公式计算)
这些计算是探索性数据分析过程的一部分。现在我需要对两年的月度数据进行同样的计算。我把 Python 程序保存在 GitHub 上的一个 Jupyter 笔记本里,名为 gettripdata 。
每个月的 tripdata 文件被保存为一个 Parquet 文件,其名称与原始 CSV 文件相同,但扩展名为.parquet。我将所有 24 个月的文件保存到一个tripdata子目录中。这让我可以使用完整的文件名读取单个月份,使用目录名读取所有数据。
Parquet 是一个列存储,它使用的空间比 CSV 文件少得多。它的阅读速度也快了很多,尤其是在只选择几列的时候,所以我可以只阅读任何特定分析所需的列。
拼花地板和熊猫很好的融合在一起。要读写拼花文件,您只需安装一个库:
conda install pyarrow
每月乘客量
为了查看每月的乘客总数,我需要year和month列,但是为了进一步分解数据,我还需要usertype和gender.,我将它们读入数据帧:
df = pd.read_parquet('tripdata',\
columns=['year','month','gender','usertype'])
df.shape
shape告诉我在两年内有超过 4000 万次乘坐。
(40058554, 4)
虽然我可以使用 Seaborn countplot但它必须扫描整个数据帧。由于获得图表是一个迭代的过程,我发现只汇总一次数据会更快,使用 Pandas groupby来计算年和月的乘坐次数。使用unstack()以宽格式显示数据。
counts = df.groupby(["year", "month"]).size()
counts.unstack()

按年份和月份统计乘坐次数
由groupby创建的对象在year和month上有多重索引。我想在图表中使用这些值,所以我将它们转换成列。我还将重命名Ride Count列。
counts=counts.reset_index( level = ['year','month'])
counts.rename(columns = {0:'Ride Count'}, inplace=True)
现在我可以使用barplot来可视化数据,但是创建一个类似于“换挡”文章中的有吸引力的图表需要几个额外的步骤:
- 为了将月份显示为文本而不是数字,创建一个包含三个字母的月份缩写的变量
months。 - 使用
hue=’year’, palette=’Paired’将条形变成两种蓝色以显示年份 - 要让年份垂直出现在每个条形上,使用
annotate并在条形间循环以注释每个条形。
注意:大多数参考资料显示了如何用高度来注释一个条,但是这不是使用这个特性的唯一方法。这里我使用了一个计数器y来存放year的当前值。
months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']plt.figure(figsize=(12,5))
ax=sns.barplot(data=counts, x="month" , y='Ride Count', hue='year', palette='Paired' )
ax.set_xlabel("Months")
ax.set_ylabel("Ride Counts in Millions")
ax.set_xticklabels(months)
ax.legend_.remove()
y=0
for p in ax.patches:
ax.annotate(counts.year[y], (p.get_x() + p.get_width()/ 2, 0),\
rotation=90, ha='center', size=12, fontweight='bold',\
xytext=(0, 10), textcoords='offset points')
y+=1

花旗自行车每月骑行次数— 2019 年和 2020 年
经过春季和夏季的下降后,乘客人数在 9 月份开始增长,并在秋季和冬季继续增长。要计算百分比变化,请使用:
100 * counts.unstack().pct_change().round(3)[1:]

从 2019 年到 2020 年的乘客百分比变化
按性别和用户类型划分的乘客数量
分析师指出,女性骑手的比例有所增加。要查看这一变化,我可以使用相同的数据框架,并按usertype和gender汇总乘坐次数。
countsg=df.groupby(["usertype","year", "month","gender"])\
.size().unstack()
countsg.head()
提醒一下,Citi Bike 区分订户(年度会员)和客户(一次出行或每日用户),性别值为 0(未指定)、1(男性)和 2(女性)。

按用户类型和性别划分的乘客数量
计算女性骑手的百分比,首先对各列求和,得出骑手总数,然后将女性骑手总数除以总数,再乘以 100,得出百分比:
countsg['total'] = countsg.sum(axis=1)
countsg['pct female'] = countsg[2] / countsg.total * 100
countsg.head()

女骑手的百分比
为了制作图表,我将把多索引转换成列:
countsg=countsg.reset_index( level = ['usertype','year','month'])
为了并排显示年份,我使用了catplot并分别显示订户和客户,我创建了一个两种类型的列表usertypes,然后我可以对其进行迭代。为了便于比较,我将两个图表的最大值设为相同的值,40%。
usertypes=['Subscriber','Customer']
sns.set(font_scale = 2)for u in usertypes:
ax=sns.catplot(data=countsg[countsg.usertype==u],\
x="month", y="pct female", col="year",\
color='Green', kind='bar', height=5, aspect=12/5 )
ax.set_axis_labels("", u + a"\n% Female")
ax.set_xticklabels(months)
ax.set_titles('{col_name}')
ax.set(ylim=(0,40))
这确实显示了女性骑手的比例在增加,对于这两种类型的用户来说都是如此,但对于订户(年度会员)来说尤其如此。

女骑手的百分比
然而,有一点它没有显示出来,那就是客户(每日用户)的乘车比例,这一比例一直远低于订户(年度会员)。为了查明这是否随着时间的推移而改变,我在usertype上做了另一个groupby:
count_type=df.groupby(["year", "month", "usertype"])\
.size().unstack()
count_type.head()

按年份、月份和用户类型划分的乘客人数
然后我创建了另一个数据框架,显示了顾客乘坐的百分比:
custpct = count_type.Customer / (count_type.Customer +\
count_type.Subscriber) * 100
custpct = custpct.reset_index(level = ['year','month'])\
custpct.rename(columns = {0:'Customer Percent'}, inplace=True)
再次使用catplot获得两张并列图表:
sns.set(font_scale = 3)
ax = sns.catplot(data=custpct,\
x="month", y="Customer Percent", col="year", dodge=False, \
color='blue', kind='bar', height=10, aspect=20/10 )
ax.set_axis_labels("", "Customer\n% Rides")
ax.set_xticklabels(months)
ax.set_titles('{col_name}')
ax.set(ylim=(0,40)) ;
在这里我可以看到,从 2019 年到 2020 年,客户与订户的比率有所增加,尤其是在夏季。

顾客乘坐的百分比
一天中各小时的乘客量
分析师注意到的另一个变化是一天中乘车次数的分布。这个分析需要与上面不同的列,所以我将返回到文件并使用year、weekday和start hour创建一个新的 DataFrame。
df = pd.read_parquet('tripdata',\
columns=['year','weekday','start hour'])
按相同的列进行聚合,将多索引更改为列,并重命名Ride Count列。
count_hr = df.groupby(["year", "weekday","start hour"]).size()
count_hr=count_hr.reset_index(level=['year','weekday','start hour'])
count_hr.rename(columns = {0:'Ride Count'}, inplace=True)count_hr['Ride Count'] /= 1000
由于catplot没有折线图,我使用subplot为一周中的每一天创建一个lineplot。我将迭代天数,从 0(星期一)到 6(星期日),使用星期几来自定义图表和标签。
sns.set(font_scale = 1)
fig, ax = plt.subplots(1,7, figsize=(20,5))days=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']for d in range(len(days)):
sns.lineplot(ax=ax[d], data=count_hr[count_hr.weekday==d],\
linewidth=4,x="start hour", y="Ride Count", hue='year',\
palette='Paired')\
.set_title(days[d]) ;
if ~d:
ax[d].set_ylabel('Ride Count in Thousands')
if d:
ax[d].set_ylabel('')
ax[d].set_yticklabels('')
if d != 3:
ax[d].set_xlabel('')
if d < 6:
ax[d].legend_.remove()
这里可以看到 2019 年到 2020 年的变化。2020 年的高峰时间比 2019 年要少得多,尤其是早高峰。周末的乘车次数也增加了。这两种趋势都表明了从通勤旅行到休闲骑行的转变。

2019 年和 2020 年——按一周中的每一天和一天中的每一小时计算乘车次数
请注意,这些图表反映了全年的情况;个别月份看起来会有所不同。
行程持续时间
这种转变的另一个迹象是乘坐时间的长短。分析师指出,在 12 月,“平均旅行持续时间延长了 20%以上——17 分钟比 14 分钟长。”虽然我确认这个数字是正确的,但我认为它有误导性。在我的分析中,我看到了旅行持续时间的异常值:自行车没有被归还或正确停靠,导致旅行时间过长。
我认为使用中值旅行持续时间比平均值更好,因为平均值被那些大值抛弃了。
我将重新读取 tripdata 文件以获取year、month和tripduration,并将持续时间从秒转换为分钟。然后执行一个groupby,但是我将使用agg('median')来获得每组的平均旅行持续时间,而不是使用size()来获得记录数。
df = pd.read_parquet('tripdata',\
columns=['year','month','tripduration'])
df.tripduration /= 60
dur = df.groupby(["year", "month"]).agg('median')
dur.unstack().round(2) # just to display

按年份和月份划分的旅行持续时间中位数(分钟)
然后我可以创建一个类似于第一个的图表:
dur.reset_index( level = ['year','month'], inplace=True)
sns.set(font_scale = 1)
plt.figure(figsize=(12,5))
ax=sns.barplot(data=counts, x="month" , y='tripminutes', hue='year', palette='Paired' )
ax.set_xlabel("Months")
ax.set_ylabel("Trip Duration in Minutes")
ax.set_xticklabels(months)
ax.legend_.remove()
y=0
for p in ax.patches:
ax.annotate(dur.year[y], (p.get_x()+p.get_width()/ 2, 0),\
rotation=90, ha='center', size=12, fontweight='bold',\
xytext=(0, 10), textcoords='offset points')
y+=1
使用中位数而不是平均值确实表明,从 2019 年到 2020 年,旅行持续时间增加了。然而,这种增长在夏季比在 12 月更加明显。

按年份和月份划分的旅行持续时间中位数(分钟)
事实上,如果我看一下从 2019 年到 2020 年的百分比变化,我看到 12 月的变化仅为 18%。
dur.unstack().pct_change().round(2)[1:] * 100

2019-2020 年中值出行持续时间的百分比变化
结论
使用 Pandas 和 Seaborn,我可以进行与商业产品相同的分析和可视化。通过用这些库分析数据,任何人都可以复制我的结果,并做他们自己的额外分析。
在 2020 年春天最初的封锁期间,疫情导致花旗自行车使用量大幅下降。乘客量在夏季反弹,并在秋季超过了去年的使用量。
工作日的使用已经发生了变化,高峰时段的高峰值较低,而下午和晚上的乘客较多,这表明从通勤到休闲骑行的转变。
用图论探索氦网络
原文:https://towardsdatascience.com/exploring-the-helium-network-with-graph-theory-66cbb8bffff9?source=collection_archive---------10-----------------------
使用区块链数据提取关于网络覆盖和“可疑”热点的见解

照片由阿丽娜·格鲁布尼亚在 Unsplash 上拍摄
氦网络快速入门
本文假设您对氦区块链有一些基本的了解,特别是它专门构建的工作算法,称为覆盖证明。从本质上讲,网络中的每个无线电台都会定期广播“信标”,也就是加密数据的小数据包。任何“见证”(接收)该传输的热点在收据中将其报告给区块链。附近热点之间的这种持续通信有助于证明在给定的地理区域中提供了足够的覆盖,并且矿工以诚实的方式提供这种服务会得到奖励。我鼓励你阅读更多关于氦的文档。
数据集管理
你在这里看到的所有数据都是通过向公开可用的氦区块链 API 发出请求而生成的。开源分类账提供了容错和可审计性,但它们也是数据挖掘的绝佳数据集。以下是我用 Python 中的requests库实现的一些 API 调用示例:
什么是图?
通常,我们选择表示数据集的方式与数据本身一样重要。一些输入,如图像和金融时间序列,适合定义明确的表示,如数组、向量和张量。由于许多 ML 模型,如卷积神经网络,严重依赖于线性代数,我们通常更喜欢类似矩阵的数据结构。然而,对于许多复杂的应用,比如药物发现、推荐系统,当然还有 【无线网络】 我们会受益于少一点的结构。
图是由离散点定义的数据结构,称为节点,节点间邻接关系的表达式,称为边。例如,您可以生成分子图,其中节点是单个原子,边是它们之间的键。出于我们的目的,我们可以将氦网络视为一个巨大的图形,具有 200,000+个节点(热点)和数百万条边(见证路径)。幸运的是,有许多开源库可以帮助构建和分析图表;在本文中,我将使用 Python 模块 NetworkX 和 PyTorch Geometric 。

给定氦热点的目击者示例(图片由作者提供)。
为城市生成氦网络图
下面的代码片段定义了CityGraph类,我们将使用它来生成给定城市的 NetworkX 图。首先,我们为每个热点创建一个节点,包括它们的位置、海拔、天线增益和最近收入等特性。每个节点的索引是其唯一的热点地址。接下来,我们遍历每个热点的所有最近见证收据,并在该节点及其唯一见证之间创建边。我们甚至可以对边缘应用权重,例如关于信号强度的度量(例如 RSSI 或信噪比)或数据包所报告的传输距离。
然后我们可以用nx.draw()函数来可视化图形。下面是宾夕法尼亚州州立大学氦网络的一个例子:

宾夕法尼亚州州立大学氦网络图。节点代表单个热点,边代表见证路径(即热点可以“听到”彼此)。“孤狼”无法证明他们的覆盖面,因此,赚取最低限度的奖励(图片由作者提供)。
特征提取:PageRank 和中间中心性
一旦我们有了这种表示,我们就可以利用数十年的图论研究来提取关于数据集的见解。原本为完全不同的领域开发的算法,甚至可能适用于我们对无线网络的分析。举个例子, PageRank 的度量是在 1999 年设计的!)作为搜索引擎找到与给定查询最相关的网站的一种方式。在这些引用图中,单个站点是节点,站点之间的超链接表示相邻关系。最有影响力的网站是用户最终最常浏览的网站。这与物联网网络有什么关联吗?让我们使用plotly来看看匹兹堡氦网络的 PageRank 值。

Pittsburgh 的氦网络存在,用 PageRank 评分着色(图片由作者提供)。
亮黄色的节点是 PageRank 分数最高的节点,而深蓝色的节点分数较低。有很多见证人当然有助于你的 PageRank 值,但不是唯一的因素。该算法还考虑了任何连接节点的相对影响,因为这是流量流向该枢纽的指标(例如,来自高价值网站(如 CNN.com)的流量比来自我的博客的流量加权更大)。另一方面,热点位于最密集的区域,但性能相对低于其邻居,被 PageRank 分数视为无关紧要。
另一个潜在的相关度量被称为https://en.wikipedia.org/wiki/Betweenness_centrality(或简称为“中间性”)。在这种情况下,该算法使用最短路径来识别有影响的节点。你可以把这个应用到航空旅行上。由于位于美国大陆的中心位置,芝加哥奥黑尔是东西和南北航班的常见中途停留目的地。中间性也被用来识别对病毒趋势影响最大的 reddit 社区,因为这些社区在看似不同的主题之间提供了最直接的联系,如政治和流行文化。同样,让我们来看看这个指标,因为它与匹兹堡的氦网络有关。

匹兹堡的氦网络,由中间中心性着色。
具有最高介数分数的热点是那些唯一连接地理上稀疏的区域的热点,例如那些在匹兹堡市中心和南部丘陵之间提供唯一见证路径的热点。我认为这些热点是网络的关键;虽然他们可能没有最高的见证人计数,但他们执行关键的覆盖范围证明。
离群点检测:图形神经网络
让我们把这些概念和一些深度学习结合起来。“图形神经网络”(GNNs)包含 DL 模型的一个子集,旨在从基于图形的数据集中提取洞察力——你猜对了。就像基于图像和 NLP 的人工智能一样,研究人员不断推出 GNN 算法、优化器和激活函数的新变体,以适应各种任务。与其纠缠于方法本身,我想把重点放在我们将如何把它们应用到我们的数据集上,在其他地方会有更好的解释。
在氦网络中,工程团队面临的首要挑战之一是识别出可能会欺骗系统以增加奖励的坏演员。这种游戏可以采取多种形式,但最流行的方法之一是让热点看起来比实际情况更分散。通过稀疏放置,矿工受益于最佳奖励比例,因为覆盖证明抑制了密集热点。虽然网络使用无线电传播的物理限制来剔除一些不诚实的活动(即,在给定距离内传播的信号强度有一个上限),但在许多情况下,绕过这些相对保守的阈值并不是非常困难。深度学习的非线性本质意味着决策边界更难预测,因此可能更难规避。
回到模型本身。我们的输入将是我们之前收集的城市图表。在我生成这个数据集时,热点分布在全球超过 11,000 个独特的城市,这些城市的规模、地理特征和监管条件各不相同。对于每个热点,我们将使用 PageRank 分数和中间中心性作为特征,并且每个见证路径将使用所报告的信标的 RSSI 和 SNR 作为边权重。最后,我们的模型的输出将是每个节点的奖励等级值的回归预测。你可以在这里 下载完整数据集(存储为泡菜二进制) 。
我的想法是,我们的模型应该概括一个“名义上”的城市是什么样子,但它将很难对异常情况进行分类。换句话说,在给定特定特征集的情况下,当奖励等级可疑地高(或低)时,我们的模型将显示高损失(以预测和实际之间的均方误差衡量)。说到模型,这里是我们的 GNN 的代码,使用 PyTorch Geometric 的 GCNConv 层和 Adam 优化器:
这将打印出我们测试集中损失最高的城市和单个热点,因为我们是在逐个节点的基础上进行预测的。让我们使用氦探测器来仔细看看我们的一些异常值:**
离群城市#1:威尔士卡迪夫

卡迪夫无可挑剔的热点(图片由作者提供)。
离群城市#2:佛罗里达州珊瑚角

另一个沿海城市佛罗里达州珊瑚角令人印象深刻的信号传播(图片由作者提供)。
你可以看到这个模型在暗示什么。在这两个地势低洼的沿海城市,大多数热点都有接近最优的回报规模和令人印象深刻的信号传播。有可能这里发生了一些欺骗,或者这只是无线电波在水上传播的人为现象。在未来的模型中,我想考虑地理因素,如当地的海拔和重要的水体。
异常热点#1:微小的橘子酱山鸡

图片作者。
离群热点#2:城市松鸦

图片作者。
离群热点#3:方方正正的棕色变色龙

图片作者。
现在,我们正在查看损失最高的个热点。在这种情况下,模型捕捉的情况是热点位于人口相对密集的区域(即附近有许多其他矿工),但他们仍然管理着完美的传输规模。我们在这里看到的另一个现象是一些非常远的见证路径,大约数百公里。也许这些热点只是拥有极其强大的天线和绝佳的位置,或者这是可疑活动的迹象。**
结束语
在本文中,我们利用图论来提取关于氦网络的见解。我们发现,像 PageRank 和 Betweenness 这样的指标,最初是为完全不同的应用领域设计的,对于在给定的城市中找到最重要(和最不重要)的热点有一定的相关性。利用这些特征和最先进的基于图形神经网络的模型,我们可以开始处理识别氦区块链上“可疑”活动的艰巨任务。我期待着进一步优化模型架构和特征提取方法,以及将分类任务扩展到其他领域,如覆盖范围映射。在短短几年的时间里,氦网络已经发展成为地球上最大的公共 LoRaWAN 网络。对于那些对去中心化和廉价物联网覆盖感到兴奋的人们来说,这已经是一个巨大的胜利。对于试图对这个庞大的(公开可用的)数据集建模的数据科学家来说,工作才刚刚开始。
参考
氦白皮书
NetworkX 文档
PyTorch 几何文档
城市图表数据集(111 MB)
用 Python 探索增长的极限
原文:https://towardsdatascience.com/exploring-the-limits-to-growth-with-python-674133874eed?source=collection_archive---------18-----------------------

“中国内陆工业城镇”——Jerry Zhang摄影
一瞥py world 3&py world 2
梅多斯等人写的《增长的极限》一书于 1972 年出版,因其对当前世界进化的可持续性的惊人预测而享誉世界。然而,它很快就从人们的记忆中消失了,可能是因为它的结论受到了很多不好的批评,被认为过于悲观和以末日为导向。另一个可以理解的原因是,所有科学证据的核心依赖于一个名为 World3 的独特模型的模拟,而这个模型乍一看非常复杂。事实上,在一个人能够合理地掌握定义世界 3 的整个系统方法之前,需要花费很多时间:除了阅读这本书之外,还需要一些额外的努力。
尽管如此,增长极限中的主张显示了兴趣的复苏,特别是因为不同的有形信号严重质疑我们今天生活的世界的可持续性。它可能涉及、新冠肺炎疫情及其对经济和物理流量的所有影响、气候趋势,或人类世指标的演变,仅举几例。今天,甚至欧洲环境署也是正式的,并承认在物理和系统的限制下,现在-如果不发生以后的情况下,走向“没有经济增长的增长的道路是必要的。经常性 研究 显示我们从现实世界状态中所能衡量的东西与世界 3 情景的变量极度吻合,导致超调和崩溃:
- 零政策情景称为一切如常,或者
- 假设经济增长仍然是一个目标,在这种情况下,密集的技术发展旨在抵消所有不良影响的持续增长。
除了这些事实之外,似乎比以往任何时候都更重要的是,这个在系统动力学框架下定义的复杂模型,应该更容易被任何人理解和理解。
深入模型
由于 World3 现有的不同版本和实现,新手可能会迷失在选择正确的版本来学习 T2 的道路上。
首先注意在模型参数和结构方面存在 4 个不同版本。每当梅多斯等人出版一本书时,他们都会重新校准模型,使其与当时的真实世界相匹配。
其次,不同的软件实现可以在网上找到。众所周知的 world 3–03 实现(2004 年至今的最新版本)使用带有图形用户界面的软件工具:
- Stella 上的官方实现——CD随 2004 年的书增长的极限:30 年更新一起提供,但现在似乎脱销了;
- ****针对 Vensim 和 Modelica 的非官方实现。
请注意,第一个实现是在技术书籍《有限世界中的增长动力》中提供的,它是一种叫做 DYNAMO 的语言的源代码。然而,由于它对系统动力学和当时使用的计算机硬件有很强的特异性,DYNAMO 没有成为一种流行的语言,现在已被弃用。

World3 有 4 个版本,2 个正式实现。从左至右,出版于 1972 年、1974 年、1992 年和 2004 年。图片作者。
我做了什么,为什么
尽管该模型取得了成功,但在当今的主流科学编程语言中,World3 的源代码却少得惊人。我找到了这些:
- 在 Javascript 中,由布莱恩·海因斯编写;
- 同样的 Javascript 代码是用 Python 包装的,但是具有因果推理和强化学习的特定特性;
- 一个由 INRIA 制作的 Python 版本,基于由 PySD 翻译的 Vensim 模型。
出于研究目的,最后一个选项似乎很有趣,但是源代码仍然很难理解。这就是为什么我更喜欢从零开始实现 World3,因为我想要一些东西:
- 基于技术报告*中给出的对原迪纳摩脚本的直接翻译【有限世界中的增长动力】,*
- 能够运行不同的可配置场景,
- 在广泛使用的编程语言中,
- 希望尽可能的清晰最小!

PyWorld3 在 GitHub 上有售。图片作者。
我最终选择了具有最小依赖性的 Python 3 代码。它不是纯粹的 Python,因为它从库调用函数来轻松处理数组操作和插值函数。该代码可在 GitHub 上获得。
使用 PyWorld3 或 PyWorld2
PyWorld3 可以通过命令pip install pyworld3安装。由于参数和功能的不同性质,模拟设置分为 5 个部分。所有默认变量返回正常业务场景:
from pyworld3 import World3world3 = World3()
world3.init_world3_constants()
world3.init_world3_variables()
world3.set_world3_table_functions()
world3.set_world3_delay_functions()
world3.run_world3()
如下图所示,模拟输出与显示污染、人口、食品、工业产出和剩余不可再生资源的原始图相当吻合。

World3 &“照常营业”的场景:将 PyWorld3 与原始印刷品进行比较(有限世界中的增长动力学,1974)。图片作者。
可以通过指定设置功能的输入来配置模拟。例如,增长极限的第一个结论是,不可再生资源存量翻倍只会导致更严重的污染危机。它在书中被描述为第二种分析场景,并且可以通过简单地改变第二行以使不可更新的资源初始常数nri加倍来获得模拟:
world3.init_world3_constants(nri=2e12) # default is 1e12
随着nri的逐渐增加,污染很快就超过了极限,人口的下降速度变得越来越重要,超过了之前的增长率。乌戈·巴尔迪将这种轨迹称为塞内卡效应,这是对塞内卡名言的回应:“财富增长缓慢、但毁灭迅速。请注意,塞内卡效应也出现在食品和工业产出库存的演变上。

所谓的“污染危机”情景:增加初始不可再生资源存量放大了塞内卡效应对人口和食物的影响。图片作者。
在 PyWorld3 的基础上,剩下的所有场景应该都是可行的。从教育的角度来看,这可能是一个有趣的项目。对于系统动力学中更柔和的学习曲线,我建议从 PyWorld2 开始,我之前的项目是 Forrester 的 World2 模型的 Python 实现。如下所示,计算出的变量揭示了类似的过冲和坍缩演化,尽管 World2 比它的继任者 要简单得多。关于《世界 2》历史的更多细节,请参见优秀的 A .米格南的博文。

world 2 & py world 2 的“照常营业”场景。与原始照片相比。变量名称对应于自然资源(NR)、生活质量(QL)、资本投资(CI)、污染率(POLR)。图片作者。
关于您应该考虑哪种实现的最后一点说明
当我决定编写 py world 3——和 py world 2——时,我想要一个没有不必要的复杂性的代码,用于教育或研究目的。它可能满足你的需求,但它应该取决于你真正需要做什么。
如果你有机器学习或统计分析的想法,从源代码实现开始似乎是一个不错的选择,因为你可能需要高效的函数调用,而避免了包装第三方软件的繁琐任务。然而,如果你想从头开始设计你自己的模型,我绝对不建议你用文本编辑器来开始你的项目,除非你喜欢头痛…用顺序编程范例来编码一个系统动力学模型会变得很麻烦,因为所有的模型方程都是相互依赖的。相反,任何具有图形单元界面的软件都有助于关注模型的设计。
如果您需要两者兼得,那么用 Vensim 设计以方便使用,然后用 PySD 自动移植到 Python 以保持效率,似乎是理论上最直接的解决方案。
一些参考
- 梅多斯、多内拉·h 等人《增长的极限》,1972 年。
- 丹尼斯·l·梅多斯等人有限世界中的增长动力学,1974 年。
- 巴尔迪,乌戈。塞内卡效应,2017。
- 海因斯布莱恩。 计算与人情(视频+幻灯片),2012。
- 米格南阿诺。 World2 模型,从迪纳摩到 R ,2020。
RGB 和灰度图像如何在 NumPy 数组中表示
原文:https://towardsdatascience.com/exploring-the-mnist-digits-dataset-7ff62631766a?source=collection_archive---------4-----------------------
让我们从图像基础开始

(图片由作者提供,用 draw.io 制作)
今天,你将学习机器学习和深度学习中一些最重要和最基本的主题。我保证今天的内容将提供一些对开始学习深度学习(机器学习的一个子集)至关重要的基本概念。
首先,我们将开始描述图像基础知识,如像素、像素值、图像属性以及 RGB 和灰度图像之间的差异。然后我们将讨论如何在 NumPy 数组中表示这些图像。
作为奖励部分,我们将探索包含数千幅手写数字灰度图像的 MNIST 数字数据集(见底部引文)。这将有助于你进一步澄清你在图像的数字表示中学到的东西。
因此,今天的内容将分为两个主要部分:
- 图像基础——关键组件和表示
- 近距离观察 MNIST 数据集(可选)
我们从图像的基础开始。
图像基础
像素
图像由微小的方形元素组成,这些元素被称为像素。即使是一个小图像也可能包含数百万个不同颜色的像素。
图像属性
每个图像都有三个主要属性:
- 尺寸 —这是一幅图像的高度和宽度。它可以用厘米、英寸甚至像素来表示。
- 色彩空间 —例如 RGB 和 HSV 色彩空间。
- 通道 —这是色彩空间的一个属性。例如,RGB 颜色空间有三种类型的颜色或属性,称为RedGreen和Blue(因此得名 RGB)。
RGB 图像与灰度图像
区分 RGB 图像和灰度图像非常重要。RGB 图像有三个颜色通道:红色通道、绿色通道和蓝色通道。但是,灰度图像只有一个通道。
像素值
图像的颜色由其像素值表示。一个像素只能有一种颜色,但可以合并以创建多种颜色。
在只有一个通道的灰度图像中,像素值只有一个从 0 到 255(包括 0 和 255)的数字。像素值 0 表示黑色,像素值 255 表示白色。灰度图像中的像素值可以表示如下:
[40]
由于值 40 接近 0 而不是 255,所以像素的颜色也接近黑色!
在有三个颜色通道的 RGB 图像中,像素值有三个数字,每个数字的范围从 0 到 255(包括 0 和 255)。例如,红色通道中像素的数字 0 表示该像素中没有红色,而数字 255 表示该像素中有 100%的红色。这种解释也适用于其他两个通道中的像素。RGB 图像中的像素值可以表示如下:
[255, 255, 0]
这个像素值代表黄色。看下图。

图片来自 Pixabay 的
黄色由绿色和红色组成。不涉及蓝色。这就是为什么像素值[255, 255, 0]代表黄色像素——红色 100% (255),绿色 100% (255),没有蓝色(0)!
深度学习中的图像表示
在机器学习和深度学习中,图像被表示为 NumPy 数组。在深度学习的上下文中,那些 NumPy 数组在技术上叫做 张量 ( 学习创建类似 NumPy 数组的张量)。
当在 NumPy 数组中表示图像时,大多数人可能会感到困惑,因为涉及到多个维度。基本上,维度的数量由以下因素决定。
- 图像尺寸 —总是需要两个尺寸来表示图像的高度和宽度。
- 颜色通道
- 图像数量
作为 NumPy 阵列的灰度图像表示
一个单个灰度图像可以用一个二维(2D)数字阵列或一个张量来表示。因为在灰度图像中只有一个通道,所以我们不需要额外的维度来表示颜色通道。这两个维度代表图像的高度和宽度。
一批3 幅灰度图像可以使用三维(3D)数字阵列或张量来表示。这里,我们需要一个额外的维度来表示图像的数量。

(图片由作者提供,用 draw.io 制作)
上面三幅灰度图像的批次的形状可以表示为:

(图片由作者提供,用 draw.io 制作)
作为 NumPy 数组的 RGB 图像表示
一个单个 RGB 图像可以使用三维(3D)数字阵列或张量来表示。由于 RGB 图像中有三个颜色通道,我们需要一个额外的颜色通道维度。
三幅 RGB 图像的批次可以使用四维(4D) NumPy 数组或张量来表示。这里,我们需要一个额外的维度来表示图像的数量。

(图片由作者提供,用 draw.io 制作)
上述 3 幅 RGB 图像的批次的形状可以用两种不同的方式表示:
- 通道-最后:这将把颜色通道轴放在最后。这是 TensorFlow (Keras)和 OpenCV 中的标准。
- 通道优先:将颜色通道轴放置在采样轴之后。

(图片由作者提供,用 draw.io 制作)
要点:通道-最后符号是将 RGB 图像表示为 NumPy 数组的标准符号。
图像基础及其数值表示的足够理论。让我们开始探索 MNIST 数字数据集。
近距离观察 MNIST 数据集(可选)
由 NIST (美国国家标准与技术研究院)构建的 MNIST 数字数据集(见底部引文),是学习深度学习的经典数据集,也是通用机器学习的经典数据集。它已经被机器学习和深度学习社区过度使用了。然而,仍然值得探索和使用这个数据集,特别是如果你打算第一次接触深度学习的话。
MNIST 数据集包含 10 个类别(0 到 9)下的 70,000 幅手写数字的灰度图像。我们将使用两个流行的 API 来加载数据集: Keras API 和 Scikit-learn API 。两者都提供了用于轻松加载 MNIST 数据集的实用函数。我们还将讨论 MNIST 数据集的两个 API 之间的差异。
使用 Keras API 加载 MNIST 数据集
from tensorflow.keras.datasets import mnist(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
获取数据的形状
print("Train images shape:", train_images.shape)
print("Train labels shape:", train_labels.shape)
print("Test images shape: ", test_images.shape)
print("Test labels shape: ", test_labels.shape)

(图片由作者提供)
成批的训练和测试图像是三维的。这是因为 MNIST 数据集包含灰度图像,而不是 RGB 图像。(60000,28,28)表示训练图像集包含 60,000 张 28 x 28 像素的图像。换句话说,它是一个包含 60,000 个 28 x 28 整数值的矩阵的数组。
训练和测试标签是一维的。它们包含 10 个类别(0 到 9)的相应标签。
import numpy as npnp.unique(test_labels)
#OR
np.unique(train_labels)

(图片由作者提供)
获取数组类型
print("Train images type:", type(train_images))
print("Train labels type:", type(train_labels))
print("Test images type: ", type(test_images))
print("Test labels type: ", type(test_labels))

(图片由作者提供)
都是 NumPy 数组。
获取数据类型
print("Train images data type:", train_images.dtype)
print("Train labels data type:", train_labels.dtype)
print("Test images data type: ", test_images.dtype)
print("Test labels data type: ", test_labels.dtype)

(图片由作者提供)
像素值的数据类型为“uint 8”,表示 8 位整数。
从列车图像集中获取单张图像
可以使用以下符号访问训练集中的单个图像:
train_images[i]
该索引从 0 开始。为了得到第 10 幅图像,我们应该使用 i=9。
train_images[9]

数组的一部分(图片由作者提供)
train_images[9].shape
这是一个 28 x 28 的灰度图像矩阵。
可视化单幅图像
我们将可视化训练数据集的第 10 幅图像。
import matplotlib.pyplot as plttenth_digit = train_images[9]
plt.imshow(tenth_digit, cmap="binary")
plt.axis("off")

(图片由作者提供)
训练集的第 10 个图像代表数字 4。通过看其对应的标签来看是否正确。
train_labels[9]
这也返回 4。因此,图像与其标签是对应的。
使用 Scikit-learn API 加载 MNIST 数据集
from sklearn.datasets import fetch_openmlmnist = fetch_openml('mnist_784', version=1)X, y = mnist['data'], mnist['target']
获取数据的形状
print("X shape:", X.shape)
print("y shape:", y.shape)

(图片由作者提供)
单个图像的像素值排列在大小为 784 的一维向量中,该向量等于 28×28。这意味着我们仍然可以像在 Keras API 中一样对矢量进行整形,以获得图像所需的格式。
获取数组类型
print("X type:", type(X))
print("y type:", type(y))

(图片由作者提供)
获取数据类型
print("X data type:", X.dtypes)

(图片由作者提供)
print("y data type:", y.dtypes)

(图片由作者提供)
MNIST 数据集的两个 API 之间的差异
- 训练和测试集:在 Kears 中,MNIST 数据集可以加载训练和测试集。但是在 Scikit-learn 中,我们需要手动创建训练和测试部件。
- Shape: 在 Keras 中,单个 MNIST 数字由大小为 28 x 28 的二维 NumPy 数组表示。在 Scikit-learn 中,单个 MNIST 数字由大小为 784 的一维 NumPy 数组表示。我们需要明确地将数组重新整形为 28 x 28 的数组。
- 数组类型:在 Kears 中,图像和标签由 NumPy 数组重复。在 Scikit-learn 中,图像由熊猫数据帧重复,而标签由熊猫系列表示。
- 数据类型:在 Kears 中,像素值和标签的数据类型为“uint 8”,表示 8 位整数。在 Scikit-learn 中,像素值的数据类型为 "float64" ,标签的数据类型为" category ",在用于以下算法之前,应将其转换为 "uint8" 。
y = y.astype(np.uint8)
重要提示:如果你使用 MNIST 数据集进行深度学习,我建议你使用 Keras API 加载数据。
阅读下一篇(强烈推荐)
学习创建类似 NumPy 数组的张量。
https://medium.com/geekculture/lets-create-tensors-like-numpy-arrays-90a4cf32144
学习 NumPy 基础知识和数组创建。
像专家一样分割 NumPy 数组。
https://rukshanpramoditha.medium.com/numpy-for-data-science-part-2-7399ffc605e5
阅读下一页(可选)
使用 PCA 压缩图像!
今天的帖子到此结束。
如果您有任何反馈,请告诉我。
同时,你可以 注册成为会员 来获得我写的每一个故事,我会收到你的一部分会员费。
非常感谢你一直以来的支持!下一个故事再见。祝大家学习愉快!
MNIST 数字数据集引用:
邓,2012 年。用于机器学习研究的手写数字图像 mnist 数据库。 IEEE 信号处理杂志,29(6),第 141–142 页。
MNIST 数字数据集来源:
http://yann.lecun.com/exdb/mnist/
MNIST 数字数据集许可证:
Yann le Cun(NYU 库朗研究所)和Corinna Cortes(纽约谷歌实验室)持有 MNIST 数据集的版权,该数据集根据知识共享署名-同样分享 3.0 许可证的条款提供。
鲁克山普拉莫迪塔
2021–12–04
通过单词袋的 2D 投影探索 YouTube 视频的脚本
原文:https://towardsdatascience.com/exploring-the-script-of-a-youtube-video-through-2d-projections-of-its-bags-of-words-6502bce920d8?source=collection_archive---------42-----------------------

我在本文中探索的主要管道。作者 Luciano Abriata 图。
视频脚本的自然语言处理
对从脚本中计算出的单词包应用 PCA,以在 2D 视图上传播视频信息。
最近,我开始玩 YouTube 视频的脚本,这些脚本由创作者随视频一起上传,或者由网站通过语音识别系统自动转录。目前,我正在寻找以图形方式显示视频内容的方法,这种方式允许我快速浏览其内容,而不必观看全部内容。从长远来看,我的目标是建立一个完整的“视频脚本浏览器”,你可以在线使用它来快速浏览视频的不同部分——请继续关注,因为这将是一个有趣的项目,而且可能也很有用!
目前,我已经取得了一些有趣的进展,我将在这里分享。这都是手动步骤,所以目前没有代码。简单地说,在这里我向你展示(1)如何获得 YouTube 视频的脚本,(2)通过删除停用词、符号等来清理句子的内容。(3)重新格式化数据,使句子的大小适合分析,(4)将句子转换成数字,以及(5)最后对这些数字应用 PCA,以显示结果。这是一个非常简单的方法,但结果至少在一个 50 分钟长的视频脚本上是有意义的,该视频呈现了关于一个相关主题的 3 个不同的故事。
我希望这篇文章将教会你一些基本的东西,并作为我进行更高级分析和我自己未来的视频脚本浏览工具的垫脚石。
1.检索 YouTube 视频的脚本
并非所有的 YouTube 视频都有可用的脚本。如果是这样,当你点击视频右下角的三个点时,你会看到“打开记录”:

如何检索 YouTube 视频的脚本(如果它可用的话)(这是指内容创建者上传它或系统可以自动转录它的时候)。作者 Luciano Abriata 图。
您可以选择所有的脚本文本,并将其粘贴到您最喜爱的程序。当你粘贴文本的时候,你会看到这导致了一个时间索引和文本的交替行的单列,并且有相当多的垃圾在那里。因此,你当然需要进行一些清理。
注意:有几种编程方法可以获得 YouTube 视频的脚本,但我发现没有一种方法能在所有视频上一致地工作。
2.通过删除停用词、符号等清理脚本内容。
你可以看到剧本被剥离成非常小的“句子”。在我分析的视频中(不是上图中显示的那个),我得到了 2032 行,这实际上意味着 1016 行原始文本。这是来自我国一个常规电视节目的 50 龙敏视频。
很多句子其实根本没有任何内容,只是表示视频的一段话是“[音乐]”之类的标签。我删除了这些行以及所有的符号、数字和 3 个或更少字符的单词,它们主要是自动脚本生成过程中的连接和噪声,内容不多。
3.重新格式化一个脚本,以获得大小适合分析的句子
至此,我从脚本中提取了 996 行文本。你可以看到每一行都很小,包含 1 到 10 个单词。(我怀疑真正的限制是由字符数给出的,因为系统希望确保整个文本适合屏幕,但沉默或音乐的间隙也会产生较短的句子)。
事实上,这些原始文本行太短,无法进行分析。因此,我重新排列了我的 996 行,将每 12 行合并成一个句子。这意味着我现在有 83 行,每行包含 30 到 40 个单词。
这 83 行涉及 847 个单词(记得我已经清理了所有的停用词、短词、符号、数字等。).其中 75%在整袋单词中只出现一次,15%出现一次,4%出现三次,6%出现在 4 到 11 次之间。
4.将句子转换成数字
在这一点上,我从文字转移到数字。为此,我收集了这些单词,并计算它们在 83 个句子中出现的次数。这意味着我得到了每个句子的袋单词。在接下来的内容中,我坚持只包含那些出现 2 次或更多次的单词,这意味着 847 个单词的 25%(即 214 个单词)。
当然,大多数单词不会出现在任何一个句子中;然而,我准备单词和句子的方式暗示了每个单词将在至少一个句子中至少出现一次。因此,我得到了一个看起来由零支配的矩阵,但实际上在其所有行和列中至少有一个大于 0 的数。
过滤了总数为 2 或更多的单词,有 83 个句子,此时我得到了一个 214 行(单词)和 83 列(句子)的矩阵。下面是该矩阵的表示,其中所有 0 都被删除,任何大于 0 的数字都被视为黑点:

每个句子(列)中每个单词(行,仅包括总计数为 2 或更多的单词)的计数矩阵的非零元素。作者 Luciano Abriata 图。
你可以看到点的密度随着你的下降而增加。这是因为行(词)是按总出现次数递增排序的。
现在我们有了数据的数字表示(是的,我知道这很简单,肯定有很多问题,但这只是一个开始),我们可以开始处理它的有趣部分了。
5.对数据应用 PCA 并解释结果
第一次 PCA 尝试
对上面的矩阵应用简单的 PCA 过程已经给出了一些有意义的结果。为了有助于理解,我利用了这个事实,即视频呈现了关于一个共同主题的 3 个不同的故事:这 3 个故事都是关于绘画的,但每个故事都专注于不同的画家,他们分别接受了采访。在下一张图中,您可以看到上面准备的输入矩阵,但它是按故事编号着色的,然后是 PCA 图,其中每个点(句子)都是根据从中提取的故事的颜色着色的。

左图:与上图相同的矩阵,但是用类型(故事)给物体(句子)着色。右图:PCA 过程的结果,用与每个点所属的故事相对应的颜色给每个点着色。我用我在这篇 TDS 文章中解释的在线工具运行了 PCA。作者 Luciano Abriata 图。
你可以看到故事 1 和故事 3 是如何分开的,尤其是在 PC2 部分。相反,故事 2 大致处于中间位置。
玩不同的 PCA 运行
选择频率较高或较低的单词有什么影响?在我的测试中,只考虑在整个文本中出现两次的单词的 PCA 不会产生任何清晰的数据分布。同时,仅对总共出现 5 次或更多次的单词(26 个单词)运行 PCA 产生红点与绿+蓝点的更好的分离(故事 1 对故事 2 + 3):

根据矩阵计算出的主要成分图,只包含故事中出现 5 次或更多次的单词的数据。我用我在这篇 TDS 文章中解释的这个在线工具运行了 PCA。作者 Luciano Abriata 图。
最有趣的是,负载图解释了在 PC 图中,哪些词在点的分离中的权重更大:

沿着主成分 1、2 和 3 的 26 个单词中的每一个的系数(“负载”)。我用我在这篇 TDS 文章中解释的这个在线工具运行了 PCA。作者 Luciano Abriata 图
在这样的图中,每个主成分(这里显示了前三个)的每个输入变量(这里是单词)有一个数字,正值和负值都很重要。最后一个变量中的正负峰值对应于单词“hotel ”,该单词总共出现了 11 次,但都出现在故事 1 中。通过观看视频,人们可以理解,整个故事 1 围绕着目前在酒店中展出的艺术品,采访本身就发生在酒店中,甚至涵盖了酒店是如何从废墟中再生的。
位置 6 处的负峰值对应于单词“黄色”。这个词一共被提到了 5 次,都是在故事 1 的一个句子里,讲的是故事拍摄时秋天的颜色。对一个句子如此强烈的敏感可能会被软化。特别是,这个故事并没有特别围绕秋天和它的颜色。
去掉“黄”字提高了一点传播范围,仍然以“酒店”为主:

与上面的分析相同,但省略了“黄色”一词。我用我在这篇 TDS 文章中解释的在线工具运行了 PCA。作者 Luciano Abriata 图
最后,删除“hotel”以将 PCA 过程的焦点转移到其他单词,这导致句子的传播减少,并且强调单词“look”、“painting”和“art”:

与上面的分析相同,但省略了“酒店”一词。我用我在这篇 TDS 文章中解释的这个在线工具运行了 PCA。作者 Luciano Abriata 图
结论
这个过程可能不是最好的,但是非常简单,而且它确实有传播剧本内容的能力,对只在一个故事中非常频繁出现的词特别敏感。如果做成一个交互式的 web 应用程序,用户可以动态地看到当单词被删除或包含时更新的结果,并且当他悬停在数据点上时可能会看到完整的句子,我认为这可能是一个非常强大的工具。
你怎么看?你对一个用来帮助检查视频脚本(或任何文本)内容的工具有什么期望?
所有的 PCA 运行都是使用我在本文中描述的工具进行的:
我是一个自然、科学、技术、编程和 DIY 爱好者。生物技术专家和化学家,在潮湿的实验室和电脑前。我写我广泛兴趣范围内的一切。查看我的 列表 了解更多故事。 成为中等会员 访问其所有故事和 订阅获取我的新故事 通过电子邮件 (我为其获得小额收入的平台的原始附属链接,无需向您支付特殊费用)。 通过各种方式在这里捐赠*。* 这里联系我 。
到 咨询关于小工作 (关于编程、biotech+bio info 项目评估、科学推广+交流、分子数据分析与设计、分子图形、摄影、分子网络教程、科学教学与辅导等。)查看我的 服务页面这里 。
达塔维尔的疫情:5 个让你惊讶的见解
原文:https://towardsdatascience.com/exploring-the-spread-of-covid-19-five-simulations-that-will-surprise-you-952d9e3ef752?source=collection_archive---------47-----------------------
模拟复杂网络中的流行病
数据科学家如何使用模拟来揭示不直观和有趣的见解

埃德温·胡珀在 Unsplash 上的照片
简介
更令人兴奋和有益的练习之一(在我看来)是探索。它通常会带来令人惊叹的非直觉见解,并告知您的受众,这是您作为数据科学家肯定想做的事情。
在本文中,我们将通过模拟探索一个虚构城镇中的疫情。继续阅读,揭示不那么明显的见解。
但是在进一步深入之前,重要的是读者要记住,模型是对现实的简化,并带有假设。
“所有的模型都是错误的,但有些是有用的”——乔治·博克斯
欢迎来到数据镇
达塔维尔是一个有一万人的小镇。这是一个紧密团结的社区,邻里之间的隔阂相对较少。最近有些居民染上了一种神秘的病毒。
这种病毒似乎通过密切接触在人与人之间传播。达塔维尔的人们很担心,新闻媒体警告说可能会有疫情。
Dataville 的顶级科学家分析了这种病毒,发现它对现有的抗体有抵抗力;没有人有豁免权。到目前为止,5%的 Dataville 居民已经被感染。
Dataville 的政府已经雇佣了数据科学家来模拟病毒的传播以帮助控制病毒。
工具
该模拟是使用 Jupyter 笔记本在 Python 中端到端构建的。我将把你链接到我的 GitHub 页面,在那里你可以抓取代码并自己使用它(见文章结尾)。
我已经在网络扩散库( NDlib )上建立了模拟——一个用于模拟、描述和研究复杂网络上扩散过程的框架。它非常直观,易于设置。我强烈推荐它。
建模方法
建模方法学不是最令人兴奋的东西,但是它很重要。我要你挑战这个模型,看看你能在哪里改进它。阅读时请记住这一点。
我对病毒及其传播网络做了几个简化的假设。
社交网络模型: 我曾经用一个数学图来表示 Dataville,它是市民和他们的关系。Dataville 的公民由图中的节点表示,他们与其他公民的联系/关系由边表示。

作者的图像:一个基本的数学图形
该病毒的传播是在一个由 10,000 人组成的复杂网络上模拟的,该网络被建模为 Barbassi-Albert 图。Barbassi-Albert 图是一种特殊的数学图,用于模拟优先连接。
优先连接意味着网络中的节点比具有更多边的节点更有可能具有边。这是社交网络中经常观察到的现象。简单来说,受欢迎的人有更多的朋友,但受欢迎的人不多。
为了使网络形象化,这里是每个节点的连接分布。你可能已经注意到了,它大致是一个幂律分布。

作者图片:节点边的分布
注意:如果你不熟悉图论,这里有一篇关于图的友好文章。
我假设网络是静态的,这意味着它的拓扑结构没有变化。事实上,社交网络是动态的。
最后,我假设在日常生活中,网络中的每个人至少与 10 个人有互动。
换句话说,Dataville 是一个小而繁忙的城镇。
状态和行为: 人可以存在三种状态之一:易感、感染或死亡。允许以下状态变化:
1。易感- >感染
2。感染- >易感
3。感染- >死亡
一个节点必须有一条边连接到一个被感染的节点才能触发状态改变一;一个明智的规则是,我们知道病毒通过密切接触传播。
病毒假设 : 病毒不会随着时间的推移而变异,传染性减弱或增强,人也不会产生免疫反应。
初始条件:初始感染率为 5%。
死亡率:一个人一旦被感染,有 2.86%的几率死于该病毒。
恢复率:假设为 1-死亡率。
传播率:一旦感染,传播病毒的概率为 6.4%。
模拟
现在,所有的假设都已经准备好了,我们可以模拟一些病毒的场景了。
重要提示 —所有模拟都运行 1000 次以上。这是一个任意的时间单位,不应假设为天、月、秒等。
场景 1:如果我们让病毒自生自灭会怎样?
Dataville 的许多人认为我们应该让病毒自生自灭。毕竟,与埃博拉相比,死亡率相对较低。
他们指出,社会限制对经济的影响会对精神健康、身体健康产生负面影响,并且会比病毒更具毁灭性。
让我们模拟一下,看看我们能收集到什么信息。

场景 1:作者的图像
经过 1000 次迭代后,感染病毒的人的比例降至零。然而,这种病毒已经摧毁了我们的人口,大约 35%的人死亡。
因此,在没有免疫力的人群中不受限制地传播病毒似乎不是最佳解决方案,即使死亡风险似乎很低。
场景 2:如果病毒像埃博拉病毒一样致命会怎样?
Dataville 的科学家警告说,这是一种死亡率类似于埃博拉病毒的新病毒株。
据估计,埃博拉病毒的致死率约为 50% ( 世卫组织)。让我们把死亡率调整到 50%,重新运行模拟。

场景 2:作者的图像
正如你可能预料的那样,这种更致命的形式杀死了我们人口中相当高的比例(大约 50%)。有趣的是,受感染的人数很快变平为零(在不到 50 次迭代之后)。因为死亡率很高,这种病毒不会传播很远。别忘了我们的模拟不允许死者传播病毒。
这种致命的病毒将是毁灭性的,但很可能会在消灭我们的人口之前自行消失。
情景 3:如果病毒传播性更强,会发生什么?
Dataville 的科学家推测,病毒在夏天变得更容易传播,因为热气流将它带得更远。
让我们通过将传输速率增加到 20%来对此建模。我们将把死亡率降低到基线值。

场景 3:作者的图像
增加的传输速率在较早的情况下导致尖峰,然后在大约 600 次迭代时变平为零。这种影响是毁灭性的,超过 70%的人口死于这种病毒。
场景 4:如果人们从病毒中恢复得更慢,会发生什么?
Dataville 的科学家发现,人们的恢复速度比最初想象的要慢得多。让我们把回收率降低到 10%。像往常一样,我们会将其他参数重置为基线。

场景 4:作者的图像
到目前为止,这是我们最具破坏性的场景,在模拟结束时,90%的人口死亡。
降低恢复率会导致感染的累积,在大约 30%的人口中达到峰值。这只是被高死亡率所抑制,在第 400 次迭代时感染的数量变为零。不好!
场景 5:疫苗的有效性如何?
Dataville 的政府正计划推出一种疫苗。据说它的有效率在 95%左右。
疫苗的工作原理是让接受者对病毒有一定的免疫力。有免疫力的人不太可能传播病毒。因此,在我们的模拟中,我们可以通过降低传播率来计算疫苗。我们将在场景 4 中这样做,这是我们最糟糕的情况,看看会有什么影响。

场景 5:作者的图像
结果令人难以置信。我们的人口死亡率从 90%下降到不到 3%。这种病毒在我们接种疫苗的人群中实际上不是问题。
最后的想法
这不是疫苗接种的宣传,这项活动的结果不应该太认真。毕竟这些模型过于简单,而且 Dataville 并不存在。
然而,我想你会同意模拟揭示了一些相当不直观的结果。例如,我们中有谁会想到较低的恢复率会产生如此毁灭性的影响——甚至比死亡率还要严重?
最终模拟复杂的现象可以帮助指导我们的决策。
我最希望你能看到模拟对探索这些现象的用处。
在下面的 GitHub 链接中,您可以自由地自己使用代码。
https://github.com/john-adeojo/Pandemic_Simulation https://www.linkedin.com/in/john-adeojo/
探索评估特征的各种方法以进行特征选择
原文:https://towardsdatascience.com/exploring-the-various-ways-to-evaluate-features-for-feature-selection-1142f7788aeb?source=collection_archive---------25-----------------------
概述用于为机器学习模型过滤掉不想要的特征的常用方法

照片由像素的皮克斯拜拍摄
特征选择是许多机器学习任务中的常见组成部分。
这是用于降低维数的方法之一,从而确保高模型性能,同时减少过拟合和计算需求。
无论您是在构建推理模型还是预测模型,通过首先验证您已经选择了用于训练模型的最佳功能集,您将获得更好的结果。
在这里,我将快速概述一些常见的方法,以确定最适合构建机器学习模型的特征。
特征选择的例子
有许多标准可用于决定保留或省略哪些功能。
将使用存储房价信息的数据集(无版权)演示每种类型的特征选择。数据集可在此访问。

代码输出(由作者创建)
此数据集中的目标标签是“property_value”。在功能选择过程中不会考虑此功能。
1.基于缺失值选择要素
有许多方法可以处理数据集中的缺失值。虽然删除没有数据的记录是一种选择,但不鼓励这样做,因为这意味着放弃有价值的信息。
也就是说,在某些情况下,如果大多数记录没有为某个要素赋值,则完全移除该要素可能是唯一的选择。
在 python 中,您可以轻松识别是否有要素缺少太多值。

代码输出(由作者创建)
如输出中所示,所有要素都没有任何缺失值,不需要消除任何要素。
2.基于方差选择要素
为了具有预测能力,要素的值需要显示某种程度的差异。
因此,评估特性的一个标准是特性值的方差。
方便的是,sklearn 模块提供了 VarianceThreshold ,这是一个基于给定方差阈值选择特性的特性选择器。
下面是如何实现 VarianceThreshold 的简单示例:

VarianceThreshold 在数据集中选择满足所需方差的 9 个预测特征,同时忽略不满足方差的 6 个特征。
3.基于与其他特征的相关性选择特征
对于构建推理模型,特征之间没有关系是理想的。强相关的预测特征只会产生更多的噪声,导致特征系数估计的更高的方差。
这将使得从基于这种模型的分析中获得洞察力变得更加困难。
预测要素与其他要素有很强相关性的现象称为多重共线性。我在这里给主题一个概述。
多重共线性可以通过首先识别表现出强相关性的要素来检测。
查找此类要素的一种方法是构建一个显示所有要素对的相关系数值的热图。

代码输出(由作者创建)
对于该数据集,很明显,以下各项之间存在很强的相关性:
- “房屋面积平方米”和“土地面积平方米”
- 土地面积平方米和房间数量
- “房屋面积平方米”和“房间数量”
直觉上,这些观察是有意义的。例如,“房子大小平方米”和“土地大小平方米”实际上给了我们相同的信息。包括这两个特征将降低用该数据训练的任何因果模型的可靠性。
要确定应消除哪些要素,我们可以找到具有高方差膨胀因子(VIF)值的要素并将其移除。
注:通常,10 或更大的 VIF 值被认为太高。

代码输出(由作者创建)
要素“land_size_sqm”具有最高的 VIF 值。移除此特征后,让我们再次计算 VIF 值。

代码输出(由作者创建)
很明显,“land_size_sqm”是唯一必须移除的要素,以解决数据集中的多重共线性问题。
4.基于模型性能选择特征
也可以让机器学习模型选择特征。
一种众所周知的利用模型的特征选择算法被称为递归特征消除(RFE)。
RFE 不同于其他特征选择方法,因为它特别要求应该选择的特征的数量。
令人欣慰的是,sklearn 模块带着它自己的 RFE 估计器再次前来救援。
在使用 RFE 进行特征选择之前,需要将数据分成训练集和测试集,并进行归一化处理。
让我们使用 RFE 来确定应该选择的 10 个特征。RFE 将使用线性回归模型来选择其要素。

RFE 的一个有价值的特征是,它可以显示从考虑因素中移除已消除要素的顺序。这可以通过。排名 _ '法。
注意:等级为 1 的特征是选定的特征。任何其他排名都代表被淘汰的功能。

代码输出(由作者创建)
如表中所示,RFE 删除了“无房间”、“大客厅”、“无浴室”、“房间大小”和“停车位”功能(按此顺序)。
结论

照片由 Unsplash 上的 Prateek Katyal 拍摄
现在,您已经更加熟悉了用于特征选择的各种方法。
请记住,特性选择的最佳方法因项目而异。
不要不分青红皂白地应用每一种特性选择方法,而要考虑哪一种最适合您的项目。
我祝你在机器学习的努力中好运!
参考
J.伊萨丁。(2020).Jiffs 房价预测数据集,第 3 版。2021 年 12 月 12 日检索自https://www . ka ggle . com/elakiricoder/jiffs-house-price-prediction-dataset。
探索国家的财富
原文:https://towardsdatascience.com/exploring-the-wealth-of-nations-22b33575cec9?source=collection_archive---------31-----------------------
强调国家财富和各种社会经济指标之间的关系
自亚当·斯密 1776 年出版《国富论》以来,解释《国富论》一直是讨论和研究的话题。美国国会于 1937 年引入了国内生产总值这一指标来衡量国家财富,自那以来,它已成为这方面的最终衡量标准。简单地说,国内生产总值(GDP)是一个国家在特定年份生产的所有商品和服务的总价值。这一衡量标准虽然详尽,但确实存在一些缺陷,一些经济学家将强调收入不平等等缺陷。尽管如此,GDP 仍然是量化和比较国家财富最常用的指标。为了使人口效应标准化,使用了人均国内生产总值指标,即国内生产总值除以国家总人口。为了进一步抵消不同货币汇率的影响,使用了购买力平价的概念。用购买力平价计算的人均 GDP 是比较各国财富的标准尺度。
国家的财富和个人的财富一样,与不同的因素有着非常复杂的关系。让我们先来研究这个反问句,即“金钱能买到幸福吗?”在国家的背景下。从国家层面来看,数据确实给出了明确的“是”的答案。我从纽约可持续发展解决方案网络发布的《2020 年世界幸福报告》中选取数据进行分析。这份报告使用盖洛普世界民意测验和劳埃德船级社基金会的世界风险民意测验来直接计算各国的幸福指数。该报告本身使用购买力平价将人均 GDP 确定为可以解释国民幸福指数差异的最大因素。为了进一步探索这一点,我用购买力平价绘制了幸福指数和人均 GDP 的图表。这两个变量有很强的相关性(皮尔逊相关系数为 0.75)。事实上,在我为这项分析考虑的所有变量中,国家财富与国家幸福指数的相关性最强。富裕国家的民众更幸福,或者可能更幸福。相关性不能预测因果关系,所以我们有一些食物(数据)供进一步思考。

作者图片
我还研究了与国家财富密切相关的其他变量。世界银行最近推出的一项综合指标是“营商便利度得分”[2]。该分数考虑了各种法律、监管和经济指标,根据各国创办和经营企业的难易程度进行排名。下图显示了这种关系。“人均 GDP”和“商业便利度”也有很强的相关性,相关系数为 0.64。在较富裕的国家创业更容易。低收入国家需要对此给予特别考虑。国家财富主要来自蓬勃发展的私营企业。商业改革促进了私营企业的发展,从而增加了国民财富。

作者图片
还有其他指标,如城市人口百分比,也与人均国内生产总值有很强的正相关关系(皮尔逊系数为 0.65)。在现代经济中,城市是经济增长的引擎,因此应促进并妥善管理城市化。有一些指标与国家财富有显著的负相关关系。其中一个是全国人口增长率,从下面的情节可以看出。

作者图片
这一分析绝非详尽无遗。指标的选择是任意的,未来的研究可以选择新的指标。地图下方的热图显示了各种国家社会经济指标之间的相互关系。除了与国家财富的相关性,在这张热图中还可以看到其他有趣的关系。

作者图片
例如,一个国家的平均预期寿命与电力供应高度相关。它还与全国人口抚养比(定义为每 100 名活跃劳动力中的被抚养者人数)呈高度负相关。这种影响很可能是由于每个家庭有大量的孩子和较高的婴儿死亡率。同样,人口增长率和人口抚养比与大多数经济发展指标呈负相关。这突出了低收入国家适当开发人力资源的必要性。总之,在公共部门领域有大量数据可供探索。这方面最好的资源是世界银行开放数据 API 3。
注:这项分析的代码可在以下网址公开使用:
https://github . com/janjuatest/Public-Sector/blob/main/GDP . ipynb
- Helliwell,John F .,Richard Layard,Jeffrey Sachs 和 Jan-Emmanuel 德·内维编辑。2021.2021 年世界幸福报告。纽约:可持续发展解决方案网络。
- 做生意,https://www.doingbusiness.org/en/rankings
- 指标数据来自世界银行公开数据 API:
https://Data help desk . World Bank . org/knowledge base/topics/125589
使用欺诈检测数据集探索 TigerGraph 的 GSQL ListAccum 和 HeapAccum
原文:https://towardsdatascience.com/exploring-tigergraphs-gsql-listaccum-and-heapaccum-with-a-fraud-detection-dataset-cf2bc2b0bb16?source=collection_archive---------42-----------------------
使用 AML Sim 欺诈数据库探索 TigerGraph 的 ListAccum 和 HeapAccum 的使用案例和差异
介绍
欢迎光临!在这篇博客中,我们将介绍两种类型的累加器:ListAccum 和 HeapAccum。这是 TigerGraph 的查询语言 GSQL 的几种不同类型的累加器中的两种。您可以在此处看到 TigerGraph 提供的所有累加器。本博客将使用 AML Sim 欺诈检测数据集介绍 ListAccum 和 HeapAccum 的一些用途。我们将了解每种方法的优势和使用案例,所以让我们开始吧!
列表累积
概观
一个 ListAccum 类似于 Python 中的一个列表。可以有重复,当一个项目被添加的顺序将是结果的顺序。总而言之,它是“元素的顺序集合”,再次类似于 Python 中的列表。让我们来看看它的一个用例。
查询一
让我们从简单的开始。比方说我想抓取某个用户的所有交易,看看是不是欺诈。为此,我们可以编写一个查询,该查询将从帐户顶点开始,经过 Send_Transaction 边,并在事务顶点结束。WHERE 子句将过滤结果,因此我们只查看某个帐户的交易(它将作为参数传递)。
CREATE QUERY listQueryOne(STRING account_id) FOR GRAPH AMLSim {
ListAccum<BOOL> @@fraud_tx;
Seed = {Account.*};
Res = SELECT t FROM Seed:a -(Send_Transaction:e)->Transaction:t
WHERE a.id == account_id
ACCUM @@fraud_tx += t.is_fraud;
PRINT @@fraud_tx;}
当传递 id 9755 时,结果将是一个包含几个真和假的列表。

查询的结果
查询二
列表的一个很酷的地方是它们可以很容易地添加到一起。比方说,我们想知道发送和接收的交易是否是欺诈性的。为此,我们可以创建另一个列表,然后将这两个列表“添加”在一起。
CREATE QUERY grabAllTransactionResults(STRING account_id) FOR GRAPH AMLSim {
ListAccum<BOOL> @@fraud_tx_send;
ListAccum<BOOL> @@fraud_tx_recieve;
Seed = {Account.*};
Seed2 = {Transaction.*};
Res = SELECT t FROM Seed:a -(Send_Transaction:e)->Transaction:t
WHERE a.id == account_id
ACCUM @@fraud_tx_send += t.is_fraud;
Res2 = SELECT t FROM Seed2:t -(Recieve_Transaction:e)->Account:a
WHERE a.id == account_id
ACCUM @@fraud_tx_recieve += t.is_fraud;
PRINT @@fraud_tx_send + @@fraud_tx_recieve;
PRINT @@fraud_tx_send;
PRINT @@fraud_tx_recieve;}
这样,我们可以看到与用户相关联的所有交易的列表,以及它们是否是欺诈性的,以及仅发送的交易和仅接收的交易的交易结果。
下面是第一个 PRINT 语句的结果:

查询 II 的第一个打印语句的部分结果。
查询三
现在,比方说,我们想要获取所有的帐户顶点,并保存一个哪个帐户有什么交易的列表。为此,我们首先需要声明一个 TYPEDEF 元组(这是我们想要的列表类型的元组),然后我们可以从 Account 开始迭代,遍历 Send_Transaction,并转到 Transaction 以获得我们想要的信息。
CREATE QUERY getAccountsInfo() FOR GRAPH AMLSim {
TYPEDEF TUPLE <BOOL tx_fraud, DOUBLE init_balance, DOUBLE current_balance, DOUBLE tx_amount> TX_INFO;
ListAccum<TX_INFO> @@txInfo;
Seed = {Account.*};
Res = SELECT t FROM Seed:a -(Send_Transaction:e)->Transaction:t
ACCUM @@txInfo+=TX_INFO(t.is_fraud, a.init_balance, a.current_balance, t.amount);
PRINT @@txInfo;
}
现在,如果我们在 GraphStudio 中运行它,我们会得到这个错误。

尝试在 GraphStudio 中运行查询时出错。
AMLSim 数据库非常大,这也是我们出现这个错误的部分原因。有时,这甚至会导致超时错误。是的,我们可以增加 TigerGraph 中的查询响应时间,但我只是想获取一个事务样本。有一种类型的累加器可以简化对其大小的限制,那就是… HeapAccum!接下来让我们来探索一下!
HeapAccum
概观
HeapAccum 类似于 ListAccum,因此它们都可以有重复项。它们也是排序的,但是对于 HeapAccum,它的顺序是它接受的一个参数。此外,HeapAccum 将容量作为一个参数。此外,HeapAccum 要求类型是元组。在查询 I 和 II 中,我们的类型是 boolean,但是 HeapAccum 不允许这样。HeapAccum 的语法是:
HeapAccum<typedefTupleName> (capacity, var1 ASC/DESC, var2 ASC/DEC, ...)
首先,我们需要创建一个 TYPEDEF 元组。那么,容量是累加器可以容纳多少元组的整数。最后,接下来的变量是 TYPEDEF 元组中的字段,后跟 ASC 或 DESC。这实质上是说,HeapAccum 的顺序应该基于某个字段的升序或降序。如果第一个字段中有两个或更多的项目具有相同的值,那么任何后续字段和升序/降序都是次要顺序(有点像决胜局)。
因此,从一开始,HeapAccum 的设置就比较复杂。如果你追求简单,那就选择 ListAccum。但它确实提供了一些有用的参数,如容量和订购内容。让我们看看这个蓄电池的运行情况!
查询四
让我们使用与查询 III 完全相同的查询,但是使用一个 HeapAccum。我们可以生产 10000 件,并基于 tx_fraud DESC 订购。(这样,欺诈性交易就会在最上面。)
CREATE QUERY getAccountsInfo() FOR GRAPH AMLSim {
TYPEDEF TUPLE <BOOL tx_fraud, DOUBLE init_balance, DOUBLE current_balance, DOUBLE tx_amount> TX_INFO;
HeapAccum<TX_INFO> (10000, tx_fraud DESC) @@txInfo;
Seed = {Account.*};
Res = SELECT t FROM Seed:a -(Send_Transaction:e)->Transaction:t
ACCUM @@txInfo+=TX_INFO(t.is_fraud, a.init_balance, a.current_balance, t.amount);
PRINT @@txInfo;
}
我们可以执行它…瞧!没有错误信息!

查询 IV 返回的值
质疑五
最后,让我们看看 HeapAccum 在哪里可能不是最佳选择。如前所述,它比 ListAccum 复杂得多。所以…让我们试着用一个 HeapAccum 重新发明查询 I。
CREATE QUERY heapQueryFive(STRING account_id) FOR GRAPH AMLSim {
TYPEDEF TUPLE<BOOL fraud_tx> BoolTuple;
HeapAccum<BoolTuple> (1000, fraud_tx DESC) @@fraud_tx;
Seed = {Account.*};
Res = SELECT t FROM Seed:a -(Send_Transaction:e)->Transaction:t
WHERE a.id == account_id
ACCUM @@fraud_tx += BoolTuple(t.is_fraud);
PRINT @@fraud_tx;
}
首先,顺序不同于查询 I;该查询将只在顶部显示欺诈性交易,随后显示非欺诈性交易。为了模拟查询 I,我们可以添加一个计数器。但是,让它自动订购的一个很酷的事情是,现在我们可以很容易地看到帐户 id 9755 是否有任何欺诈交易。运行查询,这是我们的部分结果:

查询 V 的结果
因此,首先,我们可以很容易地看到,我们的帐户 9755 有一个欺诈交易。但是您会注意到,每个返回(每个元组)的结果都在字典中。根据不同的用例,这可能没有只有列表有用。然而,总的来说,这归结于查询的目的是确定是使用 ListAccum 还是 HeapAccum。
结论+参考文献
恭喜你。您已经创建了总共五个查询,了解并使用了 ListAccum 和 HeapAccum!总之,HeapAccum 比 ListAccum 更复杂,需要一个 Tuple 作为其类型,并返回字典。然而,HeapAccum 可以很容易地限制结果(通过它的 capacity 参数),也可以很容易地对结果进行排序。总的来说,两者各有利弊,选择“最好”的要视情况而定。
这篇博客使用了来自 GitHub 知识库的数据集:
https://github.com/TigerGraph-DevLabs/AMLSim_Python_Lab
该博客使用了来自 TigerGraph 在 ListAccum 和 HeapAccum 上的文档的信息:
https://docs.tigergraph.com/dev/gsql-ref/querying/accumulators#listaccum https://docs.tigergraph.com/dev/gsql-ref/querying/accumulators#heapaccum
最后,如果您有任何问题或想要了解更多信息,请随时加入 TigerGraph Discord:
https://discord.gg/gRHWBZNpxW
非常感谢您阅读这篇博客,希望您以后继续使用 TigerGraph!
探索机器学习在地球科学中的用例
原文:https://towardsdatascience.com/exploring-use-cases-of-machine-learning-in-the-geosciences-b72ea7aafe2?source=collection_archive---------17-----------------------
行业笔记
根据地球化学预测地层,并根据不列颠哥伦比亚中部的其他地球化学分析物预测铜和锌

由特雷弗·麦金农在 Unsplash 上拍摄
伴随项目的 Tableau 仪表盘
Github 库
我将探索的数据集是区域地球化学调查数据集,这是一个自 1976 年以来在加拿大不列颠哥伦比亚省开展的项目,旨在“帮助矿产资源的勘探和开发”1。地球化学数据集是一种化学数据集,如从岩石或沉积物等地质介质中获得的选定元素的化学性质。该数据集包括“河流沉积物”,即从河流或水体中收集的沉积物样本,用于地球化学分析。河流沉积物取样被认为是矿产勘探的一级近似法,因为集水区岩性(或者,通俗地说,排水区的岩石类型)被认为是河流沉积物地球化学的主要控制因素[2,3],因此可以指示样品位置上游的矿床。虽然我更喜欢岩石地球化学的数据集,但我选择使用公开可用的数据进行分析,我希望您将这些发现作为地质数据其他用例的灵感。
我从两个假设开始分析:
- 地球化学可以用来准确预测数据集中河流沉积物样品的主要集水区岩性吗?
- 许多分析物的地球化学可以用来准确预测一种分析物(如铜)的浓度吗?
我选择查看数据集中的“Quest”区域,因为它拥有数据集中所有区域中最多的数据。Quest 地区位于不列颠哥伦比亚省中部/北部广阔的内陆高原,靠近乔治王子镇。该地区的地质特征是内恰科盆地的白垩纪至渐新世沉积物,其西面以海岸山脉为界,东面以落基山脉为界。一层厚厚的喷出火山岩和/或第四纪沉积物覆盖在内恰科盆地的沉积岩上[4]。考虑到大部分感兴趣的地质被覆盖,我认为这个区域是数据驱动勘探方法的主要候选区域。
从地球化学预测地质单元
为了开始这个项目,我通过将某些分析物的百分比值转换为百万分之一来准备和清理数据,用 Quest 区域的铅平均值填充 72 个缺失的铅值,并删除数据集中出现次数少于 30 次的基础地质单元行(稍后将详细介绍)。然后,我将地层学转换为数值(即,如果一行的地质单位值为“KTpg ”,则显示 1 来表示该单位,如果不是该单位,则显示 0),因为机器学习分类不能处理文本值。这是排序的中间步骤,因为我们将把结果转换回最终输出数据的文本值。
我使用 Python 中的 Scikit-Learn 库执行了一个标准的“训练-测试分割”。机器学习项目中的惯例是使用一部分数据来“训练”模型,并使用数据集的一小部分剩余部分(称为测试集)来测试数据。这里的想法是,模型以前从未见过测试数据,因此它被认为是模型在新数据上表现如何的准确度量。我使用了“训练-测试分离”函数的默认参数,该函数随机打乱数据,选择 80%的数据进行训练,20%的数据进行测试。然后,我对六个不同模型的测试数据测量了模型的准确性:
- 决策树分类器,准确率 61.4%
- 随机森林分类器,准确率 66.3%
- 额外树分类器,准确率 64.2%
- k 最近邻分类器,52%的准确率
- XG Boost 分类器,准确率 71.1%
图片作者。
XG Boost 模型的准确率最高,为 71.1%。我做了大量的数据处理,最终得到了这么高的精度。我对偏斜的变量进行了对数转换,通过分析变量的相关性小心翼翼地去除了异常值,尝试了不列颠哥伦比亚省的不同地区,去除了沉积单元,基于对模型的重要性去除了某些分析物,并减少了模型必须分类的单元数量。我发现这些努力大部分都是徒劳的,但是对模型性能有很大影响的一点是减少了要分类的地质单元的数量。事实证明,如果有 30 多个地质单元要对数据进行分类,机器学习模型在这种规模和性质的数据集上表现不佳。下面是最终输出的一个片段:

图片作者。
这些指标显示模型性能:
该模型在“tr jtk”(Takla 集团)单位中表现极佳。如果你回头看第一个程序(geo_unit_predictions.py),TrJTk 有 1096 个值。我将把这归因于拥有如此多数据进行训练的模型——如果你花任何时间学习如何改善机器学习模型,更多数据是你可以改善模型的最佳方式之一。按照这种逻辑,将来自其他单元的更多数据添加到该数据集中以帮助模型学习也可能会提高这些单元的准确性分数。
从其他分析物预测铜和锌
让我们继续预测铜和锌。我选择铜和锌是因为它们在加拿大被列为重要矿产。这不是机器学习组件的一部分,但我也编写了一个程序来识别您的数据集是否包含关键矿物:
在“geo_unit_predictions.py”的第 41 行,我保存了一个已清理的数据集“analytes.csv”,这是我们执行此任务的起点。因为这是一个回归问题,而不是像前一个任务那样的分类,所以我使用了所有的行,而不是删除具有深奥地质单元的行。这是预测铜的代码,但通过修改第 6 行和第 8 行,同样的代码也适用于任何其他分析物。我使用了一种类似的方法,通过同时运行 9 个不同的模型来撒下一张大网,以确定哪个模型效果最好。然后我更进一步,混合了四个最高精度模型的结果。这将准确度提高了几个百分点!请注意,由于前面示例中分类模型的输出数据,将您的模型混合在一起是行不通的(混合仅适用于回归问题)。
我们到了。预测 Quest 地区铜的准确率为 72.6%,预测锌的准确率为 71.8%。这些结果很有希望——我能想到的一个有希望的用例是为手持 x 射线荧光(XRF)数据开发一个机器学习模型。手持式 XRF 是一种通常用于地质野外工作的工具,它将 x 射线投射到样本上,并测量从样本发出的 x 射线的荧光。这些次级 x 射线具有特定元素的独特荧光特征。在我作为一名地质学家的时间里,我遇到过很少有人非常信任手持 XRF 数据——然而,鉴于这些结果,我假设可以在包含手持和实验室地球化学数据的数据集上训练一个模型,该模型将准确预测手持 XRF 数据的实验室测量结果。基本上,我认为机器学习有潜力极大地改善手持 XRF 数据。我也很想研究包含岩石物理特性的钻孔数据,以预测岩性、岩相,甚至矿物浓度。
我将所有的最终数据都放入 QGIS,但最终还是用 Tableau 进行了最终的可视化,因为我真的很喜欢工具提示提供的交互性。这里是 Tableau 仪表盘:

希望你喜欢这个项目。干杯!
马丁
参考文献
- 区域地球化学调查数据,不列颠哥伦比亚省地质调查局,2020 年。https://open . Canada . ca/data/en/dataset/49 a73c 36-4a 25-4be 5-B6 D1-Abd 020 FB 031 a
- 阿恩,华盛顿特区和布鲁梅尔,2011 年。不列颠哥伦比亚省 QUEST South 河流-沉积物数据的集水分析和解释。地球科学 BC 报告 2011–5,24,http://www.geosciencebc.com/reports/gbcr-2011-05/
- Bonham-Carter,G.F .和 Goodfellow,w . d ,, 1986 年,《使用数字化水系和地质图对河流地球化学数据进行背景校正:在育空地区和西北地区塞尔温盆地的应用:地球化学勘探杂志》,第 25 卷,第 139-155 页。
- Hayward,n .和 Calvert,A.J .,2009,《不列颠哥伦比亚省内恰科盆地东南部始新世和新近纪火山岩:使用初至断层成像解释加拿大亨特地震反射测量》。加拿大地球科学杂志。46(10):707–720。【https://doi.org/10.1139/E09-041
承认
特别感谢怀俄明州地质调查局的第二地质学家 Derek Lichtner,他提供了诚实的反馈和对本项目的出色的同行评审。
使用 Pandas、Matplotlib 和 Seaborn 探索测井数据
原文:https://towardsdatascience.com/exploring-well-log-data-using-pandas-matplotlib-and-seaborn-712779a39ac2?source=collection_archive---------15-----------------------
使用来自 Seaborn 和 Matplotlib 的多个图探索岩石物理和测井测量的示例

马库斯·斯皮斯克在 Unsplash 上的照片
机器学习和人工智能在地球科学和岩石物理领域越来越受欢迎。尤其是在过去的十年里。机器学习是人工智能的一个分支,是计算机可以学习并根据数据进行预测的过程,而无需显式编程。我们可以在岩石物理学中以多种方式使用机器学习,包括自动化异常值检测、属性预测、相分类等。
这一系列文章将着眼于从基本测井测量到岩石物理性质预测的数据集。这些文章最初是在 SPWLA 2021 大会的一个机器学习和人工智能研讨会上发表的。它们后来被扩展和更新以形成这些文章。该系列将包括以下内容,链接将包括一旦他们被释放。
1.Volve 油田数据集
2 中选定油井的初始数据勘探。缺失数据的识别
3。使用手动和自动方法检测异常值/异常数据点
4。使用机器学习预测关键储层性质
数据
2018 年,Equinor 向公共领域发布了 Volve 领域的全部内容,以促进研究和学习。发布的数据包括:
- 测井记录
- 岩石物理解释
- 报告(地质、完井、岩石物理、岩心等)
- 核心测量
- 地震数据
- 地质模型
- 还有更多…
Volve 油田位于北海挪威部分斯塔万格以西约 200 公里处。1993 年在侏罗纪时代的胡金地层中发现了碳氢化合物。石油生产始于 2008 年,持续了 8 年(是计划时间的两倍),直到 2016 年停止生产。在油田寿命期内,总共生产了 63 个 MMBO,达到了 56000 桶/天的稳定产量
有关 Volve 字段和整个数据集的更多详细信息,请访问:https://www . equinor . com/en/what-we-do/Norwegian-continental-shelf-platforms/Volve . html
这些数据在 Equinor 开放数据许可证下获得许可。
选择分析数据
Volve 数据集由 24 口井组成,包含各种测井数据和其他相关测量数据。对于这个小教程系列,我们将选择五口井。这些是:
- 9 月 15 日-F-1 A
- 9 月 15 日-F-1 B
- 9 月 15 日-F-1 C
- 2011 年 9 月 15 日至 11 日
- 15/9-F-11 B
从这些井中,选择了一套标准的测井测量(特征)。它们的名称、单位和描述详见下表。

Volve 油田选定井的标准测井测量和岩石物理推导曲线。
该系列笔记本的目标是预测三种常见的岩石物理测量值:孔隙度(PHIF)、含水饱和度(西南)和页岩体积(VSH)。传统上,这些是通过一些经验公式计算出来的。
数据探索
探索性数据分析(EDA)是数据科学工作流程中的一个重要步骤。它允许您熟悉您的数据并理解其内容、范围、质量和变化。在这个阶段,您可以识别数据中的模式以及特征(测井记录)之间的关系。
我在以前的媒体文章中已经介绍了一些 ed a 过程和情节:
- 利用测井数据进行探索性数据分析
- 使用 Matplotlib 可视化油井数据覆盖范围
- 如何使用 Python 使用无监督学习对测井数据进行聚类
作为岩石物理学家/地球科学家,我们通常使用测井曲线(数据与深度的线图)、直方图和交会图(散点图)来分析和探索测井数据。Python 提供了一个很好的工具集,可以快速简单地从不同的角度可视化数据。
在本教程中,我们将涵盖:
- 从 CSV 文件读入数据
- 查看对数图上的数据
- 查看交会图/散点图上的数据
- 查看直方图上的数据
- 使用对绘图在交会图和直方图上显示所有测井曲线
导入库和数据
第一步是导入我们需要的库。这两个工具分别是用于加载和存储数据的 pandas、用于可视化数据的 matplotlib 和 seaborn。
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import seaborn as sns
导入库之后,我们将使用 pandas read_csv函数加载数据,并将其赋给变量df。
df = pd.read_csv('data/spwla_volve_data.csv')
熊猫。描述功能
一旦数据被加载,它将被存储在一个结构化的对象中,类似于一个表,称为 dataframe。我们可以用多种方法检查数据帧的内容。首先,我们可以使用.describe()函数检查数字列的汇总统计数据。由此,我们能够找到关于每个特征的数据点数、平均值、标准偏差、最小值、最大值和百分位值的信息。
为了使表格更容易阅读,我们将添加.transpose()函数。这会将列名放在行中,将统计测量值放在列中。
df.describe().transpose()

转置的 pandas 数据帧,包含每次测井测量的汇总统计数据。使用 df.describe()方法创建。
熊猫。信息功能
我们可以调用的下一个方法是.info()。这提供了数据帧中所有列的列表,以及它们的数据类型(例如,浮点、整数、字符串等)。),以及每列中包含的非空值的数量。我们可以在下面看到,我们有一个名为 wellName 的列,它不包含在上面显示的数据帧中。
df.info() RangeIndex: 27845 entries, 0 to 27844
Data columns (total 16 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 wellName 27845 non-null object
1 MD 27845 non-null float64
2 BS 27845 non-null float64
3 CALI 27845 non-null float64
4 DT 5493 non-null float64
5 DTS 5420 non-null float64
6 GR 27845 non-null float64
7 NPHI 27845 non-null float64
8 RACEHM 27845 non-null float64
9 RACELM 27845 non-null float64
10 RHOB 27845 non-null float64
11 RPCEHM 27845 non-null float64
12 RPCELM 27600 non-null float64
13 PHIF 27736 non-null float64
14 SW 27736 non-null float64
15 VSH 27844 non-null float64
dtypes: float64(15), object(1)
memory usage: 3.4+ MB
熊猫。头和。尾部函数
下一组有用的方法是head()和.tail()函数。这些函数返回数据帧的前/后五行
df.head()

测井测量数据帧的前五行。单击以放大。图片由作者提供。
df.tail()

测井测量数据帧的最后五行。单击以放大。图片由作者提供。
使用查找井的名称。独特功能
从介绍中我们知道,在这个数据集中应该有 5 口井。我们可以通过调用 wellName 列并使用方法.unique()来检查这一点。这将返回一个数组,列出该列中所有的唯一值。
df['wellName'].unique()array(['15/9-F-1 A', '15/9-F-1 B', '15/9-F-1 C', '15/9-F-11 A',
'15/9-F-11 B'], dtype=object)
如上所述,我们可以通过名称调用数据帧中的特定列。如果我们对一个数字列(比如 CALI)执行此操作,我们将返回一个 pandas 系列,其中包含前 5 个值、后 5 个值以及该列的详细信息。
df['CALI']0 8.6718
1 8.6250
2 8.6250
3 8.6250
4 8.6250
...
27840 8.8750
27841 8.8510
27842 8.8040
27843 8.7260
27844 8.6720
Name: CALI, Length: 27845, dtype: float64
数据可视化
测井曲线
测井图是我们用来分析测井数据的基本工具之一。它们由几列轨道组成。每一列中可以有一条或多条测井曲线,相对于深度绘制。它们帮助我们可视化地下,并允许我们识别潜在的油气层段。
因为我们将创建多个对数图,所以我们可以创建一个可以多次调用的简单函数。函数允许我们将代码分解成可管理的块,并省去多次重复代码的需要。
这个create_plot函数有许多参数(输入):
- 井名:字符串形式的井名
- 数据帧:所选井的数据帧
- curves_to_plot :我们想要绘制的测井曲线/数据帧列的列表
- 深度曲线:我们想要绘制的深度曲线
- log_curves :需要以对数刻度显示的曲线列表
由于数据框中有 5 口井,如果我们试图一次性绘制所有数据,我们将得到所有井的混合测量值。为了解决这个问题,我们可以创建一个按井名分组的新数据帧。
grouped =df.groupby('wellName')
当我们调用这个新分组数据帧的head()函数时,我们将获得每个井的前 5 行。
grouped.head()

熊猫数据帧按井名分组。图片由作者提供。
为了更好地控制我们想要绘制的井,我们可以将分组的数据帧分割成单个数据帧,并将它们存储在一个列表中。这将允许我们通过传入一个列表索引值来访问特定的井。
此外,它将允许我们在数据上使用所有可用的 pandas 数据框架函数,这是有限的,并且在使用分组数据框架时会发生变化。
# Create empty lists
dfs_wells = []
wellnames = []
#Split up the data by well
for well, data in grouped:
dfs_wells.append(data)
wellnames.append(well)
如果我们循环遍历井名列表,我们可以得到索引号和相关的井名。
for i, well in enumerate(wellnames):
print(f'Index: {i} - {well}')Index: 0 - 15/9-F-1 A
Index: 1 - 15/9-F-1 B
Index: 2 - 15/9-F-1 C
Index: 3 - 15/9-F-11 A
Index: 4 - 15/9-F-11 B
在我们绘制数据之前,我们需要指定我们想要绘制的曲线,并指定哪些曲线是对数标度的。
curves_to_plot = ['BS', 'CALI', 'DT', 'DTS', 'GR',
'NPHI', 'RACEHM', 'RACELM', 'RHOB',
'RPCEHM', 'RPCELM', 'PHIF', 'SW', 'VSH']
logarithmic_curves = ['RACEHM', 'RACELM', 'RPCEHM', 'RPCELM']
让我们拜访第一口井,画一个图。
注意,Python 列表是从 0 开始索引的,因此列表中的第一口井将位于位置 0。
well = 0
create_plot(wellnames[well], dfs_wells[well],
curves_to_plot, dfs_wells[well]['MD'],
logarithmic_curves)
当我们执行此代码时,我们为 15/9-F-1 A 生成了以下曲线图。我们在一个曲线图上获得了所有测井测量结果,电阻率曲线以对数形式显示,正如我们所预期的那样。

来自 Volve 油田数据集的 15/9 井-F-1 A 井的测井曲线。使用熊猫生成。由作者创作。
我们可以对第二口井进行同样的操作:
well = 1
create_plot(wellnames[well], dfs_wells[well],
curves_to_plot, dfs_wells[well]['MD'],
logarithmic_curves)

来自 Volve 油田数据集的 15/9 井-F-1 B 井的测井曲线。使用熊猫生成。由作者创作。
使用 Seaborn 的标准交会图(散点图)
交会图(也称为散点图)是我们在岩石物理分析中使用的另一种常用数据可视化工具。关于使用交会图(散点图)和测井数据的更多信息可在此处找到:
- 使用 Python 中的 Matplotlib 创建测井数据的散点图(交会图)
类似于上面的对数图部分,我们将创建一个简单的函数,其中我们可以使用一个简单的函数生成多个交会图。这个函数利用了 Seaborn 的 FacetGrid 函数,允许我们直接在网格上绘制地图。与 matplotlib 中的 subplot2grid 相比,这是一种更容易绘制数据的方式。
该函数的参数(输入)为:
- x —字符串形式的 X 轴变量,例如“NPHI”
- y —字符串形式的 Y 轴变量,例如“RHOB”
- c —用于将颜色应用于交会图的第三个变量,例如“GR”
- 数据帧 —使用
.groupby('wellName')创建的分组数据帧 - 列 —显示在图上的列数
- xscale—X 轴刻度
- Y 轴刻度—Y 轴刻度
- vmin —颜色阴影的最小值
- vmax —颜色阴影的最大值
使用井径仪评估中子孔隙度&体积密度数据质量
我们现在可以使用我们的函数来创建用卡尺着色的密度-中子交会图。井径仪提供了钻孔尺寸的指示。在钻孔的过程中,钻孔壁会坍塌,导致钻孔变大。

使用 seaborn 创建的测井数据交会图,用于识别井眼冲蚀。作者创建的图像。
从上图中,我们可以看到,尽管 15/9-F11 B 号井有一些扩大的井眼,用较红的颜色表示,但大多数井眼状况良好,没有被太大程度的冲刷。
声波压缩与剪切交会图,伽马射线着色
我们要看的下一个交会图是声波压缩(DTC)与声波剪切(DTS)的对比。

使用 seaborn 创建的声波压缩和剪切测井数据交会图。颜色范围表示数据的伽马射线值。空白图表表示潜在的缺失数据。作者创建的图像。
当我们查看这些数据时,我们可以看到其中两个图表是空白的。这让我们马上知道我们的数据集中可能有缺失的数据。我们将在本系列的下一篇文章中探讨这个问题。
直方图
matplotlib 直方图
直方图是探索性数据分析和数据科学中常用的工具。它们是优秀的数据可视化工具,看起来类似于条形图。然而,直方图使我们能够深入了解一组数据中的值的分布,并使我们能够在一个简洁的图中显示大量数据。在岩石物理学和地球科学领域,我们可以使用直方图来识别异常值,也可以挑选关键的解释参数。例如,来自伽马射线的粘土体积或页岩体积端点。
直方图允许我们查看数字数据的分布、形状和范围。数据被分成许多条柱,由图上的单个条柱表示。
您可以在本文中找到有关使用直方图和测井数据的更多信息:
使用 Python 中的 Matplotlib 创建测井数据直方图
我们可以从主数据帧中调用一个简单的直方图,只需将.hist(column-name)追加到数据帧对象的末尾。
df.hist('GR')

使用熊猫创建的简单伽马射线直方图。图片作者。
马上我们可以看到我们有一些问题。首先是所有的井都分组在一起,面元的数量太少,情节看起来不太好。因此,我们可以改变它一点,首先增加箱的数量,并删除网格线。
df.hist('GR', bins=40)
plt.grid(False)

使用 pandas 创建的简单伽马射线直方图,增加了分区/箱的数量。图片作者。
以上产生了对情节的即时改进。我们现在可以更清楚地看到数据的分布,但是所有的数据仍然是组合在一起的。
Seaborn 直方图
我们还可以调用 Seaborn 绘图库,它让我们可以更好地控制绘图的美感。在第一个例子中,我们可以添加一个核密度估计(KDE)。

测井数据的 Seaborn 直方图。图片由作者提供。
从上面的图中,我们可以看到标签是自动生成的,我们还绘制了 KDE 线。
为了将数据拆分到不同的井中,我们可以提供另一个参数:hue,这将允许我们使用第三个变量来拆分数据。
如果我们为色调传递井名,我们可以为每个井生成单独的直方图。

Seaborn 直方图显示了每口井的伽马射线。图片由作者提供。
我们可以对体积密度(RHOB)做同样的处理。我们还可以添加想要显示的箱子数量。

Seaborn 直方图显示了每口井的体积密度数据。图片由作者提供。
FacetGrid
如果我们想要将数据分割成每个孔的单独直方图,我们需要使用 FacetGrid 并将所需的直方图映射到它。
对于 FacetGrid,我们指定数据帧和要将数据分割成的列。如交会图部分所述,hue控制每一列中数据的颜色,col_wrap指定图形换行到新行之前的最大列数

使用 Seaborn 创建的伽马射线数据直方图。图片由作者提供。
KDEPlot
如果我们想以直线的形式查看数据的分布,我们可以使用核密度估计图(kdeplot)。如果我们想看看数据是否需要标准化,这是很有用的。

使用 seaborn 的伽马射线数据的核密度估计图。图片由作者提供。
Seaborn 配对图
我们可以使用 seaborn 库中的一行简单代码,快速创建一个包含交会图和直方图的网格,而不是每次只查看有限数量的变量。这就是所谓的配对图。
我们传入数据帧,以及我们想要分析的变量。沿着配对图的对角线,我们可以有一个直方图,但在本例中,我们将使用 KDE 图。此外,我们可以使用 plot_kws 参数指定颜色、形状等。

Volve 油田测井数据的 Seaborn pairplot。这说明了测井测量值之间的关系。图片作者。
我们现在可以很容易地看到每个测井测量值之间的关系,而不是创建单独的图。这可以通过使用色调参数将数据分割到每个孔中来进一步增强。

来自 Volve 油田的测井数据的 Seaborn pairplot,用井名着色。这说明了测井测量值之间的关系。图片作者。
摘要
在本教程中,我们使用了许多工具来探索数据集,并对其有了一些初步的了解。这是通过测井曲线、交会图(散点图)、直方图和配对图实现的。这些工具让我们对数据及其内容有了初步的了解。
下一步是确定数据集中是否存在任何缺失的数据。这篇文章即将发表。
这本笔记本最初是为 2021 年 SPWLA 大会的 SPWLA 机器学习研讨会发布的。
感谢阅读!
如果你觉得这篇文章有用,请随时查看我的其他文章,这些文章从不同的角度研究了 Python 和测井数据。你也可以在 GitHub 找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
如果你喜欢阅读这些教程,并想支持我作为一名作家和创作者,那么请考虑报名成为一名媒体成员。一个月 5 美元,你就可以无限制地阅读数千篇各种主题的文章。如果您使用 我的链接 ,注册,我将为您赚取一小笔佣金,无需额外费用!
使用 Welly Python 库探索测井数据
原文:https://towardsdatascience.com/exploring-well-log-data-using-the-welly-python-library-5e808cd3137b?source=collection_archive---------23-----------------------
专门用于加载和探索测井 LAS 文件的 Python 库

照片由阿里·埃利奥特在 Unsplash 上拍摄
welly 库由 Agile Geoscience 开发,用于加载、处理和分析单井或多口井的测井数据。该库允许探索 las 文件头中的元数据,还包含显示典型测井记录的绘图功能。此外,welly 库包含用于识别和处理数据质量问题的工具。
Welly 库可以在 https://github.com/agile-geoscience/welly的敏捷地球科学 GitHub 上找到
在这个简短的教程中,我们将看到如何从 Volve 字段加载一个井,并探索这个库中可用的一些功能。
使用不同数据的这篇文章的视频版本可以在下面找到。
数据集
我们正在使用的数据集来自于 2018 年发布的公开发布的 Equinor Volve Field 数据集。本教程中使用的文件来自 15/19-F1B 井。Volve 数据集的详细信息可以在这里找到。
本教程是我的 Python 和岩石物理学系列的一部分。之前文章的链接可以在这里找到。
本文附带的笔记本可以在 GitHub 资源库中找到:https://github.com/andymcdgeo/Petrophysics-Python-Series
导入库和数据
本教程的第一步是从 Welly 库中加载所需的模块,Well 和 Curve。这些模块用于处理测井数据和单独的曲线。
from welly import Well
from welly import Curve
import matplotlib.pyplot as plt
我们的 LAS 文件可以使用Well.from_las()方法加载。这将创建一个新的井对象。
well = Well.from_las('Data/15_19_F1B_WLC_PETRO_COMPUTED_INPUT_1.LAS')
数据探索
文件和油井信息
现在我们的数据已经加载完毕,我们可以开始研究所选井的内容和元数据。如果我们调用我们的well对象,我们将看到一个包含井名、位置、坐标和曲线助记符列表的汇总表。
well

我们还可以调用特定的函数来访问所需的信息。
第一个是header,它将返回关键的标题信息,包括井名、唯一的井标识符(UWI)、字段名称和公司名称。
well.header
这将返回:
{'name': '15/9-F-1 B', 'uwi': '', 'field': 'VOLVE', 'company': 'Statoil Petroleum'}
现在让我们来看看这口井的位置信息。为此,我们可以为我们的数据对象调用.location方法。
well.location
这会以字典的形式返回一个位置对象。
Location({'position': None, 'crs': CRS({}), 'location': 'Maersk Inspirer', 'country': '', 'province': '', 'county': '', 'latitude': '058 26\' 29.907" N DMS', 'longitude': '001 53\' 14.708" E DMS', 'api': '', 'td': None, 'deviation': None})
我们正在使用的文件不包含太多关于井的位置的信息,但是我们有关于纬度和经度的信息。这些可以通过在定位方法中添加.latitude和.longitude来提取,并转换成更容易阅读的格式。
lati = well.location.latitude
long = well.location.longitude
print(lati)
print(long)
对这些方法使用 print 函数可以提供更好的输出。
058 26' 29.907" N DMS
001 53' 14.708" E DMS
探索数据
我们在上一节中看到,在查看井口时,我们有许多曲线。通过调用count_curves()函数,我们可以知道有多少。
well.count_curves()
这将返回总共 22 条曲线。
我们还可以使用方法_get_curve_menmonics()获得 las 文件中的曲线助记符列表。
well._get_curve_mnemonics()
这将返回 las 文件中所有助记符的列表。
['ABDCQF01',
'ABDCQF02',
'ABDCQF03',
'ABDCQF04',
'BS',
'CALI',
'DRHO',
'DT',
'DTS',
'GR',
'NBGRCFM',
'NPHI',
'PEF',
'RACEHM',
'RACELM',
'RD',
'RHOB',
'RM',
'ROP',
'RPCEHM',
'RPCELM',
'RT']
查看所有曲线的另一种方法是调用.data。这将返回一个包含井名的 dictionary 对象,以及该曲线的前 3 个和后 3 个值。
well.data
如下例所示,许多第一个和最后一个值被列为 nan,它代表非数字。
{'ABDCQF01': Curve([nan, nan, nan, ..., nan, nan, nan]),
'ABDCQF02': Curve([nan, nan, nan, ..., nan, nan, nan]),
'ABDCQF03': Curve([nan, nan, nan, ..., nan, nan, nan]),
'ABDCQF04': Curve([nan, nan, nan, ..., nan, nan, nan]),
'BS': Curve([36\. , 36\. , 36\. , ..., 8.5, 8.5, 8.5]),
'CALI': Curve([nan, nan, nan, ..., nan, nan, nan]),
'DRHO': Curve([nan, nan, nan, ..., nan, nan, nan]),
'DT': Curve([nan, nan, nan, ..., nan, nan, nan]),
'DTS': Curve([nan, nan, nan, ..., nan, nan, nan]),
'GR': Curve([nan, nan, nan, ..., nan, nan, nan]),
'NBGRCFM': Curve([nan, nan, nan, ..., nan, nan, nan]),
'NPHI': Curve([nan, nan, nan, ..., nan, nan, nan]),
'PEF': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RACEHM': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RACELM': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RD': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RHOB': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RM': Curve([nan, nan, nan, ..., nan, nan, nan]),
'ROP': Curve([ nan, nan, nan, ..., 29.9699, 29.9903, nan]),
'RPCEHM': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RPCELM': Curve([nan, nan, nan, ..., nan, nan, nan]),
'RT': Curve([nan, nan, nan, ..., nan, nan, nan])}
我们可以更深入地研究 las 文件中的每条曲线,方法是传递曲线的名称,如下所示:
well.data['GR']

这为我们提供了一些曲线的汇总统计数据,例如:
- 空值是什么
- 曲线单位
- 曲线数据范围
- 数据的步长值
- 样本总数
- 缺失值的总数(nan)
- 曲线的最小值、最大值和平均值
- 曲线描述
- 前 3 个和后 3 个值的列表
数据质量控制
检查测井数据的质量是岩石物理工作流程的重要组成部分。
井眼环境可能是一个恶劣的环境,具有高温、高压、不规则的井眼形状等,所有这些都会对测井测量产生影响。这可能会导致许多问题,如缺失值、异常值、常量值和错误值。
welly library 附带了许多质量控制检查,允许我们检查所有数据或特定曲线的问题。
质量控制检查包括:
- 检查间隙/缺失值:
.no_nans(curve) - 检查整条曲线是否为空:
.not_empty(curve) - 检查曲线是否包含常数值:
.no_flat(curve) - 检查单位:
check_units(list_of_units) - 检查值是否都为正:
all_positive(curve) - 检查曲线是否在范围内:
all_between(min_value, max_value)
方法的完整列表可在 Welly 帮助文档中找到:https://Welly . readthedocs . io
开始之前,我们需要导入质量模块,如下所示:
import welly.quality as wq
在我们运行任何质量检查之前,我们首先需要创建一个列表,列出我们想要运行的测试以及我们想要运行这些测试的数据。
要做到这一点,我们可以建立一个字典,关键字是我们想要运行检查的曲线。如果想在所有的曲线上运行它,我们需要使用键Each。
对于每条曲线,我们将检查是否有任何平线值,任何差距,并确保曲线不是空的。对于伽马射线(GR)和体积密度(RHOB)曲线,我们将检查所有值是否为正值,它们是否在标准范围内,以及单位是否是我们期望的。
tests = {'Each': [wq.no_flat,
wq.no_gaps,
wq.not_empty],
'GR': [
wq.all_positive,
wq.all_between(0, 250),
wq.check_units(['API', 'GAPI']),
],
'RHOB': [
wq.all_positive,
wq.all_between(1.5, 3),
wq.check_units(['G/CC', 'g/cm3']),
]}
我们可以按原样运行测试,但是,输出不容易阅读。为了更容易和更好,我们将使用 IPython.display 的 HTML 函数来制作一个漂亮的表格。
一旦模块被导入,我们可以创建一个名为data_qc_table的变量来存储信息。分配给这个变量的是data.qc_table_html(tests),它从我们上面创建的tests字典中生成表格。
from IPython.display import HTML
data_qc_table = well.qc_table_html(tests)
HTML(data_qc_table)

运行测试后,我们可以看到返回了一个彩色的 HTML 表。任何用绿色突出显示的都是真的,任何用红色突出显示的都是假的。
从表中我们可以看出,BS(位大小)曲线在三次测试中有一次失败。在no_flat栏下,我们标记了一个错误值,表明该曲线包含常量/平坦值。当钻头尺寸曲线测量钻头直径时,这已被正确标记,钻头直径对于给定的一次运行或一系列运行是恒定的。
我们还可以看到许多曲线被标记为包含间隙。
表中还可以看到只针对 GR 和 RHOB 运行的测试。当我们在特定的曲线上运行特定的测试时,结果的剩余部分将变灰。
我们可以运行另一个测试来识别不是 nan 的数据部分。为此,我们设置了一个新的测试,并使用Each应用于所有曲线。
tests_nans = {'Each': [wq.fraction_not_nans]}
data_nans_qc_table = well.qc_table_html(tests_nans)
HTML(data_nans_qc_table)

一旦我们运行这些测试,我们就会看到一个类似于上面的表格。在最后一列中,我们有每条曲线值的总分数,这不是一个 nan。这些值是十进制的,值 1.0 表示 100%完整。分数列包含该数字的四舍五入形式。
我们可以写一个短循环,并打印出每条曲线的百分比值。这提供了一个更清晰的表格来了解每条曲线的缺失数据百分比。
print((f'Curve \t % Complete').expandtabs(10))
print((f'----- \t ----------').expandtabs(10))
for k,v in well.qc_data(tests_nans).items():
for i,j in v.items():
values = round(j*100, 2)
print((f'{k} \t {values}%').expandtabs(10))
这将返回一个漂亮的
Curve % Complete
----- ----------
ABDCQF01 9.72%
ABDCQF02 9.72%
ABDCQF03 9.72%
ABDCQF04 9.72%
BS 100.0%
CALI 10.6%
DRHO 10.62%
DT 12.84%
DTS 11.48%
GR 97.91%
NBGRCFM 39.73%
NPHI 10.28%
PEF 10.37%
RACEHM 25.72%
RACELM 25.72%
RD 96.14%
RHOB 10.37%
RM 96.14%
ROP 97.57%
RPCEHM 25.72%
RPCELM 25.72%
RT 25.72%
从结果中我们可以看出,许多曲线的缺失值百分比很高。这可能是由于一些测量直到井更深时才开始。我们将能够在下一节用图来确定这一点。
数据绘图
可视化测井数据是岩石物理学的核心,测井曲线是最常见的显示格式之一。welly 库允许快速、轻松地生成测井曲线。
首先,我们生成一个我们希望在每个轨道中显示的数据列表。如果我们想在一个轨迹中显示多条曲线,我们可以嵌入另一个列表,例如['MD', ['DT', 'DTS']]。内部列表中的曲线将绘制在相同的轨迹上,并且比例相同。
接下来,我们可以调用 plot 函数并传入曲目列表。
tracks = ['MD', 'GR', 'RHOB', 'NPHI', ['DT', 'DTS']]
well.plot(tracks=tracks)

正如在“数据质量”一节中所讨论的,我们假设一些测井曲线没有一直延伸到油井顶部。这是非常常见的做法,避免了从油井顶部到底部运行工具的需要和成本。
让我们把较低的音程放大一点。为此,我们可以使用常规的 matplotlib 函数来设置 y 轴限制。请注意,我们确实需要颠倒数字,以便首先是较深的值,然后是较浅的一秒。
tracks = ['MD', 'BS', 'CALI', 'GR', 'RHOB', 'NPHI', ['DT', 'DTS']]
well.plot(tracks=tracks)
plt.ylim(3500, 3000)(3500.0, 3000.0)

从结果中我们可以看到,我们现在只需很少的努力就可以得到一个漂亮的图。
但是,对绘图外观的控制受到当前实施的限制,不允许对绘图进行粒度控制,如颜色、比例和显示反比例曲线(如中子和密度曲线)。
测井数据到熊猫数据框
在最后一节中,我们将看看如何将测井数据从 welly 导出到 pandas。Pandas 是最受欢迎的存储、分析和操作数据的图书馆之一。
转换是一个简单的过程,可以通过调用我们的 well 对象上的.df()来实现。
df = well.df()
我们可以通过从 pandas 调用.describe()方法来查看数据的汇总统计来确认数据已经被转换。
df.describe()

摘要
由 Agile-Geoscience 开发的 welly library 是一个处理和探索测井记录文件的强大工具。在本例中,我们已经了解了如何加载单个 las 文件,探索关于油井和曲线内容的元信息,并在测井图上显示测井数据。
Welly 有更多的功能,可以处理多个测井记录,并根据钻井数据创建合成地震图。
你可以在这里找到并探索 welly 知识库。
感谢阅读!
如果您觉得这篇文章有用,请随时查看我的其他文章,这些文章介绍了 Python 和测井数据的各个方面。你也可以在GitHub找到我在这篇文章和其他文章中使用的代码。
如果你想联系我,你可以在LinkedIn或者我的 网站 找到我。
有兴趣了解更多关于 python 和测井数据或岩石物理学的知识吗?跟我上 中 。
使用 Google 数据流将数据存储导出到 BigQuery
原文:https://towardsdatascience.com/export-datastore-to-bigquery-using-google-dataflow-1801c25ae482?source=collection_archive---------14-----------------------

波多黎各德拉克鲁斯(作者)
如何使用 Google Dataflow 将数据存储导出到 BigQuery,并对实体进行额外过滤
在上一篇文章中,我展示了如何构建一个无服务器的解决方案,将各种数据从 Datastore 导出到 BigQuery。该文章中介绍的方法完全有效,甚至适用于大型数据存储。然而,主要的缺点是每次我们将所有行从数据存储导出到 BigQuery。对于大型数据存储来说,这可能会产生不必要的成本,消耗不必要的时间。
解决这个问题的方法之一是对数据库进行一系列更新。例如, AWS DynamoDB 提供了流,可以很容易地与 AWS Lambdas 链接。在谷歌 Firestore (被命名为数据存储的下一代)中可以找到非常相似的功能,其中对文档的更改会触发云功能— 参见文档。
尽管 datastore 不提供任何流功能,但我们仍然可以尝试通过使用查询来解决这个问题。数据存储库导入/导出不支持实体的本地过滤。因此,我们必须手动操作。程序如下:
- 过滤实体,导出到 JSON,存储到云存储中
- 将 JSONs 从云存储加载到 BigQuery
让我用谷歌数据流来完成这个任务。
Google Dataflow 是一个托管解决方案,用于执行不同的数据处理模式,如 ETL、批处理和流处理。但是谷歌数据流是数据流模型的可能实现之一。用于描述处理的 SDK 是在框架 Apache Beam 下实现的。
数据流管道
数据流模型是围绕管道组织的,管道是你从头到尾的数据处理工作流程。在管道内部,有两个对象很重要。PCollection 表示分布式数据集,PTransform 表示对 PCollection 的处理操作。

p 收集/p 转换概述(按作者)
我将使用 Python 作为编程语言。但是,管道也可以在 Java 和 Golang 中构建。完整的工作示例可在 GitHub 项目中获得(https://GitHub . com/jkrajniak/demo-datastore-export-filtering)。在这里,我将只对重要的代码块进行评论。
https://github.com/jkrajniak/demo-datastore-export-filtering
管道
让我们开始构建管道:
with beam.Pipeline(options=pipeline_options) as p:
# Create a query and filter
这将创建一个管道p,选项存储在pipeline_options中。接下来,操作符|将被用于连接每个p 转换块
rows = p | 'get all kinds' >> GetAllKinds(project_id, to_ignore)
这是第一阶段,它将从给定项目的数据存储中读取所有类型,并从该列表中产生一个 PCollection。该块在内部实现了expand方法(如下)。此外,过滤是为了去除一些我们不希望被导出的种类。最后,[Create](https://github.com/apache/beam/blob/master/sdks/python/apache_beam/transforms/core.py#L2906) 转换用于从种类列表中构建一个 p 集合。
接下来,对于每一种类型,我们必须构建一个查询——这是通过下一个 PTransform 块'create queries'实现的
rows = (p
| 'get all kinds' >> GetAllKinds(project_id, to_ignore)
| 'create queries' >> beam.ParDo(CreateQuery(project_id, param))
)
我们使用[ParDo](https://beam.apache.org/documentation/programming-guide/#pardo),它是一个通用的并行处理转换模块。它接受一个从beam.DoFn类派生的对象,该对象必须实现方法process(self, *args, **kwargs)。下面是CreateQuery类的process方法的实现。
def process(self, kind_name, **kwargs):
*"""* ***:param*** ***kwargs:* ***:param*** *kind_name: a kind name* ***:return****: Query
"""* logging.info(f'CreateQuery.process {kind_name} {kwargs}')
q = Query(kind=kind_name, project=self.project_id)
if kind_name in self.entity_filtering:
q.filters = self.entity_filtering[kind_name].get_filter()
logging.info(f'Query for kind {kind_name}: {q}')
yield q
上面的方法负责根据过滤参数生成获取元素的查询。一个简单的 YAML 配置文件用于定义过滤选项
这个解决方案的一个重要注意事项。数据存储中的实体需要有一些可用于获取记录子集的字段。在这个例子中,我们设置字段timestamp将用于获取记录的子集。如果管道每天执行一次,那么记录匹配查询(endTime-24h)<= timestamp < endTime将被选择。您可以想象任何其他类型的查询,而不仅仅是基于时间戳。例如,您可以将上次获取的记录的 id 存储在某个地方,下次只获取比存储的 id 大的记录。
接下来,我们向管道添加另外三个元素:
- 应用查询和提取实体
- 将实体转换为 JSON
- 将 JSONs 保存到 BigQuery
管道中的最后两个阶段非常明显:
从数据存储中读取使用上一步中创建的查询从数据存储中获取实体。结果,创建了来自数据存储的实体的 p 集合。接下来,在beam.Map(entity_to_json)中,每个实体都被转换成 JSON 表示。beam.Map是beam.ParDo的特例。它从 PCollection 中获取一个元素并生成一个元素。
管道的最后一个元素是输出 PTransform。没有经过过滤的实体被定向到一个空表中。另一个是从过滤中获得的,被附加到现有的表中。为了将元素导入这两个输出,我们使用了一个标记特性,它允许生成多个 PCollections。
如果种类名称在要过滤的选项中,那么我们用write_append标记元素,否则,我们将write_truncate标记附加到元素上。
接下来,我们将这两个拆分的集合写入 BigQuery:
在每种写方法中,我们使用SCHEMA_AUTODETECT选项。输出表名称是从种类名称动态派生的,如果需要的话。
如果您在 Google Dataflow 中运行管道,那么整个作业将如下所示:

数据管道(按作者)
所以当你调用运行管道的命令时,实际上会发生什么呢?基本上,如果你用 runner direct来做,工作流将会在你的本地机器上运行。
有了 runner dataflow,工作流将在 GCP 执行。首先,你的管道代码被打包成一个 PyPi 包(你可以在日志中看到命令python setup.py sdist被执行),然后zip文件被复制到 Google 云存储桶。下一个工人准备好了。工人无非是谷歌云计算的实例。您甚至可以在云控制台中看到它们:

而且,如果你需要的话,你可以进入它们。请注意,启动 workers、下载并在 workers 上安装您的管道需要时间。
事实上,Dataflow 中的最新特性提供了一个定制的 Docker 容器映像。这使您可以减少工作人员的启动时间(所有依赖项都已经打包到映像中),您可以使用不公开的第三方库,或者您可以在后台运行一些软件——没有限制。
当代码被安装在工人身上时,管道被执行。
为什么不是纯云功能?
让我来讨论一下为什么谷歌数据流而不是纯云功能。云功能可能是一个完全有效的解决方案,但最终,架构和维护会非常困难。首先,与满载模式相反,这里我们必须物理地获取记录,并将它们存储在云存储中。一个函数的内存限制是 4096 MB,最大运行时间是 9 分钟。因此,为了拥有一个健壮的、可伸缩的解决方案,我们必须对一批记录运行多个函数。您可以想象并行执行的树,对于每个页面结果,执行一个函数来获取记录的子集。然后并行的记录将被转换成 JSON 并加载到 BigQuery
为了跟踪并行执行的进度,可以使用与我在之前的文章中展示的类似的解决方案
https://medium.com/nordcloud-engineering/keep-track-on-your-cloud-computations-67dd8f172479
或者谷歌工作流。虽然组织起来是可行的,但我认为这种努力不值得可能的成本降低。
定价
嗯,您需要为执行时间(按每秒的增量计费)和资源付费。管道至少有一个工作线程,它消耗 vCPU、内存、存储和可选的 GPU。如果您的任务不是计算和存储密集型的,那么您可以通过调整工作选项来更改默认设置。默认情况下,用于批处理的 worker 的磁盘大小设置为 250 GB,用于流处理的 worker 的磁盘大小设置为 400 GB。如果你的处理可以适应内存,那么这是一个相当大的数字。在上面的例子中,我使用了每个工作人员 25 GB 的磁盘大小——这已经足够了。

价格估算,两个工人,每个工人 25GB,每月 vCPU 1 小时(由作者提供)
Google Dataflow 和 Apache Beam model 是一个强大的数据工程工具,允许构建复杂的数据管道。它可以用于批处理和流处理,具有不同的输入源和输出目的地。此外,工作被有效地无缝地分配给工人,没有任何调整。
我希望你喜欢这个故事,它会对你的日常工作有所帮助。如果您有任何问题或建议,请随时通过 Twitter 或 Linkedin 联系我。
将多个项目中的数据存储导出到 BigQuery
原文:https://towardsdatascience.com/export-datastores-from-multiple-projects-to-bigquery-3da9c92a4b8f?source=collection_archive---------20-----------------------

Nieuwpoort(作者)
如何使用 Google Dataflow 从多个项目中导出数据存储——对实体进行额外过滤。
这是对我之前的故事的一个简短扩展,在那里我描述了如何将数据从 Datastore 增量导出到 BigQuery。在这里,我将讨论如何将前面的解决方案扩展到在多个项目中拥有数据存储的情况。目标保持不变,我们希望在 BigQuery 中获得数据。
总的来说,这个问题可以用下图来表示

建筑草图(作者)
数据流过程可以存在于一个源项目中,也可以放在一个单独的项目中——我将把数据流过程放在一个单独的项目中。结果可以存储在 BigQuery 中,该 big query 可以位于与数据流流程相同的项目中,也可以位于另一个项目中。
一般化
让我们从概括开始。首先,我用两个新字段扩展了配置文件:SourceProjectIDs它只不过是源 GCP 项目的列表,而Destination定义了输出 BigQuery 数据集的位置。
SourceProjectIDs:
- project-a
- project-b
- project-c
Destination:
ProjectID: dataflow-streaming
Dataset: datastore_dev
扩展数据流管道定义如下:
rows = (
p
| **'projects'** >> beam.Create(project_ids)
| **'get all kinds'** >> beam.ParDo(GetAllKinds(prefix_of_kinds_to_ignore))
| **'create queries'** >> beam.ParDo(CreateQuery(entity_filtering))
| **'read from datastore'** >> beam.ParDo(ReadFromDatastore._QueryFn())
| **'convert entities'** >> beam.Map(entity_to_json)
)
它被扩展了一个额外的步骤projects,这个步骤产生了带有一个源项目列表的PCollection(来自配置文件)。需要对get all kinds步骤做一点小小的改动。GetAllKinds被改为 PTransform 步骤,为每个项目创建一个元组列表(project_id, kind_name)。
DoFn 的process方法接受元组作为任何其他可序列化的对象。至此,下一步create queries创建查询,从特定的kind_name获取记录,这些记录保存在project_id的数据存储中。
def process(self, project_kind_name, **kwargs):
*"""
:param **kwargs:
:param project_kind_name: a tuple with project_id, kind_name
:return: [Query]
"""* project_id, kind_name = project_kind_name
这个步骤产生的查询已经包含了project_id,所以我们不再需要传递项目 id。
存储在 BigQuery 中的 JSON 对象的模式在__key__字段中还包含一个属性project。BigQuery 中输出表的名称是通过在kind_name前面加上project_id来构造的。
都是权限的问题
所提出的解决方案的线索在于权限。数据流使用两个服务帐户(SA),一个在作业创建期间使用,另一个由工作实例用来访问资源。
我们对工人使用的 SA 感兴趣。默认情况下,当为您的项目启用计算引擎 API 时,会自动创建此服务帐户,并有一个标准名称<project-number>-compute@developer.gserviceaccount.com
因此,让您的数据流管道访问不同项目中的数据存储。因此,在每个源项目中,将帐户<project-number>-compute@developer.gserviceaccount.com添加到角色为role/datastore.viewer的项目中。

源项目之一中的 IAM 权限(按作者)
仅此而已——管道按预期工作:

具有项目步骤的扩展管道(作者)
数据流是自动化 ETL 过程的强大工具。正如你所看到的,它可以很容易地推广到与不需要与数据流过程存在于同一个项目中的源一起工作。
这个故事的灵感来自于安德鲁·弗莱舍提出的问题——谢谢!
我希望你喜欢这个故事,它会对你的日常工作有所帮助。如果您有任何问题或建议,请随时通过 Twitter 或 Linkedin 联系我。
从/向 Google 云平台导出数据
原文:https://towardsdatascience.com/exporting-data-from-google-cloud-platform-72cbe69de695?source=collection_archive---------6-----------------------
GCP 数据流管理指南
我已经在另一篇文章的中讨论了 GCP 的一些功能,这将指导你自动执行简单的代码块。
可能会派上用场的一件事是,不仅在 GCP 上执行代码,而且将代码的结果导出为一个文件,您可以在某个地方访问它。
注意,这里表达的观点是我自己的。我不是 GCP 认证用户,下面描述的过程是我自己做的,灵感来自网上的许多来源和花在云上的时间……可能有更好的方法来实现下面陈述的目标。
在这篇文章中,我将讨论两种不同的方法,它们将允许您将代码执行的结果导出到:
- 1)云桶(带有大查询表的选项):如果您需要对 GCP 执行额外的操作,例如对某些数据表执行常规的 ML 预测,大查询表将非常有用。如果您想获取可视化数据,也可以通过 Tableau 或 Power BI 等第三方软件访问它们。
- 2)Google Drive:Google Drive export 将允许您为许多用户提供简单可读的访问,如果数据要定期共享以供进一步个人使用的话。
- 3) FTP 服务器:您的组织可能正在使用 FTP 服务器来交换和/或存储数据。
1.导出到 GCP 存储桶
1)创建 GCP 存储桶
要在大型查询表上导出文件,您应该首先在 GCP 存储桶上导出数据。存储页面将显示当前存在的所有存储桶,并为您提供创建存储桶的机会。

进入云存储页面,点击Create a Bucket。参见文档配置铲斗的不同参数。
创建后,您存储桶将可以使用给定的名称my_test_bucket进行访问。
2)创建服务帐户密钥
我建议你使用一个service account key来监视和控制对你的桶的访问。您可以在这里创建一个服务帐户密钥,这将允许您将该密钥链接到您的存储桶。将文件下载和上传到存储桶时,需要使用密钥进行预先识别

密钥将是一个具有以下结构的.json:

3)在代码文件中输入 python 代码片段
创建目标存储桶和服务帐户密钥后,您可以使用 python 代码继续复制存储桶中的文件。
- 首先,我们导入所需的库。我使用了以下版本

2.然后,我们定义您正在使用的项目 ID,以及您之前生成的服务帐户令牌的路径。

3.我们现在可以使用令牌进行身份验证:

4.现在我们已经确定了,我们需要(I)定义目标存储桶(您在本教程开始时创建的)和(ii)定义放置区,即存储桶中文件将被复制到的位置。在我的例子中,我将文件复制到一个子文件夹/tmp 中(bucket 中没有真正的子文件夹,但它是这样显示的):

5.将文件上传到存储桶

如果您现在访问您的 bucket,您应该看到文件被复制到子文件夹/tmp 中。根据您的帐户被授予的权限,如果该文件夹不存在,您可能无法在存储桶中创建子文件夹。同样,您可能无法创建文件,而只能编辑它。如果您在上述步骤中遇到问题,请尝试复制 bucket 的根目录和/或手动上传一个具有相同文件名的文件。不过,我不能 100%肯定这些担忧是正确的。
4)可选:导出 BigQuery 表上的文件
我们只是将数据导出为云桶上的. csv 文件。但是,您可能希望在云中以表格格式保存数据。如果您想在表上启动 ML 管道,或者将第三方软件(例如 Tableau)连接到数据,这可能会很有用。
由于这些步骤在谷歌文档中有很好的描述,我就不在这里赘述了。然而,鉴于在 GCP 上存储数据的成本较低,我建议对你的数据进行云存储,这样你就可以随时访问它并自动使用它。
2.在 Google Drive 上导出
在 Google Drive 上导出文件有很多好处,特别是如果您组织中的用户不熟悉云技术和界面,并且喜欢使用旧的电子表格导航。然后,您可以将数据保存在.xlsx或.csv中,并将文件导出到用户可以访问的驱动器上。
我们将使用 PyDrive 来控制上传和下载文件到 Google Drive。您需要获得一个client_secrets.json文件,将您的代码链接到您想要复制文件的帐户。您可以按照此处描述的步骤轻松创建该文件。
我按照这些步骤在这里复制一个文件到 GDrive 上。这个解决方案的好处是只需要对 Google 帐户进行一次认证,这就创建了一个文件mycreds.txt。然后,在进一步的身份验证过程中使用该文件来绕过手动身份验证。

第一次执行此功能将会触发在您的浏览器中打开一个 Google 身份验证步骤。认证成功后,进一步执行导出将使用mycreds.txt文件直接访问驱动器并复制数据。
正如你可能已经注意到的,这个解决方案也可以用在 GCP 的其他平台上,或者只是本地的。要在 GCP 使用它,只需认证一次,生成一个mycreds.txt文件。如果您手动执行代码,那么请确保该文件存在于您的代码库中。如果您使用 Docker 定期自动执行您的代码,那么请确保将该文件复制到您的Dockerfile中。
就是这样!
哦,顺便说一下,使用相同的逻辑,从 GCP 的 Google Drive 获取数据也同样简单。参见下面的功能

3.在 FTP 服务器上导出
如前所述,您可能希望在 FTP 服务器上导出一些数据,以便进一步共享。例如,我必须为一个数据分析师团队设置一个每日数据导出,该导出应该在他们可以访问的 FTP 服务器上交付。数据将通过 API 调用获取,每天在 FTP 上进行管理和导出。
我用的是[pysftp](https://pypi.org/project/pysftp/),0.2.9 版本。
您只需要 FTP 服务器的hostname、username、password,这些应该由您的组织提供。您可以使用下面的函数将位于localpath的文件filename发送到 FTP 服务器。

与 GDrive 的情况类似,您可以使用同一个库将文件从 FTP 服务器获取到 GCP 供进一步使用。
下面的函数有一个额外的参数,ftp_subfolder参数,它提供了感兴趣的文件所在的子文件夹。

4.最后的话
就是这样!这三种方法将允许您实现从&到可能在您的组织中使用的几个数据中心的一些数据流。请记住,这些程序是我自己做的,可能还有改进的余地!😃
将这些方法与自动代码执行结合起来,应该可以让你部署简单的数据处理,让你的生活更轻松,工作更干净;)
一如既往,我期待评论&建设性的批评!
为求职者展示 NLP 程序
原文:https://towardsdatascience.com/exposing-nlp-routines-for-job-hunters-90e6aed661a3?source=collection_archive---------42-----------------------
从零开始逐个任务地进行数据科学
为求职者提供资源来检查关键字匹配

由汉娜·莱特在 Unsplash 上拍摄的照片
我正在写一个关于创建 NLP 应用程序来帮助人们改进简历的迷你系列文章。我在我的初始帖子中介绍了核心思想,然后我展示了如何将一个标记应用于特定的关键字,最近,我使用 Python 和 Vue.js 创建了一个 shell 应用程序。在这篇为求职者展示 NLP 程序的文章中,我们将关注于构建服务。这一系列为求职者提供了检查关键字匹配的资源,击败了招聘人员的关键字匹配筛选过程。在下一集加入我,修改代码库,全栈风格,并记录我们的进展。
用户情景和任务
在敏捷世界中,这相当于一个新的用户故事,这个故事有如下一些任务。
故事:作为求职者,我希望能够复制&粘贴一份职位描述,并看到突出显示的关键词/短语。
任务:
- 后端:将 NLP 类添加到 web 服务器
- 后端:添加一个接收文本、调用 NLP 类并返回关键字的 web 服务路由
- 前端:增加了一个文本区域、表单和提交按钮,允许用户复制和粘贴文本。
- 前端:添加一个 API 调用,将文本段传递给后端,收集响应,重绘 UI。
用投资框架测试我们的故事
- 独立的:我们的故事应该是独立的,不依赖于其他功能。我们将不得不修改几个脚本文件,但故事是独立的。
- 可协商的:我们的故事紧紧围绕谁、什么和为什么,但我们避免任何实施细节。UX 的设计者和开发者可以自由设计和制造产品。
- 有价值的:一旦我们发布了故事,一个简单的复制&粘贴练习将突出工作描述中的关键词和短语。这样的服务对大多数人来说是很看重的,也是很手工的。
- 可估计:如果在最早的阶段,我已经可以看到最多有五个源文件需要修改,那么调整大小就很简单了。
- 小:如果你关注我的工作,你会知道我练习极度敏捷,使用 3-4 小时的时间盒。我会说,在我通常的时间范围内,这是一个很好的故事。
- Testable :我们已经知道结果应该是什么——文本应该带有文本标记,显示由后端的自然语言处理算法识别的关键词。
接下来,我们需要定义我们的验收测试来帮助我们
验收测试
- 用户可以导航到允许用户输入工作文本的表单
- 用户可以复制并粘贴作业文本,然后按提交按钮来执行分析
- 一旦按钮被按下,文本段落通过 NLP 例程被发送进行处理,并且从管道有一个很好的响应时间
- 用户得到结果,并在屏幕上看到突出显示的关键词。
也许,作为一名数据科学专业人员,你还没有接触过作为敏捷团队的一部分编写特性、故事、史诗或任务级分解。如果您有经验,您可能知道我们正在处理 backlog,整理它,与我们的开发人员进行对话,并在迭代计划之前进行我们的规模估算。作为一名作家,我很容易走捷径,因为我是一个只有我的 scrum 团队。这一部分描述了一个典型的 backlog 整理和迭代计划讨论。通常一个故事点相当于一个人-天的工作量。因此,让我们假设这是一个故事点,但是该特性对我们的服务消费者有价值,因此我们正在优先考虑,它将在 sprint 结束时发布。因为我是一个团队,只有我一个人,所以这是全栈开发的一个例子。
后端任务
作为后端开发人员,有两项任务分配给我
- 后端:将 NLP 类添加到 web 服务器
- 后端:添加一个接收文本、调用 NLP 类并返回关键字的 web 服务路由
添加 NLP 类可能是最简单的,因为我已经定义了自己的 Python 类。
from nlp import nlp
LangServices = nlp()
添加 web 服务路由有点复杂
展示 Python Flask web 路由和相关服务功能的作者工作的图示
所以我创建了一个名为 keywords 的 web route,它允许 HTML post 或 get 操作,并期望以 json 对象的形式获取数据。具体来说,json 对象需要
{'text': 'the text given by the user as part of the job definition they are reviewing'}
文本值被传递给 NLP 类,该类用一个关键字列表进行响应,这些关键字被包装在一个 JSON objected 中,并被传输到浏览器(前端)。
前端任务
作为前端开发人员,有两项任务分配给我
- 前端:增加了一个文本区域、表单和提交按钮,允许用户复制和粘贴文本。
- 前端:添加一个 API 调用,将文本段传递给后端,收集响应,重绘 UI。
构建用户界面需要一些设计和 HTML 编码。首先,让我们看看我做的设计,然后我们需要讨论 HTML。

项目 UI 更改的初始模型的作者图片
由于我们处于原型模式,UI 设计保持简单。我们有一个大的文本区域,用户可以复制和粘贴文本。他们可以使用提交按钮来处理文本,或者使用重置按钮来清除文本。Vue.js 组件使用一个叫做模板和脚本的概念。你可以在下面的要点中看到代码。
作者作品中的 HTML 代码示例
我们使用 bootstrap 作为设计系统,模板主要用于绘制表单。
有两种方法来维护表单。“OnReset”用于重置按钮按下,而“getKeywords”用于提交按钮功能。如果您需要查看链接,下面是将事件处理程序链接到事件的 HTML 代码行。
<b-form @submit="getKeywords" @reset="onReset" v-if="show">
“onReset”方法仅用于擦除表单文本并恢复默认值。“getKeywords”稍微复杂一点,但是概括起来就是它对/keywords 路径进行 API 调用,将文本作为 JSON 对象传递。然后,该方法注册承诺的响应,节点事件循环设法从后端收集响应并处理它。返回的关键字列表将替换“查询”变量中存储的默认关键字列表。你可以在下面看到所有的默认值。Vue.js 是动态的,数据变化会强制“dom 更新”。
return {
queries: ['birds','scatt'],
description: 'Tropical birds scattered as Drake veered the Jeep',
form: {
text: ''
},
show: true,
payload: {'text': ""}
}
至此,我已经完成了作为这个故事的开发者分配给我的四项任务。我应该担心编写测试用例以及更新任何自动化测试,但是这并不在本文的范围之内。我需要我的测试作为任何开发-运营工作流中的发布条件,导致自动化的 bug 发布和/或代码库更新。现在剩下的就是测试了。
测试
有四个测试条件被定义为故事的验收标准
- 用户可以导航到允许该用户输入工作文本的表单。这个测试通过了,因为我们有一个基本的布局,有一个文本区和几个按钮来操作它。
- 用户可以复制并粘贴作业文本,然后按下提交按钮进行分析。是的,复制&粘贴功能可用,并且我们有提交按钮。
- 一旦按钮被按下,文本段落将通过 NLP 例程进行处理,并且从管道中有一个良好的响应时间。你可能会笑,但是 的响应时间在我的开发机器上是完美的 ,但是 在 dev 中一切都正常!
- 用户得到结果并看到在他们的屏幕上高亮显示的关键词。是的,我的用户获得了他们的工作;对他们来说,这几乎是实时的,他们可以看到关键词和短语被突出显示。
作为一个更详细的测试,我去找了一份工作描述并试了一下。

作者展示了一个张贴职位描述并查看突出显示的关键词的测试的图片。
测试表明这个故事已经完成,并且会被产品负责人接受,他也是我!
关闭
在这篇文章中,我继续我的概念,通过使用自然语言处理例程来帮助求职者加快任何工作申请的关键字分析部分。多年来,我与同事和朋友讨论过机器学习和人工智能从业者如何需要多学科技能,使他们能够快速构建原型并从经验中学习。许多人表示并同意这一需要,因此这是我的贡献。我希望你喜欢这个完整的系列,但是如果你有问题或者对这个系列感兴趣,请随时联系我。
在亚马逊 EKS 上公开 Tensorflow Serving 的 gRPC 端点
原文:https://towardsdatascience.com/exposing-tensorflow-servings-grpc-endpoints-on-amazon-eks-e6877d3a51bd?source=collection_archive---------22-----------------------
gRPC,带 kubernetes、nginx 和 tensorflow 服务
Tensorflow serving 是一种流行的方式,用于打包和部署在 Tensorflow 框架中训练的模型,以进行实时推理。使用官方的 docker 映像和经过训练的模型,您几乎可以瞬间启动一个容器,公开 REST 和 gRPC 端点来进行预测。
关于 tensorflow 服务的大多数示例和文档都集中在流行的 REST 端点使用上。很少有人关注如何适应和使用 gRPC 端点用于他们自己的用例——更少有人提到当您扩展到 kubernetes 时这是如何工作的。

真实活码头鲸的罕见镜头——由托德·克雷文在 Unsplash 上拍摄
在这篇文章中,我将简单介绍 gRPC、kubernetes/EKS、nginx,以及我们如何将它们与 tensorflow 服务结合使用。
为什么 gRPC 超过 REST?
有很多原因。首先,gRPC 使用高效的 HTTP 2 协议,而不是传统的 HTTP 1。它还使用语言中立的序列化协议缓冲区,而不是 JSON,这减少了序列化和反序列化大型 JSON 有效负载的开销。
有些人谈论 API 定义和设计的好处,其他人谈论 HTTP 2 的效率——但这里是我在机器学习部署方面使用 gRPC 的经验:
- gRPC 非常高效。通过使用 protobufs,它可以显著减少推理时间和大型 JSON 负载的开销
- 生产中的 gRPC 有一个相当大的学习曲线,要弄清楚它是一件非常痛苦的事情
简而言之,gRPC 可以提供巨大的性能优势。但是作为一个比较随便的 API 开发者,我可以肯定地说不——它并不比 REST“更容易”。对于一个刚接触 gRPC、HTTP 2 和 nginx、TLS/SSL 以及 tensorflow 服务的人来说,有很多问题需要解决。
不过好处还是值得的。我看到在进行初始负载测试时,大批量的推理时间减少了 80%。对于在推理时间方面有严格服务水平协议(SLA)的团队,gRPC 可以成为救命稻草。虽然我不会在这里更深入地解释 gRPC,但是互联网上有很多帮助你入门的教程。
在 AWS 上设置 Kubernetes 集群
让我们开始设置一个 kubernetes 集群。我们将使用 AWS EKS 和eksctl命令行实用程序。在很大程度上,如果你没有 AWS 账户,你也可以在 Docker Desktop 上关注 kubernetes。
如果你还没有安装eksctl或aws-cli,你可以使用这个库中的 docker 镜像。它还附带了用于与我们的集群交互的kubectl。首先,构建并运行容器。
docker build -t eksctl-cli .
docker run -it eksctl-cli
在新容器中启动 bash 会话后,使用首选用户和 AWS 访问键 id 登录 AWS:
aws configure
你需要一个密钥、秘密密钥、默认区域名(我用的是us-east-1)和类似 json 的输出格式。现在,我们可以检查集群的状态。
eksctl get clusters
如果你没有像我一样的活跃集群,你会得到No clusters found。这样的话,让我们创建一个。
eksctl create cluster \
--name test \
--version 1.18 \
--region us-east-1 \
--nodegroup-name linux-nodes \
--nodes 1 \
--nodes-min 1 \
--nodes-max 2 \
--with-oidc \
--managed
如果你愿意,你可以改变参数,但是因为这只是一个例子,所以我把集群的大小留得很小。这可能需要一点时间。如果您转到控制台,您将看到您的群集正在创建。

确保您使用正确的用户登录,并查看正确的区域
现在我们的集群已经完成,我们可以安装 nginx 来实现入口和负载平衡。让我们测试一下kubectl是否按预期工作。以下是一些命令示例。
kubectl config get-contexts
kubectl get nodes
您应该看到当前的集群上下文类似于eksctl@test.us-east-1.eksctl.io,还可以看到由我们的命令创建的节点。如果您以适当的权限登录(即创建群集时使用的用户),您可以在控制台中查看类似的内容。

集群很好,所以让我们安装 nginx。回到 docker 容器中的命令行:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.43.0/deploy/static/provider/aws/deploy.yaml
如果你使用的是除 AWS 之外的 kubernetes 提供者,查看 nginx 安装说明(Docker Desktop k8s 有一个)。这个命令将为 nginx 创建所有必要的资源。您可以通过查看新名称空间中的窗格来检查它是否已经启动并正在运行。
kubectl get po -n ingress-nginx
我们最感兴趣的是,我们是否为我们的集群创建了一个具有外部可达 IP 地址的负载平衡器。如果您运行此命令:
kubectl get svc -n ingress-nginx
你应该看到nginx-ingress-controller有一个类似something.elb.us-east-1.amazonaws.com的外部 IP。如果您将它复制并粘贴到浏览器中,您应该会看到这个。

从没这么开心得过 404 分
太好了。我知道这看起来不太好,但我们实际上所做的是创建进入我们集群的适当入口,我们可以将事情暴露给web。
在 Kubernetes 上部署 Tensorflow 服务
Tensorflow 有一些关于将模型部署到 kubernetes 的可接受的文档,并且很容易创建您自己的映像来为定制模型服务。为了方便起见,我将经典的half_plus_two模型推送到 dockerhub 上,这样任何人都可以在这个演示中使用它。
下面的 YAML 为一个简单的 k8s 应用程序定义了部署和服务,该应用程序公开了 tfserving grpc 端点。
要在我们的集群中部署它,只需从命令行应用原始 YAML。
kubectl apply -f [https://gist.githubusercontent.com/kylegallatin/734176736b0358c7dfe57b8e62591931/raw/ffebc1be625709b9912c3a5713698b80dc7925df/tfserving-deployment-svc.yaml](https://gist.githubusercontent.com/kylegallatin/734176736b0358c7dfe57b8e62591931/raw/ffebc1be625709b9912c3a5713698b80dc7925df/tfserving-deployment-svc.yaml)
另一个kubectl get po将显示我们的 pod 是否创建成功。要确保服务器已启动,请检查日志。你应该看到像Running gRPC ModelServer at 0.0.0.0:8500和Exporting HTTP/REST API这样的东西。gRPC 是我们通过 pod/服务提供的唯一产品。
kubectl logs $POD_NAME
在通过 nginx 公开之前,让我们确保服务能够工作。将 tensorflow 服务转发到您的本地主机。
kubectl port-forward service/tfserving-service 8500:8500 &
那么我们有多种方法可以检查服务是否在运行。最简单的就是与grpc Python 客户端建立一个不安全的连接。
如果您在 docker 容器中输入 Python shell 并运行上面的代码,您应该能够不安全地连接到 grpc 服务。
>>> grpc_server_on(channel)
Handling connection for 8500
True
这意味着服务正在按预期工作。让我们看看我们可以调用的更多 tensorflow 特定方法。在你的 docker 容器中应该有一个名为get_model_metadata.py的文件(如果跟随其他地方这里是链接)。让我们运行它并检查输出。
python get_model_metadata.py
抱歉,太长了
哇,一大堆难以处理又不方便的信息。最能提供信息的部分是{'inputs': 'x'...部分。这有助于我们制定适当的预测请求。注意—我们在这些 Python 脚本中实际做的是使用 tensorflow 提供的库来生成预测 protobufs,并通过我们不安全的 gRPC 通道发送它们。
让我们利用这些信息对 gRPC 进行预测。在您当前的目录中,您还应该有一个get_model_prediction.py文件。
运行并检查输出。您将看到一个类似 json 的响应(它实际上是一个 tensorflow 对象)。
outputs {
key: "y"
value {
dtype: DT_FLOAT
tensor_shape {
dim {
size: 3
}
}
float_val: 2.5
float_val: 3.0
float_val: 4.5
}
}
model_spec {
name: "model"
version {
value: 1
}
signature_name: "serving_default"
}
我们已经在 gRPC 上做了第一次预测!太神奇了。在我看来,所有这些在 tensorflow 服务器端都没有得到很好的记录,这使得开始变得很困难。
在下一节中,我们将通过 nginx 和我们的公共 URL 实际公开我们的 gRPC 服务。
通过 Nginx 公开 gRPC 服务
走 nginx 路线的关键不同之处在于,我们不再能够建立不安全的连接。nginx 将要求我们通过端口 443 为我们的域提供 TLS 加密,以便访问我们的服务。
由于 nginx 默认启用 443 端口上的http2协议,我们不应该在那里做任何改变。但是,如果您在端口 80 上有现有的 REST 服务,您可能希望在 nginx 配置中禁用 ssl 重定向。
kubectl edit configmap -n ingress-nginx ingress-nginx-controller
然后补充:
data:
"ssl-redirect": "false"
并保存。
创建 TLS 机密
要为您的域创建 TLS,您可以做一些类似于这个的事情。正确的 TLS/SSL 涉及一个认证机构(CA ),但这超出了本文的范围。
首先创建一个cert目录,然后创建一个 conf 文件。
编辑 DNS.1 ,使其反映您的 ELB 的实际主机名(我们之前在浏览器中看到 nginx 时使用的 URL)。不需要编辑CN。
使用这个,创建一个密钥和证书。
openssl genrsa -out cert/server.key 2048
openssl req -nodes -new -x509 -sha256 -days 1825 -config cert/cert.conf -extensions 'req_ext' -key cert/server.key -out cert/server.crt
然后使用它们在默认名称空间中创建新的 kubernetes secret。我们将在入口对象中使用这个秘密。
CERT_NAME=tls-secret
KEY_FILE=cert/server.key
CERT_FILE=cert/server.crt
kubectl create secret tls ${CERT_NAME} --key ${KEY_FILE} --cert ${CERT_FILE}
最后,我们为 grpc 创建带有必要注释的入口对象。注意,我们在入口中指定了tls-secret。我们还在进行路径重写,并在/service1上公开我们的服务。通过按路径划分我们的服务,我们可以通过 nginx 公开不止一个 gRPC 服务。
用你的网址替换— host:。您可以应用上面的 yaml,然后编辑结果对象,反之亦然。这种方法非常简单:
kubectl apply -f [https://gist.githubusercontent.com/kylegallatin/75523d2d2ce2c463c653e791726b2ba1/raw/4dc91989d8bdfbc87ca8b5192f60c9c066801235/tfserving-ingress.yaml](https://gist.githubusercontent.com/kylegallatin/75523d2d2ce2c463c653e791726b2ba1/raw/4dc91989d8bdfbc87ca8b5192f60c9c066801235/tfserving-ingress.yaml)
kubectl edit tfserving-ingress
现在,在默认名称空间中有了一个入口。我们不能像以前那样使用不安全的连接进行连接,因为此端点指定了 TLS。我们必须使用刚刚创建的证书建立一个安全的连接。
好的方面是,如果我们有这个证书,我们现在可以从任何地方连接——我们不再需要kubectl将服务转发到我们的容器或本地机器。它是公开的。
用您生成的证书替换crt_path,用您的 URL 替换host。
请注意我们公开服务的定制路线。默认情况下,Tensorflow 服务在/tensorflow.serving.PredictionService上是可用的,但是如果我们将它们都暴露在同一个域上,这就很难添加新的服务。
gRPC 仅连接到主机和端口,但是我们可以使用任何我们想要的服务路由。上面我使用了我们在 k8s 入口对象中配置的path:/service1,覆盖了 tensorflow serving 提供的基本配置。当我们调用上面的tfserving_metadata函数时,我们指定/service1作为参数。
这也适用于进行预测,我们可以很容易地建立一个安全的通道,并通过它使用正确的主机、路由和证书对我们的服务进行预测。
同样,我们用自定义路径覆盖路线,将我们的数据转换成张量原型并进行预测!简单得要命…哈哈不尽然。你不会在 tensorflow 文档中找到这些(或者至少我没有),而 nginx 中 TLS/gRPC/HTTP 2 等不熟悉的工具的加入让这变得更加困难。
当你完成后,删除你的集群,这样你就不会被收费。
eksctl delete cluster test
唉,我们完了
希望这对您有所帮助,从各种文章、源代码、堆栈溢出问题和 github 问题中拼凑起来确实很困难。
可能值得注意的是,我们在这里没有做任何实际的 CI/CD,我不推荐在产品中使用kubectl edit——但是这些是其他文章的主题。如果我使用了任何不恰当的术语,或者有你认为可以解释得更好的东西,请联系我!很乐意在 LinkedIn 或 twitter 上联系,或者直接在媒体评论区查看。
✌🏼
说明性数据分析:工具包
原文:https://towardsdatascience.com/expository-data-analysis-1-40d47acb057c?source=collection_archive---------43-----------------------
计算笔记本以及如何设置它们

增强了 Windows 终端的 Visual Studio 代码。(图片由作者提供)
看看你能不能认出这个。这是星期天早上。你刚刚给出了一个庞大的数据集,比如说你的一个产品在过去 6 个月的大量使用数据。数据集每天有一个 Excel 文件,每个 XLSX 的大小约为 4MB。你被告知有…四天时间向首席执行官提交你的初步调查结果。你看,不仅仅是数据科学家处理大量数据。有时候,产品经理也会这样做。
你将如何着手执行这项任务?你需要三种资源:合适的人、合适的技能和合适的工具。先说工具。
疯狂中的方法——说明性数据分析
事实是,数据科学项目有一定的节奏,如果你愿意,可以称之为生命周期。虽然存在其他更复杂的方法,但现在,请观察来自哈佛大学数据科学入门课程的这个相当简单的流程:

每个人都知道思想泡泡捕捉你的真实想法。(图片来自模块的课堂笔记)
考虑一下这里的箭头。考虑一下他们如何像一个数据驱动的游戏蛇和梯子(或者 వైకుంఠపాళి ,我们小时候习惯这么叫它)。还要考虑图中最后的勒索:能讲个故事吗?
数据科学方法有时可能是不确定的。你将需要迭代,直到你得到你想要的结果。你需要快速叙述一下。正因为如此,快速执行基本任务,同时能够连贯地解释这些步骤是有好处的。我认为,在许多现实世界的案例中,探索性数据分析实际上是说明性的数据分析:你不仅仅是探索数据集,而且还要解释数据中的可能性/异常等。这通常会很困难,尤其是当您在版本控制或者没有合适的工具时。
计算笔记本
大多数数据科学家通常使用计算笔记本进行数据探索/展示。正如《自然》 的这篇文章所说,你需要相当于实验室笔记本的东西来进行科学计算。就像基因科学家(例如)将 DNA 凝胶与实验室协议一起粘贴一样,数据科学家“粘贴”数据,编写代码,编写解释,生成图表和其他形式的可视化,以记录他们的计算方法。
这是一个驱动编码、探索和文档的范例。使用计算笔记本,数据科学家执行代码,看看发生了什么,反复修改和重复,同时记录他们的想法,与他们(和他们的产品经理)的对话!)和数据。这在团队内部以及主题、理论、数据和结果之间建立了更强大的联系。
工具
对于那些喜欢使用 r 的人来说,计算笔记本可以是那些在 RStudio 中的笔记本。一些团队也使用 Databricks workspaces 利用云集群和直接与 SQL 等语言中的数据湖对话的能力。
然而,最受欢迎的计算笔记本是 Jupyter,其在 GitHub repos 中的使用量从 2015 年的 20 万台笔记本飙升至 2018 年的 250 万台(来源)在这一点上,可以说 Jupyter 是数据探索的事实上的标准工具。Jupyter 笔记本将阐述与计算结合在一起,具有大量文章的功能,如标题格式、项目符号列表等,同时还具有在文本中嵌入代码的能力(或者反之亦然,如果你愿意)。然而,最明显的优势是您可以将代码的执行(“内核”)与代码本身分开。所以你可以在网络浏览器中输入你的代码,但是把它链接到在某个超级计算机集群上执行的内核。
朱庇特的问题
Jupyter 笔记本可能是自切片面包以来最受欢迎的计算笔记本,但它们也有批评者。其中最突出的是 Joel Grus,他在 2018 年的 JupyterCon 上展示了 144 张幻灯片。
他讲话的摘要:
- 状态:要让笔记本正常工作,你必须按顺序执行计算单元。如果你不这样做,事情就坏了。这对于用户来说并不是显而易见的。
- 非模块化代码 : Jupyter 笔记本不强迫用户写模块化代码。
- 不写单元测试:大多数数据科学家跳过写单元测试或者忽略测试驱动开发的原则。使用 Jupyter 笔记本鼓励这一点。
- 没有智能感知/自动完成:大多数成熟的软件工具都有帮助你写代码的功能,通过(过去被称为)智能感知。Jupyter 笔记本没有那个。
- 不兼容的内核:你可以很容易地在一个特定的内核上写一个笔记本,但是在另一个内核上执行。这可能会导致严重的混乱。
- 阅读其他笔记本:阅读其他人制作的笔记本通常是一种痛苦,因为你不确定他们使用的是什么版本的库。
除此之外,我还要补充一点:
- Jupyter 内核与 Jupyter 外壳断开:这意味着:您安装的库可能不是特定 Jupyter 外壳正在使用的库。最坏的情况是,在安装正确版本的库之前,您可能必须关闭本地 Jupyter 内核。
简而言之,许多 Jupyter 笔记本用户有一种忽视软件工程的既定原则的趋势,如分解、状态管理或测试。因此,这会影响数据科学团队快速重现或复制结果的能力。
解决方案
作为一名前世的软件架构师,我忍不住点头同意 Grus 的批评。同时,作为一名产品经理,我看到了说明性数据分析的价值,快速迭代和解释结果的需要,而不总是依赖于数据科学家。或者即使我读了,我也希望能够快速阅读他们的作品。
幸运的是,有一个解决方案。
步骤 0:安装 conda
如果你已经涉足 Python,你会想跳过这一个。一般的方法是为大多数 Python 相关的任务安装 conda 。Conda 的常规安装在一个单独的设置中安装所有的东西——最新的 Python 库、Jupyter 笔记本服务器、VS Code、Spyder 等等。这里面有细微差别,32 位和 64 位 conda 之间,Python 2.7 和 Python 3.x 之间,甚至 Anaconda 和 Miniconda 之间。本文(及其他)深入探讨。确保您已经安装了 Visual Studio 代码。

嘿,JetBrains 和 IBM Watson 在 Anaconda 做什么?我应该用迷你康达,可能。(作者截图)
但是等等。先不要推出 Jupyter 笔记本!小心第一步。
步骤 1:创建一个环境
取决于你在哪里,这里是开始变得沉重的地方。这被称为非常具有挑战性的话题,但是用 Python 处理的正确方法是建立一个虚拟环境。事实上,让我强调一下:所有好的 Python 代码库必须必须有一个虚拟环境设置。许多人忽视这一点,后果自负。
为什么?Python 是关于同时使用多个库的。这给我们带来了三个非常具体的挑战:
- 知道使用什么库:即使是一个简单的分析也可能需要使用多个库。没有一个详尽的列表,您将会错过一些东西,从而破坏代码。
- 知道使用什么版本的库使用什么版本的库:Python 世界的发展速度确实非常快,所以最新版本的库可能无法与您使用的版本兼容。
当你打算分享你的笔记本电脑时尤其如此。这些是我和我的团队留下的战斗伤疤:打开 Jupyter 笔记本,意识到团队在过去几个月里煞费苦心开发的精心制作的代码片段无法在你的笔记本上运行,这些伤疤就形成了。现在已经是周日下午了。
正确的方法是(不,不要惊慌)从一开始就创建虚拟环境。在启动 Jupyter 笔记本之前,您应该为项目创建一个文件夹,然后在其中创建一个虚拟环境。
你可以这样做:
- (强烈推荐,不过可以随意跳过)如果你在 Windows 上,
- 通过 Windows Store 安装全新的 Windows 终端。
- 将 Anaconda PowerShell 添加到 Windows 终端。
- (奖金 : 美化它使用电力线,书呆子字体,卡斯卡迪亚代码等。)
2.如果您不在 Windows 上或者跳过了步骤 1,请打开 Anaconda 的提示符。如果没有,请在 Windows 终端中打开 Anaconda PowerShell。导航到您创建的文件夹。
强烈建议在特定的文件夹位置创建一个环境。如果RainInStraits是您想要的文件夹名称(嗯,对我来说是这样),您会想要执行以下操作(不用补充,您不必键入以#开头的注释):
❯ mkdir RainInStraits
#This generates the folder❯ cd RainInStraits
#Navigate to the newly created folder❯ conda create --prefix ./envs jupyter matplotlib numpy
最后一个命令做了一些非常有趣的事情:它将创建一个环境作为子文件夹,并将我在那里提到的所有库安装在该环境中。这很好。现在,您可以开始使用 Jupyter 笔记本了。
步骤 2:用 Visual Studio 代码创建一个 Jupyter 笔记本
是的,你没看错。您想要启动 Visual Studio 代码(您已经在前面的步骤中安装了它,不是吗?)首先。
- 打开您之前创建的文件夹:

我能说什么呢,我是守旧派。我更喜欢黑暗模式下的文本编辑器。就像过去 CGA 显示器的美好时光一样。(作者截图)
2.然后,点击Control + Shift + P(或 Mac 中的等效键)这将打开命令面板。选择Jupyter: Create New Blank Notebook

是的,我最近一直在创作许多 Jupyter 笔记本。为什么这么问?(作者截图)
3.快到了!点击屏幕右侧的内核部分:

眨眼就错过了。(作者截图)
4.选择您刚刚创建的虚拟环境。您将能够根据它的路径选择它。

请注意,我们刚刚创建的环境没有名称。你必须通过文件夹来识别它。(作者截图)
5.为了卫生起见,对屏幕左下方的解释器进行同样的操作,并再次选择正确的环境:

虚拟环境在上面。底层是相同的虚拟环境。(作者截图)
6.你准备好了!
第三步:使用 Jupyter 笔记本进行阐述性数据分析
这可能是数据科学家强烈反对我的地方。因此,我认为 Jupyter 笔记本的关键在于它是一个笔记本— 它是一段文字,应该如此对待。换句话说,它有一个标题,一个执行摘要,解释和文字内容,应该让非数据科学家或编码人员容易阅读。
Jupyter 笔记本能够在单元格中嵌入降价文本,但当你从文本切换到图形时,它们才真正大放异彩。他们必须通过代码完成的事情是偶然的——我们不应该忘记这一点。
这是我的一个笔记本的例子,我在探索收益的波动性等等。很多笔记——还有一些数学——最后是一些 Python 代码。

乳胶并不难。只是需要习惯。呃,还有,这是拼写乳胶。(作者截图)
我已经选择了这个虚拟环境,并执行了这段代码以获得:

编译错误在黑暗模式下不那么可怕。真实的事实。(作者截图)
哦!我忘记在虚拟环境中安装熊猫包了。在“常规”Jupyter 设置中,我必须停止本地服务器,安装软件包,然后重启服务器。但是有了虚拟环境和 Visual Studio 代码,我切换到打开的 Windows 终端窗口,安装 pandas,然后切换回来。

并行 windows ftw!(作者截图)
代码执行完美!

南更聪明。(作者截图)
功能丰富

按一个点(。)还有……魔法!(作者截图)
- 但是等等,我没提到最精彩的部分。你现在有…自动完成。
2.当您甚至通过 Matplotlib 生成图形时,您可以轻松地进行滚动。

1.点击展开图片(作者截图)

2.在单独的选项卡中滚动到您的核心内容(作者截图)
3.使用正确的扩展,您可以高效地浏览数据:
- Excel Viewer :读取 VS 代码中的 CSV 和 Excel,就像在 Excel 中一样:

来源:分机的网页
2。sand dance:CSV 数据的可视化:

来源:扩展的网页
4.还有一件事。(总有多一件事吧?)调试。

还有人会对调试器感到兴奋吗?(作者截图)
您可以在一个弹出窗口中查看内核中的所有活动变量。您不仅可以检查单个变量,还可以扩展数据框架。过滤,分类,玩他们。WUT。

比 Excel 好,不是吗?(作者截图)
是的,你可以在一个单元格中点击 F10,然后逐行执行代码。

逐行调试就像对代码进行逐行注释一样。(作者截图)
俏皮!年轻的学徒,这就是我们的方式。
导出环境
既然您已经设置了环境和工具,那么您可能希望与其他人共享您的工作。这就是拥有专用虚拟环境的真正优势所在。在你分享你的*。ipynb 文件,您需要导航回您打开的 Windows 终端窗口,导航到您之前创建的 envs 文件夹,并键入以下内容(随意将文件名从environment.yml更改为更合适的名称):
❯ conda env export --from-history > environment.yml
当您分享您的*。ipynb 文件,一定要共享这个 yml 文件。这样,您的收件人也能够在他们结束时恢复您的环境。他们需要在命令行中输入以下内容:
❯ conda env create -f environment.yml
注意:理论上,也许可以点击conda env export > environment.yml,但是,这将列出所有作为你选择的依赖项安装的库。这些依赖关系可能依赖于操作系统,并且可能无法在所有平台上正常运行。添加一个--from-history将库限制为您自己安装的库。这使得 conda 可以选择特定于接收方平台的依赖关系。
好了,各位。通过这种设置,您可以:
- 监控状态:逐行调试监控变量,哟。
- 智能感知/自动完成:它就是工作(tm)。
- “已知的”内核:你清楚该使用什么内核以及如何使用。
- 阅读其他笔记本:阅读其他人制作的笔记本通常是一种痛苦,因为你不确定他们使用的是什么版本的库。
- 依赖控制:始终将正确的库集更新到正确的版本,包括在您共享笔记本电脑时。
引用 Edgar Dijikstra 的话来说,如果你不遵循软件工程的既定原则,Jupyter 笔记本可能会被认为是有害的。但是有了正确的工具和正确的原则,它们实际上可以帮助你进行探索性的数据分析。
在接下来的几篇文章中,我将进一步解释一种更有效的共享包含 Docker 容器的笔记本的方式,并且将简要地涉及数据分析的说明性方面。现在,尽情享受这是一个全面的工具包来执行说明性数据分析。
借助 MediaPipe 的机器学习模型,在网络浏览器中实现精致的手和手指跟踪
原文:https://towardsdatascience.com/exquisite-hand-and-finger-tracking-in-web-browsers-with-mediapipes-machine-learning-models-2c4c2beee5df?source=collection_archive---------13-----------------------
了解这个精通 JavaScript 风格的计算机视觉库,这里重点介绍它的手部跟踪工具。有了这个,你的网络应用程序可以检测和跟踪每只手的 21 个点,实时获得每个点的流体 x,y,z 坐标。好几个手!也可以看看他们的其他基于 ML 的计算机视觉工具。
快速链接
- 简介
- 动手测试
- 在 MediaPipe 的 CodePen 示例中测试更多高级功能
- 手跟踪工具的性能
- media pipe 的所有跟踪工具
- 想象潜在应用的范围
介绍
MediaPipe 只使用设备的常规网络摄像头,为增强现实应用提供基于跨平台机器学习的解决方案。具体来说,它提供了各种(脸、手、身体等。)检测和跟踪算法,允许程序员生成令人惊叹的视觉效果。最棒的是,这些工具中的许多都受 JavaScript 支持,这意味着您可以为您的 web 应用程序和页面添加出色的功能。
在最近发布了在我的电脑和智能手机上运行良好的文档和示例之后,我在这里重点介绍 MediaPipe 的 JavaScript 手部跟踪库。该工具可以检测和跟踪视频馈送中的手,并为每只手返回 21 个节点的{x,y,z}坐标(相当于每根手指 4 个关节加上 1 个手掌)。
动手测试
通过简单地复制和粘贴 MediaPipe 提供的最少代码,我在 30 秒内就让它在我的网站上运行了。确保通过 https 提供服务,因为它需要访问网络摄像头。这是它在 Firefox 上运行的第一个证据:

首先在浏览器中测试 MediaPipe(这里是 Firefox,但它在 Chrome 中也能完美运行)。在https://lucianoabriata . alter vista . org/tests/media pipe/hand-tracking-with-web cam-simplest . html播放并查看代码。注意:要查看估计的手部模型,请向下滚动到画布,这可能需要几秒钟才能显示出来!作者 Luciano Abriata 截图。
使用这个网页,我进一步测试了它能多好地检测各种姿势的手…如你所见,它能全部正确!即使是那些手部姿势本质上包含遮挡的姿势。

测试不同的手部姿势,包括一些包含相当多遮挡的姿势。由作者 Luciano Abriata 的截图组成。
您可以在此链接中自己运行此示例:https://lucianoabriata . alter vista . org/tests/media pipe/hand-tracking-with-web cam-simplest . html
注意:要查看估计的手部模型,请向下滚动到画布,这可能需要几秒钟才能显示出来!
这是代码。看看你需要写的有多少:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="[https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js](https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js)" crossorigin="anonymous"></script>
<script src="[https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js](https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js)" crossorigin="anonymous"></script>
<script src="[https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js](https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js)" crossorigin="anonymous"></script>
<script src="[https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js](https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js)" crossorigin="anonymous"></script>
</head><body>
<div class="container">
<video class="input_video"></video>
<canvas class="output_canvas" width="1280px" height="720px"></canvas>
</div>
</body><script type="module">
const videoElement = document.getElementsByClassName('input_video')[0];
const canvasElement = document.getElementsByClassName('output_canvas')[0];
const canvasCtx = canvasElement.getContext('2d');function onResults(results) {
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
canvasCtx.drawImage(
results.image, 0, 0, canvasElement.width, canvasElement.height);
if (results.multiHandLandmarks) {
for (const landmarks of results.multiHandLandmarks) {
drawConnectors(canvasCtx, landmarks, HAND_CONNECTIONS,
{color: '#00FF00', lineWidth: 5});
drawLandmarks(canvasCtx, landmarks, {color: '#FF0000', lineWidth: 2});
}
}
canvasCtx.restore();
}const hands = new Hands({locateFile: (file) => {
return `[https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`](https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`);
}});
hands.setOptions({
maxNumHands: 2,
minDetectionConfidence: 0.5,
minTrackingConfidence: 0.5
});
hands.onResults(onResults);const camera = new Camera(videoElement, {
onFrame: async () => {
await hands.send({image: videoElement});
},
width: 1280,
height: 720
});
camera.start();
</script>
</html>
您还可以在 MediaPipe 的网站上看到该代码的核心以及一些基本信息:
https://google.github.io/mediapipe/solutions/hands#javascript-solution-api
在 MediaPipe 的 CodePen 示例中测试更多高级功能
MediaPipe 刚刚在 CodePen 上发布了一个示例,展示了各种功能,例如:
- 更换网络摄像头和交换视频(在手机、平板电脑和笔记本电脑等各种设备中处理应用程序的关键),
- 跟踪 2、3 或 4 手牌(我试过 4 手牌,仍然很好用!)
- 调整检测参数(尽管默认参数在我尝试的所有条件下都工作得很好)。
- 此外,CodePen 示例提供了运行时的刷新率,单位为 fps。
我把这个例子复制到我网站的一个干净的网页上。运行时看起来是这样的:

MediaPipe 的笔,在https://lucianabriata . alter vista . org/tests/media pipe/hand-tracking-with-web cam . html清理过,在我的智能手机上运行。作者 Luciano Abriata 截图。
原笔在 https://codepen.io/mediapipe/pen/RwGWYJw 的但是你可以在这里更容易地尝试:https://lucianabriata . alter vista . org/tests/media pipe/hand-tracking-with-web cam . html
手跟踪工具的性能
当我在笔记本电脑上追踪两只手时,我得到了大约 25 fps,在智能手机上大约 20 fps。都不是很新(笔记本电脑是 2017 款东芝,带 i7 和 8GB RAM,没有专门的 GPU 手机是 2019 年的谷歌 Pixel 3),两者都有很多程序和浏览器标签打开。因此,尽管有改进的地方,我还是要说这个库工作得很好。
使用默认参数,该示例在各种光照条件下运行良好。实际上,我找不到它不能正确检测我的手的情况!
MediaPipe 的所有跟踪工具
截至 2021 年 9 月 7 日,MediaPipe 不仅提供手和手指跟踪,还提供人脸检测和人脸网格计算、虹膜检测、全身姿势检测、头发分割、一般对象检测和跟踪、特征匹配和自动视频裁剪。并非所有这些工具在 JavaScript 中都可用(至少到目前为止),但它们很可能会变得可用。要了解更多关于 JavaScript 可用的 MediaPipe 工具,请查看:
要了解所有编程语言中所有这些工具的更多信息,请访问它们的主要网站:
https://github.com/google/mediapipe
想象潜在的应用范围
在其网站上,MediaPipe 预计将应用于艺术(参见一些惊人的例子:https://developers . Google blog . com/2021/07/bring-artworks-to-life-with-ar . html)、通信,特别是基于标志的通信(这里有一个项目正在进行:https://developers . Google blog . com/2021/04/signal 1-SDK-sign-language-interface-using-media pipe-now-available . html)、假体控制和https://developers . Google blog . com/2021/05/control-your-mirru-prosthesis-with-media pipe-hand-tracking . html。
事实上,谷歌的 meet 已经使用了 MediaPipe 的一些工具来控制背景(https://ai . Google blog . com/2020/10/background-features-in-Google-meet . html)。他们还使用这项技术来跟踪全身姿势(https://ai . Google blog . com/2020/08/on-device-real-time-body-pose-tracking . html),这项技术可以被健身房应用程序重新利用,谁知道是否可以对体操表演进行更客观的评估。
我还可以在人机界面中推进增强现实的应用,人类用户可以用手抓住物体,在 3D 中探索它们,就像它们实际在手中一样。类似于 Oculus Quest 或 MS 的 HoloLens 等高端设备提供的功能,但可以在任何设备上运行。
作为一名科学家和业余音乐学习者,我不能忽视手跟踪工具在研究鼓手、吉他手等肢体运动和协调方面的潜力。尽管这项技术可能还不能满足正常的执行速度,但我认为它已经足够满足一些初步调查。我会玩这个,如果我发现一些有趣的东西,我会发一个新帖子让你知道。
喜欢这篇文章,想给我提示?【https://www.paypal.me/LAbriata】-谢谢!
我是一个自然、科学、技术、编程和 DIY 爱好者。生物技术专家和化学家,在潮湿的实验室和计算机里。我写我广泛兴趣范围内的一切。查看我的 列表 了解更多故事。 成为媒介会员 访问我和其他作家的所有故事, 订阅通过邮件获取我的新故事 (平台原创附属链接)。
扩展倒数秩——ExtRR:介绍
原文:https://towardsdatascience.com/extended-reciprocal-rank-ranking-evaluation-metric-5929573c778a?source=collection_archive---------27-----------------------
一种强有力的排名评价指标的研究

Unsplash 上的 Marten Newhall
动机
最近,我正在评估搜索结果的质量。倒数排名(RR)对我们的用例来说似乎很有希望,因为在结果集中的 100 或 1000 个文档中,我们只有几个“已知的”相关文档。
我在倒数排名和其他排名方法上遇到的问题——在我们的用例中,
- RR 只评估最上面的文档。
- 在其他措施中,您需要标记/分级结果集中的所有文档(未标记的零级),即使您不确定某些内容是否相关。
- 我需要像常规测试一样工作的东西,有预期的结果,实际的结果,并对它们执行断言。
所以,我试图将这种倒序推广到 N 个已知的相关文档中。因此得名扩展互惠等级(ExtRR)。
注意,这些是 N 个已知的相关文档,不一定是前 N 个文档
平均倒数排名是搜索/推荐排名评估度量。

这里,
- ||||表示查询的总次数
- rank(i) 表示第Ith查询的结果集中顶部文档的位置。
因此,为了计算平均倒数排名(MRR),我们简单地将各个查询的倒数排名相加,然后除以查询的总数。
正如我们在上面的公式中看到的,如果在结果列表中发现顶部的文档位置靠后,我们会降低搜索质量。
扩展倒数秩(ExtRR)
我们可以将这个概念推广到已知的相关文档,而不是集中在单个最上面的文档。
考虑下图,我们有 9 个结果,其中 3 个是已知的相关文档,其他的可能相关也可能不相关。

上表中的max _ expected _ position表示单据的预期位置。
如果单据的实际位置大于预期值,评估员应扣分。
注意,我没有将图中三个文档的max _ expected _ position分别设为 1、2、3。这意味着这个文档不一定是前三个文档。相反,可以这样想——“我知道三个文档(我不知道它们的确切位置)肯定是相关的,它们应该在第十个位置之前出现”
如上图所示, DOC #5 必须出现在 1 和 4 之间的任何位置。
现在,直观地,我们可以说文件#5 偏离了 1 个位置,即 5 - 4 = 1。
在倒数排名中,如果顶部文档偏离 1 个位置,意味着它位于第二个位置,我们说质量得分为 1/2。
同样,在我们的例子中,文档#5 的倒数将是 1/(5–4+1)= 1/2
使用上述类比,我们可以设计出 ExtRR 的公式:

如果在最大预期位置或之前找到文档,则 ExtRR 的除数(y)将为 1。
使用上面的公式,让我们计算上一个例子的 ExtRR:
ExtRR =(1/1+1/(5–4+1)+1/(8–6+1))/3 = ~ 0.61
或者,
我们甚至可以说,3 次测试中有 1 次通过。
理想的情况是在最大位置之前找到所有文档。在这种情况下,ExtRR 将:
ExtRR = (1/1 + 1/1 + 1/1) /3 = 1
Github 代码链接: 扩展倒数排名
测试链接: ExtRR 测试
结论
正如我们到目前为止所看到的,ExtRR 主要关注已知的“n”个相关文档,并根据它们在搜索结果中的位置给出质量分数。
因此,当我们确定一些文档是相关的,但不确定其他文档是否相关时,ExtRR 是一个很好的选择。
ExtRR 最大的优点是——你可以通过使用一些相关的文档来评估搜索质量,而不需要对所有的文档进行评级。
参考
https://en.wikipedia.org/wiki/Mean_reciprocal_rank
元数据在现代数据环境中的扩展使用和优势
原文:https://towardsdatascience.com/extended-use-of-metadata-in-modern-data-landscape-and-benefits-t-4336837ac37e?source=collection_archive---------42-----------------------
企业正在对其数据平台和相关工具集进行现代化改造,以满足数据从业者的快速需求,包括数据科学家、数据分析师、商业智能和报告分析师,以及包括业务和技术人员在内的自助服务。
然而,随着大多数组织中工具栈的现代化,生成的各种元数据也在现代化。随着数据量每天都在增加,与数据相关的元数据也在增加,管理数据的需求也在增加。

一个广义的数据景观。礼貌:Tejasvi Addagada
当我们看到数据环境并听说目录时,我们首先想到的是,“它扫描从关系到 NoSQL 或图形的任何数据库,并给出有用的信息。”
- 名字
- 模型化数据类型
- 推断的数据类型
- 数据模式
- 具有最小和最大阈值的长度
- 最小值和最大值
- 数据的其他分析特征,如值的频率及其分布
在目录中管理元数据的基本好处是什么?
- 提高数据情报的可用性,带来更好的洞察背景
- 减少分析过程中寻找答案的周转时间
- 提高专题专家为影响分析提供信息的效率
- 消除环境中数据之间关系的模糊性
- 通过含义、确定的冗余和关系简化数据视图

元数据的类型,由 Tejasvi Addagada 提供
由于技术进步和公共政策的变化,元数据的使用在过去一年中有了多倍的发展。大多数企业将目录用于所列的几个用例。
- 数据发现 —与数据民主化原则相关
- 回答诸如“数据作为对象和实例作为元素在模式中的物理位置”等问题
- 在单个或多个应用程序记录系统、参考系统(如湖泊或仓库)中搜索数据
2.系统隐私剖析 —与数据保护和隐私管理惯例相关
- 识别数据主体私有的因素,即使建模名称可能不相关
- 帮助了解应用程序的风险分类,包括它们在 SOC 操作中的日志。
3.控制对数据的访问 —类似于数据安全的原理。
- 确定数据授权并在单个存储库中处理它们
- 管理用户组、用户、数据访问策略、可以授予/撤销数据访问权限的所有者
4.数据管理 —与管理数据和治理数据相关
- 管理和确定与数据创建和处理相关的流程
- 人员信息,如数据、业务、流程和人员管理数据的所有者
- 在整个组织中寻找数据所有权的共性,以管理上下文
5.意为 —与“以正确的方式解释数据”的原则相关。
- 数据对特定情况和个人的意义的定义
- 基于数据在流程中的应用,收集和发现单一和共同的上下文描述
6.用途 —对应于公司内外数据互操作的原则
- 使用方式包括报告、仪表板、人工智能模型
- 使用频率,使用特定数据的文物年份
7.对数据进行分类以实现更好的管理——与数据可用性原则相关
- 数据变化和应用的速度—主数据、参考数据、交易数据
- 隐私分类—隐私、敏感、特殊类别、行为数据
- 标签——国家标识符、地址、姓名、卡片相关数据、健康信息
- 转换分类-本地、派生或转换的数据
8.规范管理 —逻辑组、名称、规范建模属性名称、其他标准建模名称、BIAN 中的类关联、MISMO 等
9.规则操作 —与互操作性原则相关&覆盖范围
- 业务元数据不可或缺的一部分,在操作元数据流程中经常被忽略,为操作而编排
- 通过业务规则、策略实施规则、派生和转换规则、数据质量规则、规则执行统计数据更好地对规则进行分类
- 维护业务规则是执行影响分析、数据分析和需求分析的一个很好的使能因素
- 管理数据之间的关系可以更好地找到数据参与的规则。

目录中管理的规则类型
10.数据操作 —扩展了数据分发管理的原理。
- 帮助了解数据使用情况、派生/本机特征、年份、上次使用、管道、归档和销毁策略、分区、作业、时间表。
治理如何支持元数据?
元数据管理还要求分析师在变更的适当阶段将信息放入目录中。这可以通过在数据的整个生命周期中始终包含正确的利益相关方来实现。数据也有一个生命周期,POSMAD(计划、获取、存储/共享、维护、应用、衰退),这有助于展现血统。
即使是丰富的敏捷管理模型,如 scrum、kanban、DAD、FDD,也可以从在项目中管理和使用数据的机构知识中获益,以加速特性的交付。数据治理可以平衡元数据的托管和服务,保证元数据适用于大多数用例。
它来自哪里?
它适用于哪些流程?
谁使用商业术语?
哪个系统利用数据元素—存储、共享、转换和衰减
随着治理通过特定的操作节奏和过程将元数据的主动管理正式化,将它集成到项目生命周期的数据变更或使用计划中变得更加容易。数据治理功能提供了放置导轨或护栏的余地,这些导轨或护栏有助于评估、指导和监控元数据的管理,以帮助实现在组织中管理数据和元数据的目标。
此外,管理元数据需要一个标准框架,该框架可以引导人员方便地捕获与数据相关的信息。
管理目录时常见的一些问题
- 你是否已经将目录民主化,让组织中的任何人员都可以输入他们知道的关于数据的信息?是否有确定的数据管理员能够指导组织使用基线信息?
- 来自数据源的元数据是否促进了数据湖和数据仓库中模式漂移的管理?应该多长时间扫描一次源中的元数据或将其推入目录中?
- 您是否在关注在生动的上下文中使用的带有专业名称的业务术语,但您是否在捕捉同义词或通用名称,以便在全球范围内轻松使用?
- 元数据管理是否弥合了地区、全球运营和 IT 之间的差距?业务分析已经准备好支持这种交流,但是元数据管理附带了可以加速推进这一方面的使能器。
- 今天,您是否有一个论坛,让所有相关的利益相关者能够在发布数据之前对他们所知道的数据进行共同理解或丰富?
- 您计划如何利用元数据存储库中捕获的优秀信息来帮助业务自助服务?是不是目录太技术化,接受不了?
- 合规性是否带走了你在小方向上的最大努力,让你首先找到你为什么要这么做?
- 您的元模型是否考虑了各种用例——比如一个业务术语可以有多少数据所有者,多少可以是贡献者和查看者,我们可以定义多少真理系统?
业务元数据不会自己生成信息,它需要每个负责任的利益相关者、系统和流程一致地生成元数据,包括定义和管理方面。这一领域的供应商关注的焦点是最大限度地从数据库和系统中自动提取任何信息。但是,元数据如果得到积极管理,将使组织能够更好地治理数据。
目录可以是丰富数据人员在数据操作上的协作的一种方式。关于使用目录作为协作工具的进一步阅读—https://www . dattamza . org/dattamza-blog/data-catalog-as-a-single-means-to-collaborate-through-data-operations
在 LinkedIn 和 Twitter 或我的网站上关注我
使用 lambda 支持的定制资源扩展云形成
原文:https://towardsdatascience.com/extending-cloudformation-using-lambda-backed-custom-resources-c99b98cc7304?source=collection_archive---------12-----------------------
CloudFormation 是在 AWS 生态系统中管理服务的一个很好的工具,但是使用定制资源,可能性是无限的

:照片由 Unsplash 上的 Kaushik Panchal 拍摄
本文分为两部分:
- 云形成的背景和自定义资源的介绍。
- 监控代码:使用 CloudFormation 管理 Datadog 监控器。
如果你想跳过,这篇文章中讨论的所有代码都可以在我的 GitHub 这里 找到。
云的形成
CloudFormation 是 AWS 提供的一个奇妙的“基础设施即代码”产品,它允许您有效地对资源集合建模,并在它们的整个生命周期中管理它们。AWS 的“意大利面条碗”继续以极快的速度增长,这意味着即使是云的形成也无法跟上它们,但不要担心,因为我们能够使用自定义资源来填补任何缺口和更多缺口。
对于云来说,基础设施即代码是一种新常态,它让您能够深入了解您的环境是如何形成的,并且有信心在不导致任何严重错误的情况下更新您的堆栈。例子包括云的形成和地形。
CloudFormation 提供了一种通用语言来描述和配置您的云环境中的所有基础设施,它以可重复的方式配置资源,并且由于一切都是以代码形式编写的,因此可以进行版本控制。CloudFormation 通过调用模板中描述的 AWS 服务来调配和配置资源。创建完所有资源后,它会报告您的堆栈已经创建好,可以使用了。如果堆栈创建失败,CloudFormation 将回滚您的更改。
有时你会想做一些 CloudFormation 不支持的事情,这可能是部署后的手动步骤,甚至是在 AWS 生态系统之外提供一些基础设施。幸运的是,自定义资源使这变得非常容易。
自定义资源
自定义资源使您能够在模板中编写自定义供应逻辑,AWS CloudFormation 会在您创建、更新或删除堆栈时运行这些模板。如果已经在模板中定义了定制资源,CloudFormation 将在堆栈操作期间向资源提供者端点发送外部请求,并等待响应。来自响应的数据随后可用于供应和配置其他资源。可以在 Cloudformation 模板中定义自定义资源,如下所示:
CustomResource:
Type: 'Custom::MyCustomResourceTypeName'
Properties:
ServiceToken: RequestAddress
var1: variable1
var2: variable2
定制资源只需要一个属性:ServiceToken,它将告诉 Cloudformation 将请求发送到哪里,但是也可以包含其他属性。对于 lambda 支持的定制资源,ServiceToken将是 lambda Arn,Cloudformation 将以事件的形式发送请求。请求示例如下所示:
{
"RequestType": "Create",
"ServiceToken": "lambda-arn",
"ResponseURL": "[http://pre-signed-S3-url-for-response](http://pre-signed-S3-url-for-response)",
"StackId": "cloudformation-stack-id",
"RequestId": "request-id",
"LogicalResourceId": "CustomResource",
"ResourceType": "Custom::MyCustomResourceTypeName",
"ResourceProperties": {
"ServiceToken": "lambda-arn",
"var1": "variable1",
"var2": "variable2"
}
}
一旦请求被发送,Cloudformation 将在预先签名的 URL: ResponseURL中等待响应。根据操作的类型,可以创建、更新或删除RequestType。定制资源提供者将处理请求,执行您要求的任何任务,并向预先签名的 URL 返回一个响应SUCCESS或FAILED。如果返回无响应或Failed状态,云形成操作将失败并回滚。
{
"Status": "SUCCESS",
"PhysicalResourceId": "function-return-value",
"StackId": "cloudformation-stack-id",
"RequestId": "request-id",
"LogicalResourceId": "CustomResource",
"Data": {
"out1": "output1",
"out2": "output2"
}
}
包含在Data字段中的任何值都存储在预先签名的 url 位置,并且可以使用!GetAtt函数在模板中引用。要从上面的输出中检索out1值,可以使用下面的命令:!GetAtt CustomResource.out1。
Lambda 支持的自定义资源
当 lambda 用作定制资源提供者时,每当创建、更新或删除定制资源时,都会调用该函数。CloudFormation 调用带有请求数据的函数(如上)并等待响应。幸运的是,有许多库使得编写定制资源变得非常容易。我将使用的是custom-resource-helper:AWS 提供的一个基于 Python 的库,使用 decorators。
自定义资源助手是一个非常棒的包,它大大降低了部署 lambda 支持的自定义资源的复杂性。下面的代码可以作为 lambda 开发的起点。它可以分为以下几点:
- 首先导入
CfnResource类。一个CfnResource对象被实例化并被调用helper。 - create、update 和 delete decorators 确定将为 difference CloudFormation 堆栈操作调用哪个函数。
- CloudFormation 模板中定义的资源属性在 lambda 事件中的
ResourceProperties对象中可用。 - 保存在
helper对象的Data对象中的任何内容都可以被 CloudFormation 模板中的其他资源使用!Get Att内在函数引用。 - 每次成功执行函数后,
helper对象负责将响应上传到 lambda 事件中定义的ResponseUrl。
作为代码进行监视:使用 CloudFormation 管理 Datadog 监视器
为了真正展示 CloudFormation 定制资源的一些好处,我在 Datadog 中提供了一个演示,演示了如何使用它们来配置监视器,以便在任何资源出现故障时发送警报。以下示例仅监视失败的 lambda 调用。
本演练的先决条件如下:
- 本文中的所有资源都是使用 AWS SAM CLI 部署的。要继续操作,必须安装 CLI,并且必须在项目的根目录中定义配置文件:samconfig.toml。
version=0.1
[default.deploy.parameters]
profile = "personal"
stack_name = "datadog-monitor-dev"
s3_bucket = "custom-resources-dev-20200318"
s3_prefix = "datadog"
region = "eu-west-1"
capabilities = "CAPABILITY_IAM"
confirm_changeset = true
tags = "project=\"datadog-monitor-dev\" stage=\"dev\""
- 在您的 Datadog 帐户中设置 AWS 服务集成,并确保选择 lambda tile。这使得 Datadog 能够收集 Amazon lambda 指标。一旦完成,所有 lambda 函数将在 Datadog 无服务器视图中可用。
- 检索 DataDog API 密钥并创建应用程序密钥。这些必须存储在 AWS secrets manager 中,如下所示,您可以为作为参数添加到 CF 模板的机密选择任何名称:
{
"DD_CLIENT_API_KEY": "API KEY",
"DD_CLIENT_APP_KEY": "APP KEY"
}
Datadog API
Datadog 有一个 HTTP REST API,允许您以编程方式与平台进行交互。下面的代码中使用了 API 来创建 Monitor 类,该类将由自定义资源 lambda 使用。以下代码被总结为以下要点:
- API 的基本 url 是:
https://api.datadoghq.eu/api/v1 - 要验证对 API 的调用,必须在请求头中提供 datadog API 密钥和应用程序密钥。这些键用于初始化类对象。
- 该类有三个方法来创建、更新和删除 Datadog 监视器,还有一个帮助器方法来构造简单的监视器查询。
create_monitor方法接受以下参数:名称、消息、优先级、函数名和标签。该名称就是监视器在 Datadog UI 中显示的名称。消息是每个警报发送的内容。优先级表示警报的严重性。functionname 被_create_query方法用来构造一个简单的监视器查询,而标签被用来标记监视器。创建监视器后,返回其monitor_id。_create_query方法使用 functionname 参数创建一个监视器查询,该查询检查最近一个小时内的任何 lambda 错误。- 除了增加了
monitor_id之外,update_monitor方法采用与create_monitor方法相同的所有参数,因此它知道要更新哪个监视器。 delete_monitor方法使用monitor_id删除监视器。
Datadog Python 模块
Datadog 自定义资源 lambda
使用 Datadog Monitor 类以及上面定义的自定义资源框架代码,现在可以创建一个自定义资源,该资源可以创建、更新和管理 Datadog Monitor 以及 CloudFormation 堆栈操作。
- 如先决条件中所述,Datadog API 和 App 密钥存储在 AWS secrets manager 中。这些是使用
aws_lambda_powertools包检索的。 - 创建了
CfnResource和DD_monitor对象。 - 在第一次创建堆栈时执行的
create函数中调用create_monitor方法;如@helper.create装饰器所示。所需的参数在 CloudFormation 模板中定义,并在事件对象的ResourceProperties字段中提供。 - 分别在
update和delete函数中调用update_monitor和delete_monitor方法。 create和delete函数都返回monitor_id,它将被指定为资源的物理 id。
Datadog 自定义资源 lambda
测试λ
测试λ是将在 Datadog 中监控的λ。lambda 将仅用于生成一些成功和失败的调用度量。
测试λ
云形成模板
现在已经定义了所有的 lambdas,下一步是创建 CloudFormation (SAM)模板来部署它们。该模板明确定义了三个资源,即DataDogMonitorLambda、一个TestLambda和TestLambdaDDMonitor。
DataDogMonitorLambda是自定义资源将调用的 lambda 函数。这一点很明显,因为定制资源中的ServiceToken指向了DataDogMonitorLambdaArn。TestLambda是将被监控的λ。TestLambdaDDMonitor是通过调用DataDogMonitorLambda创建的数据狗监视器。创建监视器所需的参数作为附加属性传递。
部署堆栈
要使用 SAM 部署堆栈,请执行以下命令:
sam build --use-containersam deploy
检查变更集中的所有内容,并按下y继续部署。

云形成变更集
一旦栈创建完成,所有资源都将拥有逻辑和物理 id。因为自定义资源 lambda 中的create函数返回它刚刚创建的 Datadog 监视器的monitor_id,所以它被设置为资源物理 ID。可通过以下地址在 Datadog 中查看监视器:https://datadoghq.eu/monitors/{MonitorID}。

云形成资源
测试显示器
最初,Datadog 监视器没有数据,如下所示,这是因为监视器要求 lambda 执行成功或失败。

生成一些数据的最简单方法是在 AWS UI 中手动调用 TestLambda。因为监视器每小时只检查失败的调用,所以最好成功调用几次 lambda,等待数据填充到 Datadog 中,然后导致 lambda 失败。

最后,如果监控器在 lambda 执行失败后按预期工作,它应该保持在警报状态。

我希望你喜欢读这篇文章,就像我喜欢写它一样。Lambda 函数和定制资源的简单性真正打开了云形成的可能性。我正计划写另一篇关于你如何使用自定义资源来管理你的雪花账户的文章,所以请关注这个空间!
查看我的其他工程帖子:
- 连接到 AWS 上私有子网中的 ec2 实例
用惊人的结果扩展 Julia 的算子
原文:https://towardsdatascience.com/extending-julias-operators-with-amazing-results-96c042369349?source=collection_archive---------36-----------------------
如何在 Julia 中加载和扩展方法,用一个非常激动人心的例子

https://unsplash.com/photos/gdL-UZfnD3I
介绍
有了 Julia,模块扩展的可能性实际上是无限的。这种无限的潜力是由于朱莉娅神奇的多重调度带来的。多重分派允许我们扩展旧的方法,以便处理我们可能想要传递的新类型。如果你想进一步了解多重分派,以及为什么我认为这是一个编程计算机的好方法,我写了一篇文章详细介绍了它,并提供了一些非常好的例子,你可以在这里查看:
**
多重分派对 Julia 模块来说也意味着大多数模块都是直接重要的,并且变得更加有用。使用多重分派,我们可以将任何方法应用于语言内部的任何类型。这包括来自完全不同模块的类型。我有一个非常酷的想法,将它应用到车床软件包中。对于那些不熟悉车床的人来说,车床是 Julia 的一个包容性预测学习模块。这个包包含了面向对象的语法,这使得它的用法非常类似于 Pythonic 的反义词 SkLearn。如果说 Flux 是 Julia 的张量流,那么车床就是 Julia 的 SkLearn。
也就是说,Python 的 SkLearn 模块和 Julia 的车床模块都有一个突出的特性,那就是管道类型。管道类型允许我们以一定的顺序组合对象,以快速和自动地处理数据。车床的代码实际上已经很旧了,急需更新。我们将通过使用这些基本操作符重新创建我们的管道类型来开始分派这种类型的过程。**
重构管道
我想首先用一个戏剧性的变化来重构这些管道,这个变化也是不间断的。以下是最新版本的车床中已经存在的代码:
[@doc](http://twitter.com/doc) """
Pipelines can contain a predictable Lathe model with preprocessing that
occurs automatically. This is done by putting X array processing methods
into the iterable steps, and then putting your Lathe model in.\n
--------------------\n
==PARAMETERS==\n
[steps] <- An iterable list of methods to call for X modification. These mutations should
have ALREADY BEEN MADE TO THE TRAIN X.\n
pipl = Pipeline([StandardScalar(),LinearRegression(trainX,trainy)])\n
--------------------\n
==Functions==\n
predict(xt) <- Returns a prediction from the model based on the xtrain value passed (xt)
"""
function Pipeline(steps)
predict(xt) = [xt = step[xt] for step in steps]
(var)->(steps;predict)
end
这个函数工作得很好,并将采取一系列可迭代的步骤。然而,这样做的问题是我们不知道将返回什么类型。该类型将是一个 Julia 不安全类型,它将构造一个新的类型,该类型实际上是由我们在语言中放入的内容定义的。我们可以通过将管道转换为常规类型来改变这一点。我还将在完整的车床软件包中添加一个新的抽象类型。这将允许我们限制可以作为参数传递的内容,同时不删除从预处理模块放置对象的能力。如果我们将这个函数限制为 just ::Model,我们将无法将缩放器或编码器放入这个函数中——这在某种程度上违背了创建它的初衷。让我们继续创建这个抽象类型:
abstract type LatheObject end
我将把它添加到 Lathe.jl 文件中并导出它,这将定义类型层次结构的顶层。现在我将进入 Lathe.preprocess 并为所有预处理器对象添加子类型。我将为车床做同样的事情。型号:
abstract type Model <: LatheObject endabstract type Preprocessor <: LatheObject end
现在,所有被定义为这些超类型下的子类型的类型都可以通过分派方法传递。现在,我们可以将这个分派添加到我们的管道函数中。然而,为了确保在这些模块中定义这个抽象类型,我们可能还希望在车床模块中导出我们的车床对象,并对每个模块使用 using,以便将该类型加载到每个子模块中。
export LatheObject
现在我们将 using 添加到我的 preprocess.jl 文件和 models.jl 文件中。
using Lathe: LatheObject
尽管整个包可能会被预编译,但是为了提高速度,直接导入这个类型可能会更聪明,即使它是导出的。最后,我将包含来自 REPL 的代码,以确保该类型被正确导入,并且在包中有子类型。
include("Lathe.jl")
为了确保这样做有效,我将导入并创建一个 StandardScaler 类型。为此,我进入了 REPL:
using Lathe.preprocess: StandardScaler, LatheObject
子类型运算符<:/>
StandardScaler <: LatheObject

作者图片
酷!
如果你想了解更多关于子类型的知识,我写了一整篇文章来介绍它们以及它们的用法,你可以在这里找到更多的细节:
**
让我们在模型类型上尝试同样的事情。
using Lathe.models: LinearRegression
LinearRegression <: LatheObject
现在让我们将这种类型转换添加到我们的参数中,以确保只传递带有 predict()函数的车床对象:
function Pipeline(steps::LatheObject ...)
predict(xt) = [xt = step[xt] for step in steps]
(var)->(steps;predict)
end
我还打算加上…这将使得不再需要提供 iterable,我们现在可以提供无限数量的车床对象作为参数。否则,我们需要转换类型数组{LatheObject}。现在,我将为管道类型编写一个外部构造函数。我们可以使用{}来添加一个未知类型到我们的构造函数中。旧函数将成为内部构造函数。我们还需要将这些类型放入 iterable 中,因为这是 predict()方法所需要的。
mutable struct Pipeline{P}
steps::Array{LatheObject}
predict::P
function Pipeline(steps::LatheObject ...)
steps = [step for step in steps]
predict(xt) = [xt = step[xt] for step in steps]
(var)->(steps;predict)
end
end
我们将通过将(var) ->()部分更改为 new()方法的用法来完成内部构造函数,将 predict()的类型设置为 p。工具类型已经是模型的子类型,为这个类型创建了三个具有不同规范的类型层。这是我们的最终构造函数的样子:
mutable struct Pipeline{P} <: Tool
steps::Array{LatheObject}
predict::P
function Pipeline(steps::LatheObject ...)
steps = [step for step in steps]
predict(xt) = [xt = step[xt] for step in steps]
new{typeof(predict)}(steps, predict)
end
end
这个结构的可变也是至关重要的。这是因为 Julia 的基本操作符将用于改变我们传递的管道对象。
扩展运算符
从这一点开始,我们将在一个笔记本上工作,代码将在以后添加到车床文件中。我这样做是为了让我的读者可以轻松地访问和重新创建即将到来的代码。以下是为感兴趣的人准备的笔记本:
笔记本
为了从 Julia 的基础上扩展我们的操作符,我们需要直接导入它们。我将从加法运算符+开始。
using Lathe.models: Pipeline, LatheObject, LinearRegressionusing Lathe.preprocess: StandardScaler
import Base: +
现在,我们可以用一个管道分派这个+方法,以便能够通过简单地使用这个操作符,用 predict()方法将任何车床对象添加到管道中。
+(p::Pipeline, step::LatheObject) = push!(p.steps, step)
现在,我们可以构造两种类型,并将其放入管道中。
x, y = [5, 10, 15, 20, 25, 30], [2, 4, 6, 8, 10, 12]scaler = StandardScaler(x)model = LinearRegression(x, y)
现在,我们将制作一个管道,但我将避免将模型添加到我们的步骤中:
pipe = Pipeline(scaler)
假设我们想在这个例子中添加模型的步骤。现在,我们可以使用我们分派的+操作符将车床对象推送到我们的管道步骤。我很好奇的一件事是,这是否会创建一个新的管道副本并返回它,或者实际上改变我们原来的管道。为了测试这一点,我将把它作为一个名为 pipe2 的新变量。如果是这种情况,我们将会遇到很大的问题,因为我们将得到管道步骤的返回,而不是管道返回的新步骤:
pipe_with_model = pipe + model
当然,pipe_with_model 现在只是一组步骤。然而,我们真正想要检查的是模型是否在管道结构的步骤数组中。用下面的 for 循环打印这些步骤表明它正如预期的那样工作了!:
for step in pipe.steps
println("=STEP=", step)
end

作者图片
记住 than,我们可以在不使用断言操作符的情况下调用它,只需使用 pipeline + object。使用这种方法,我们还能够将管道添加到管道中,使得使用这种方法和数据数组的链的可能性几乎是无限的。让我们试着从这个模型中得到一个预测,即使我们从未用我们的定标器定标我们的 X,为了让这个模型实际上与定标器的数据一起工作,我们应该这样做。

作者图片
这产生了一个错误,因为我甚至没有想到要改变内部构造函数中 predict()方法的功能。这是我们管道真正的最终构造函数。
mutable struct Pipeline{P} <: Tool
steps::Array{LatheObject}
predict::P
function Pipeline(steps::LatheObject ...)
steps = [step for step in steps]
predict(xt) = [xt = step.predict(xt) for step in steps]
new{typeof(predict)}(steps, predict)
end
end
既然我们已经重新创建了 predict()方法,我们可以再次尝试运行它。
y_hat = pipe.predict(x)

作者图片
我想做的最后一件事是扩展-()方法,以便允许删除步骤。这将以同样的方式完成,但是使用一个整数作为第二个参数。然后我们将使用 deleteat 删除数组中二聚体!()方法。
-(p::Pipeline, n::Int64) = deleteat!(p.steps, n)pipe - 2

作者图片**
结论
如果你问我,这肯定是非常棒的。关于 Julia 语言的一件很酷的事情是,它实际上主要是自己编写的。这使得它既易于贡献又易于扩展。这就是为什么在 Julia 语言中,非常有限数量的扩展基函数在随机的包中执行不同的功能是很常见的。我认为这是利用这一点的一种非常有趣和酷的方式,并且很难反对使用加法和减法运算符来从管道中删除步骤等等。在我看来,这些新管道既酷又令人兴奋!非常感谢您的阅读!
扩展 KNIME Python 与 Plotly Express 和 Kaleido 的集成
原文:https://towardsdatascience.com/extending-knime-python-integration-with-plotly-express-and-kaleido-5c306074a5ca?source=collection_archive---------28-----------------------
以最少的编码生成强大的交互式图形和高质量静态图像的替代方法

作者图片:截图 KNIME 分析平台工作流程示例-桌面视图
KNIME Analytics 平台的一个关键优势是它的灵活性,允许您安装各种扩展来增强其常规的“开箱即用”功能。当然,是否安装特定的 KNIME 扩展取决于您的项目及其环境的需要。有时候可能只是被你自己的个人喜好所驱使。其中一个领域与数据可视化有关。在本文中,我描述了如何配置 KNIME,以使用 Plotly Express 和 Kaleido 作为我首选的 python 视图图像生成引擎,从而为我的 KNIME 工作流提供了大量额外的强大图形库。
目录
装置
- 基础环境
- KNIME 与 Python 的集成
- 安装 Plotly 和 Plotly Express
- 安装 Kaleido
- 最终 Conda 环境配置
KNIME 工作流程示例
- 概观
- 读取源数据
- 转换数据
- 生成情节表达和万花筒图像
- 示例图像
- 与 KNIME 堆积面积图视图的比较
- KNIME Conda 环境传播
外卖食品
参考
装置
基础环境
简而言之,我的基本安装环境如下:
- 本地机器:运行 Chrome OS 的 Google Pixelbook。
- 托管在谷歌云平台(计算引擎)上的 Linux VM。操作系统:Ubuntu。版本:16.04 LTS。
- 谷歌 Chrome 远程桌面配置为访问 Linux 虚拟机上的 Xfce 桌面。安装说明此处【1】。
- 用于 Linux 的 KNIME 分析平台(版本 4.3.1)。安装说明此处【2】。
此外,我还使用了 Google Cloud Storage Bucket 来存储源数据,以供 KNIME 工作流使用。
KNIME 与 Python 的集成
接下来,我安装了用于 KNIME 分析平台的 KNIME Python 集成。对于 KNIME 版本 4.3,安装说明在这里是【3】。本质上有三个步骤:
- 安装 KNIME Python 集成扩展
- 安装 Anaconda Python (个人版)。
- 为 KNIME Python 集成创建一个 Conda 环境。
请注意,建议从 KNIME Analytics 平台中自动创建 Conda 环境(而不是使用 YAML 配置文件手动创建)。
我把默认版本设为 Python 3,创建了一个新环境,名为“py36_knime_mjh_update”。正在使用的 Python 版本是 3.6.12。

图片作者:截图— KNIME 首选项— Python
安装 Plotly 和 Plotly Express
设置好核心的 KNIME Python 集成环境后,接下来我安装了 Plotly 和 Plotly Express 模块。为此,我使用 Anaconda Navigator 将 conda 安装到上面创建的 KNIME conda 环境中。

作者图片:截图—使用 Anaconda Navigator 的 Plotly 和 Plotly Express 安装
这将安装以下软件包和依赖项:

作者提供的图片:截图—选择的 Plotly 和 Plotly Express 包
安装 Kaleido
最后一步是安装万花筒。这需要几次尝试才能让它工作。
对于第一次尝试,我尝试使用 Anaconda Navigator 如上所述对 Kaleido 进行 conda 安装。然而,当我试图执行 KNIME 工作流来生成静态图像时(见后面),抛出了一个异常。由于某种原因,它无法找到 Kaleido 可执行文件夹在:
"../anaconda 3/envs/py36 _ knime _ mjh _ update/lib/python 3.6/site-packages/kaleido/"
在进一步尝试找出问题所在后,我决定用锤子敲打这个东西,并从终端窗口安装 Kaleido。
首先,我激活了 KNIME conda 环境:
$ conda activate py36_knime_mjh_update
然后我安装了 Kaleido:
$ pip install kaleido
现在快速检查显示,Kaleido 可执行文件夹现在在预期的位置。这解决了问题。

作者图片:截图 Kaleido 可执行文件夹位置
最终 Conda 环境配置
在这个GitHub gist【4】中,KNIME conda 环境的最终配置以 YAML 文件的形式提供。
KNIME 工作流程示例
概观
我的示例工作流如下所示。

作者图片:截图— KNIME 工作流示例
本质上,它是这样做的:
- 连接到谷歌云存储桶
- 从 Excel 文件中读取源数据
- 对数据应用一些转换,为查看数据做准备(并将结果表转储到文件中)。
- 以多种格式生成堆积面积图(用于演示目的)
我现在将更详细地描述各种元素。
读取源数据

作者图片:截图 KNIME 工作流-读取源数据
示例数据涉及 2020 年和 2021 年前几周苏格兰涉及 COVID19 的死亡。它可以从苏格兰国家记录中公开获得。数据以 Excel 电子表格的“长格式”提供。

作者图片:截图-源数据(长格式)
转换数据

作者图片:截图 KNIME 工作流-转换数据
在这里,源数据被简单地过滤和透视,以便生成“宽格式”的数据表,作为堆积面积图的输入。我还将输出表转储到一个 KNIME 表文件和一个 Excel 文件中(以便以后需要时参考)。

作者提供的图片:截图-转换后的数据(宽格式)
生成情节表达和万花筒图像
现在我们来为我们的数据生成堆积面积图。这是使用 KNIME Python 视图节点实现的。

图片作者:截图— KNIME 工作流— Python 视图
要配置此节点,请右键单击→配置:

作者图片:截图 Python 视图配置
在左侧面板中,我们可以看到输入变量。这是我们的“输入表”,在 Python 视图节点中,它被视为数据帧(相对于熊猫)。因此,没有必要显式加载 pandas 模块。
中央面板包含用于本例的脚本。全文如下:
下面是它首先做的事情:
- 导入所需的模块 plotly.express 和 plotly . offline . plot
- 将短名称应用于 input_table 数据帧
- 根据 input_table 列名创建列表。
- 从列表中抓取第一个列名,指定为 X 轴
- 抓取剩余的列名,指定为 Y 轴的类别。
- 定义情节。在 plotly express 中,使用 px.area 函数生成堆积面积图。
然后,出于演示目的,它生成了各种格式的堆积面积图:
- 在默认浏览器中自动启动交互式绘图
- 将交互式绘图保存为 HTML 文件(以及所需的 JavaScript)
- 使用 Kaleido 将堆叠面积图保存为 png、jpeg、svg 和 pdf 格式的静态图像。
最后,它使用 plotly.io 模块将图形转换为静态图像字节字符串。它将这个赋值给 Python 视图节点的 KNIME“output _ image”变量。
示例图像
由 Plotly Express 生成的交互式 HTML 绘图在默认浏览器中自动启动,此处显示启用了“悬停时比较数据”切换:

作者图片:截图— Plotly Express —堆积面积图—交互式 HTML 绘图
这是使用 Kaleido 引擎生成和保存的静态图像(png 格式):

作者图片:由 Kaleido 生成的静态 PNG 图像
右键单击 KNIME Python 视图节点,选择 Image:

作者图片:截图 KNIME Python 视图节点-右键菜单

作者图片:截图 KNIME Python 视图节点-输出图片
与 KNIME 堆积面积图视图的比较
应该注意的是,KNIME Analytics Platform 的“开箱即用”基础版本已经为生成不同类型的图提供了各种“现成的”JavaScript 视图节点。其中包括堆积面积图节点。

作者图片:截图
为了快速比较,我简单地在类似的工作流中连接了这个节点。

按作者分类的图像:屏幕截图—带有堆叠面积图(JavaScript)节点的 KNIME 工作流
这是生成的静态图像:

作者图片:截图 KNIME 堆积面积图节点-静态图片
还可以查看交互式 JavaScript 图表:

作者图片:截图— KNIME 堆积面积图节点—交互式图表
使用固定的 KNIME 视图的优点是在这种情况下根本不需要任何代码。只需将其插入工作流程,进行配置,然后按下按钮。这取决于你自己的需要。
这很大程度上取决于个人喜好。我自己的选择是为了 Plotly Express(及其“母公司”Plotly)提供的更大的灵活性和剧情范围。在这个例子中,python 代码保持在最低限度(只需要几行优雅的代码)。我当然可以忍受!
KNIME Conda 环境传播
最后,您会注意到,我还将 KNIME Conda 环境传播节点包含到了这个示例工作流中(尽管对于我们的目的来说,它在这里并不是真正必要的)。

图片作者:截图— KNIME 工作流— Conda 环境传播
这是一个允许您在工作流程中保留特定 KNIME conda 环境的功能。这可确保指定所需的 python 模块和版本,使工作流更具可移植性。例如,您希望与其他同事共享工作流程或将工作流程上传至 KNIME 服务器。更多信息请阅读本【6】。
外卖食品
- KNIME Analytics Platform 提供了基于特定需求和个人偏好扩展其“开箱即用”功能的灵活性。
- KNIME Python 集成发挥了 Python 的威力,并允许您将其集成到您的工作流中。
- Plotly Express 和 Kaleido python 模块带来了丰富的图形库,并提供了出版质量的交互式图形和静态图像。
- 特别是 Plotly Express 允许您将编码减到最少。仅仅几行优雅的代码就能达到目的!
- 当设置 KNIME conda 环境时,似乎需要执行 Kaleido 的 pip 安装(相对于 conda 安装)以使其在 KNIME 中运行。
最后,我很想听到任何人关于万花筒装置的最后一点的意见。
对于那些希望获得 KNIME 工作流副本(包括使用的源数据)的人来说,在 KNIME Hub 上有一个修改版本。只需搜索“使用 Plotly Express 和 Kaleido 的 Python 视图”。
参考文献
1 在计算引擎上为 Linux 设置 Chrome 远程桌面
[2] KNIME 分析平台安装指南
3 KNIME Python 集成指南
[4] KNIME Python Conda 环境 GitHub Gist 上的 YAML 文件
[5] 苏格兰国家记录——涉及冠状病毒的死亡(新冠肺炎)
6 KNIME 康达环境传播
请多放些树
原文:https://towardsdatascience.com/extra-trees-please-cec916e24827?source=collection_archive---------26-----------------------

杰伊·曼特里在 Unsplash 上的照片
冒险进入决策树和随机森林的机器学习领域
网格搜索。管道。决策树。支持向量机。超参数调谐。混淆矩阵。ROC 曲线。如果你在三个月前向我提到这些术语中的任何一个,我绝对不知道你在说什么。我们说的是同一种语言吗?然而,现在我在这里,刚刚使用所有这些技术、方法和工具完成了一个项目。这就是加速新兵训练营学习节奏的奇妙之处;我的大脑已经接受了这样一个事实:它每天都会被新的概念淹没,并被期望几乎立即投入使用。
我们 Flatiron School 的数据科学项目的第三个主要项目是一个专门关注分类的监督机器学习项目。与我们之前的其他项目不同,我们被指示建立预测分类目标变量的模型,而不是连续或数字变量。与预测电影票房价值或房屋销售价格不同,我们可以选择一些不同的分类数据集,或者选择我们自己的数据集。在搜索了几天的存储库和数据收集后,我最终选定了一个与我的兴趣相符的,并提出了一个需要解决的现实世界的问题。
我们作为一个团队在 Zoom 上见面,讨论我们选择的主题。当我听我的同事讨论他们的话题时,我意识到我错过了一个难得的机会,为这个项目选择一些乐观和有吸引力的东西进行调查。我听到学生们谈论他们兴奋地做出的不同体育相关的预测,以及其他人使用算法来预测音乐类型或视频游戏评级的计划。这么多有趣的想法!当轮到我介绍我的话题时,我重重地叹了口气,尴尬地解释说,我选择了一个与胎儿健康结果和死亡率有关的数据集。没错。不知何故,活泼的无忧无虑的·明挑了一个最严肃、最令人沮丧的话题。
然而,我的项目本身和数据的目标实际上是充满希望和乐观的。这一切的前提是持续的公共健康问题,即美国的胎儿死亡率。这是一个我非常关心的话题,这促使我花费数小时寻找用于此目的的正确数据。我发现的是这个数据集包含 2000 多行患者的 CTG 记录,完整的特征包括胎儿心率、子宫收缩、胎动等等。然后由“产科专家”将每个记录分类为正常、可疑或病理胎儿健康结果。我通过将可疑结果和病理结果合并到一个标记为“处于危险中”的类别 I 中,将这调整为一个二元分类问题。我开始证明胎儿健康结果可以仅从 CTG 检查的指标中预测,这意味着这种预测可以自动化,医疗保健提供者可以简单地从这种容易获得的技术的读数中采取更积极的救生措施。
开始这个项目时,我首先意识到的一件事是,管道基本上是一种神奇的创造,可以简化将分类器与数据相匹配的过程。管道完成了缩放、预处理和设置分类器的所有步骤,并将它们合并到一个对象中,该对象干净地完成了整个过程,并消除了一些人为错误。由于我想探索许多不同的算法,所以我首先编写了一个函数来接受您选择的分类器,并返回一个准备好适合 X 和 y 的管道对象。(请注意,我的 class_weight 参数被设置为“balanced ”,以考虑到我不平衡的目标变量;在我的数据中,正常健康的婴儿比高危婴儿多得多。)
作者要点。
现在,可以说比管道更令人惊奇的是网格搜索的能力。GridSearchCV 允许您为一个模型检查许多不同的超参数,并通过为所选度量选择最佳参数来优化您的模型。在这种情况下,我优先考虑的指标是回忆。通过优化召回率,我将模型预测中的假阴性或第二类错误的数量降至最低。虽然我可以接受模型有时错误地预测婴儿处于危险之中,但我不太能接受模型有时错误地预测婴儿健康。因为我们在这里讨论的是人的生命,所以召回指标是这个项目中最重要的指标,远远高于精确度、准确度或任何其他评估指标。为此,我编写了一个函数,它接受一个管道对象(例如,来自上面的函数)和一组要调优的超参数,并返回一个网格搜索对象。如果你要优化的不是回忆,比如准确性,你只需要改变评分参数来反映这一点。
作者要点。
最后,我编写了一个函数来接收一个网格搜索对象(就像上面的函数所创建的那样),并返回用于评估的相关信息。这才是真正的奇迹发生的地方。最后一个函数接收上面的网格搜索对象,并开始优化您的模型。你坐着等着。你的电脑发出一些可怕的噪音。你慢慢后退。你的丈夫担心地问,他是否能在三个房间之外听到你的电脑。你微笑着说一切都好,而你却大汗淋漓,脑子里盘算着一台新 MacBook 的价格。你偶尔会往房间里瞥一眼,看到不祥的沙漏仍在屏幕上,深灰色的圆圈仍在 Jupyter 笔记本的右上角,呼呼声和咆哮的音量只会越来越大。但是过了一段时间后,一切又恢复了光明和宁静。在屏幕上,在你的输出单元里,等待着你的是:
- 根据您选择的评估标准,您的模型的最佳可能超参数,
- 一份分类报告,其中包含您针对此优化模型的精确度、召回率、F1 分数和准确度值,
- 还有一个彩色编码的混淆矩阵,告诉你基于这个模型预测的真阳性、真阴性、假阳性和假阴性的数量。
作者要点。

图片作者。
然后,一旦你有了这个,你就可以从头再做一遍!一遍又一遍,尽可能多的次数,直到你有了你最好的模型。(而且不,当电脑发出那些噪音时产生的焦虑不会消失;你只是逐渐接受你的电脑随时可能爆炸的事实。它让生活变得令人兴奋。)就我个人而言,我尝试了几个决策树、一些逻辑回归、一个随机森林、一个支持向量机,甚至一些带有 TPOT 分类器的 AutoML(没有我希望的那么好)。上面这张图片是我用额外的树分类器完成的最好的模型。它的召回率为 97%,虽然不完美,但比我的基线模型的召回率 77%要好得多。
等等,但是这个额外的树分类器到底是什么呢?
如果你熟悉随机森林,那么你就知道它是一种由决策树集合组成的算法。Extra Trees 是一种非常类似的算法,它使用一组决策树来最终预测数据点属于哪个类或类别。然而,额外树不同于随机森林,因为它使用整个原始样本,而不是像随机森林那样使用替换对数据进行子采样。另一个区别是节点的分割方式。随机森林总是选择最好的可能分割,而额外的树选择随机分割。额外的树和随机森林都是为了优化最终结果而设计的。
我感到惊讶的是,我能够用额外的树建立一个比随机森林更好的模型。虽然我的随机森林模型和我的网格搜索的最佳参数返回了 94%的良好回忆分数,但我的额外树模型返回了更好的分数。这里的教训是,使用不同的算法和分类器会有很多收获,所以不断迭代不同的模型和算法,直到找到最适合特定数据集和评估指标的模型和算法,这总是一个好主意。
在令人头痛的线性回归和处理多重线性和异方差等问题之后,这些分类模型真的是一股新鲜空气。看到这些模型能做多少事是很有启发性的;他们如何在不完善的数据下仍然表现出色。我特别喜欢直观地看到决策树模型如何做出决策的能力!了解所有这些不同的模型是如何运作的,它们背后的数学和科学,以及它们所呈现的直觉和可解释性是非常有趣的。我迫不及待地想进入我们的下一阶段,这将向我们介绍像深度学习和神经网络这样的概念。
那么,我的项目的最终结果是什么?我能够拒绝我的无效假设,即 CTG 数据和胎儿健康结果之间没有关系,并得出结论,有风险的胎儿健康结果可以通过模型预测 97%的时间。但是事情远不止如此;如果你对我使用的方法、详细的结果和可视化以及我的结论和建议感兴趣,请查看我的 GitHub repo 项目:【https://github.com/dtunnicliffe/fetal-health-classification】T2。
从 PayPal API 提取数据
原文:https://towardsdatascience.com/extract-data-from-paypal-api-c25c76748746?source=collection_archive---------30-----------------------
或者使用 Serverless Node.js 从任意数据源 API 加载,并在加载到数据仓库之前执行任何 ETL/ELT

作者图片
代码为的 Github 库
项目大纲
您将了解如何:
创建一个开发人员访问和沙箱和模拟测试交易的贝宝账户
- 创建一个沙盒账户,在 PayPal 的测试环境中集成和测试代码。
- 用一些事务数据填充您的测试环境
- 尝试从 PayPal 报告 API 中提取这些数据。
使用 AWS Lambda 创建 PayPal 数据连接器
- 连接到您的 PayPal 帐户,使用 PayPal API 授权令牌提取交易数据,例如,昨天的交易数据。
- 使用名为的配置文件。/config.json ,它将设置如何访问和保存你的数据。例如,您可能希望使用与数据仓库中的表名相同的前缀来命名文件。
- 向 PayPal API 发送 http 请求以获取交易数据,并将结果作为 JSON 文件保存到您的 AWS S3 存储桶中。
- 使用 npm 运行测试命令在本地运行

图片作者@mshakhomirov
关于这个想法
构建数据仓库:
通常,您会希望将您的数据仓库解决方案(BigQuery、Snowflake 或任何其他解决方案)放在图表的中心。
- 轻松连接任何外部数据源,即设置管道从 PayPal API 获取数据并保存到云中。
- 将数据加载到 BigQuery (AWS S3 到带有节点的 BigQuery 摄取管理器。JS)
- 使用 Git、CI/CD 创建文档化的数据转换管道。例如,用数据表单
- 通过云形成或地形简化和自动化部署(基础设施代码
- 使用 Google Data Studio 创建 BI 报告(例如,收入对账等。)

作者图片💡迈克·沙克霍米罗夫
现代数据堆栈工具(当然不是完整的列表):
- 摄入:五川,缝合
- 仓储:雪花,大查询,红移
- 转换:dbt、数据流、API。
- BI: Looker,Mode,Periscope,Chartio,Metabase,Redash

图片作者@mshakhomirov
谈到数据提取和摄取您可能希望使用付费和托管工具,如 Fivetran 或 Stitch 从任意数据源(即支付商户提供商、汇率、地理编码数据库等)提取数据。)但是如果你遵循这个指南,你将完全有能力自己做这件事。
真实生活场景
假设你是一名数据工程师
您正在进行一个项目,将各种数据源连接到您在 BigQuery 中的数据仓库。您的公司是一家手机游戏开发工作室,在 IOS 和 ANDROID 两个平台上销售各种产品。
你的筹码
你的开发栈是混合的,包括AWS和GCP。你的团队经常使用 Node.js。
数据科学团队使用 Python,但服务器和客户端数据管道是使用 Node 创建的。
您的数据堆栈是现代的、事件驱动的和数据密集型的。
数据仓库解决方案必须具有足够的成本效益和灵活性,以便您可以添加任何您需要的数据源。
它必须能够轻松扩展,以满足您不断增长的数据。
任务
所有数据来自各种数据表面的文件,即数据库、kinesis 消防水带流和各种通知服务。它以不同的格式(CSV、JSON、PARQUET 等)存储到您的云数据湖中。).
作为一名数据工程师,你的任务是创建一条新的数据管道,将来自 PayPal 的财务数据输入数据仓库。财务团队将使用它来分析谷歌数据工作室每天的收入对账报告。
您决定使用 AWS Lambda 函数和 Node.js 每天从 PayPal 交易 API 提取数据,并首先保存到 AWS S3。

图片作者@mshakhomirov
这个简单的管道将确保数据被保存,并准备加载到 BigQuery,在那里它将被组织成表。
先决条件、库和设置
工具
- 已安装 Node.js 和节点包管理器
- 对云计算(Amazon Web Services 帐户)、AWS CLI 和 AWS SDK 有基本的了解
- PayPal 开发者账户
- Shell(命令行界面)命令和脚本(高级)。
技术
- 对 REST APIs 的理解。
- 良好的节点知识。JS(中级)。您将创建一个 Lambda 函数。
- 你必须了解 Node。JS 基本概念,即异步函数、节点包和代码如何工作。
- 基本调试(控制台、打印报表)
- 循环:即用于
- 分支:if、if/else、开关
- Shell 命令和脚本,就像您希望从命令行使用 AWS CLI 部署 Lambda 并能够在本地测试它一样。
我们开始吧
第一步。创建一个开发人员访问和沙箱的贝宝帐户
转到 developer.paypal.comhttps://developer.paypal.com*,创建一个* 应用 。这将是对 Node.js 应用程序的集成。
创建一个 沙盒账号 在 PayPal 的测试环境中集成和测试代码。
用一些交易数据填充您的测试环境
试着将 收拢 这些数据从 PayPal 的 API 上报。
如果这些步骤中的任何一个导致困难,请参考下面的帮助部分。
第二步。在你的机器上创建一个名为 bq-paypal-revenue 的本地 Node.js 应用。此应用程序将执行以下操作:
连接到您的 PayPal 账户,使用 PayPal API 授权令牌提取交易数据,例如昨天的交易数据。
使用一个名为 的配置文件。/config.json 其中将有所有设置如何访问和保存您的数据。例如,您可能希望使用与数据仓库中的表名相同的前缀来命名文件。
发送http请求到PayPal API获取交易数据并将结果作为 JSON 文件保存到您的 AWS S3 桶中。
本地运行用 npm 运行测试 命令
可交付的
该项目的可交付成果是一个工作的 Node.js 应用程序,它将能够在本地运行,并作为 lambda 函数部署在 AWS 帐户中。
感觉卡住了?
帮助
第一步:贝宝沙盒
-
使用访问令牌查询 PayPal API。如何获得访问令牌的更多信息可以在这里找到
-
PayPal API 基础知识解释如何创建实时和沙盒 PayPal 账户。尝试创建一个沙盒。另一个有用的链接是 PayPal 沙盒测试指南,它解释了测试过程,并提供了一些创建 PayPal 开发者账户的技巧。
-
使用您的沙箱
client_id:secret并将其添加到 CURL 请求中: -
结果,您会看到类似这样的内容:

图片作者@mshakhomirov
-
现在,您可以使用这个访问令牌创建另一个请求来提取交易或您需要的任何其他数据,例如发票等。重复使用访问令牌,直到它过期。然后,获得一个新的令牌。这个想法是自动化的过程,所以在步骤 2 中,我们将创建一个微服务,以编程方式从 PayPal 获取数据。
-
用上一步中的标记替换''。您将看到类似这样的内容:

作者图片💡迈克·沙克霍米罗夫
- 您可以看到还没有交易。
第一步:如何模拟测试贝宝交易
- 在您的沙箱中创建样本交易的最简单方法是使用 PayPal 产品 API 执行器

图片作者@mshakhomirov
你想使用贝宝结账标准,并创建一个完整的交易流程将是:
- 获取访问令牌
- 创建订单(授权和获取)。快速结帐的默认意图是捕获,并在交易被买方批准后立即捕获资金。点击此处了解更多关于授权的信息。
- 批准订单(买方批准订单并乐意付款)
- 更新订单
- 获取订单付款
- 显示订单详细信息
让我们创建一个订单(用您的令牌替换“持票人”后的短语):
- 因此,您将看到创建了一些示例事务:

图片作者@mshakhomirov
- 客户现在将接受并批准它:

图片作者@mshakhomirov
重要提示:为了模仿 Sandox 客户,您需要在 paypal.developer.portal 中创建一个测试个人账户,该账户不同于您的测试企业账户。通过这样做,您将能够实际批准您的订单。前往 https://developer.paypal.com/developer/accounts/创建一个测试个人账户。然后登录并使用它来批准订单。

图片作者@mshakhomirov

图片作者@mshakhomirov
-
订单批准后,您可能需要更新它。下面的 CURL 将订单更新为已创建或已批准状态。您不能更新状态为已完成的订单。
-
最后一步是获取付款。转到 APEX 或点击“捕获”或使用 CURL 和之前批准的订单 ID:
重要提示:订单必须得到客户的批准才能获得付款,这是最棘手的部分,通常需要一些服务器开发,但您可以简单地使用 APEX 创建一个虚拟买家(使用个人沙盒帐户)来测试它。
- 现在您可以查看订单详情:
步骤 1:使用报告 API 从 PayPal 获取历史交易
当您的沙箱中最终有了一些完成的事务时,您会想要使用一个报告 API :
输出应该是这样的:

图片作者@mshakhomirov
这个 JSON 响应包含您需要的选定日期范围内的所有事务。接下来,您可能希望以编程方式运行这个命令,并将数据保存到云存储中,例如 AWS S3。这是一个“数据湖”步骤,旨在安全地存储您的数据,以便您可以在加载到数据仓库之前检查或更改它。这当然会在云存储和数据仓库中创建您的数据的副本,但是我建议这样做,以便您可以轻松检查任何数据加载错误。在存储桶上设置一个过期策略将在一定天数后清理云存储。
步骤 2:用 AWS Lambda 创建一个无服务器 Node.js 应用程序,从 PayPal API 中提取数据
您可能希望使用以下 Node.js 模块:
- axios 向 PayPal API 发出 HTTP 请求
- 时刻处理日期时间数据和参数
- 将数据保存到 S3 的 aws-sdk
- 在本地测试你的 lambda
初始化一个新的 Node.js 应用程序,这样你就有了这样一个./package.json:
您的应用程序目录如下所示:

图片作者@mshakhomirov
-
使用
./config.json来分隔您的真实环境和临时环境。例如: -
*创建一个文件来配置您的 PayPal 访问令牌凭证,并将“Basic ”替换为 Base64 编码的 client_id 和 secret 的组合,即:您还可以从本教程开始时获得 access_token 的一个 CURL 请求中复制并粘贴这个 Base64 编码的字符串。
-
您可能希望将 Base64 编码的字符串
clien_id:client_secret传递给授权头。例如,如果您使用 Postman,您会发现您的 client_id 和 client_secret 值附加到标题中的文本“Basic”中,如下所示:"Authorization": "Basic QWNxd2xIYT.....使用它来获得带有 OAuth2.0 的访问令牌,然后在您的 PayPal API 调用中使用。点击阅读更多相关信息。 -
现在创建一个名为
app.js的文件: -
现在,您需要创建一个名为
processEvent的函数来处理一个事件,该事件将触发该函数从 PayPal 提取交易数据。
最终解决方案
检查 Github 库
-
如果交易请求太大,PayPal 将对输出进行分页。所以
processEvent会先估算大小,然后从 PayPal API 中逐页提取数据。 -
要在您的机器上本地测试和运行,请使用:
npm i,然后使用命令npm run test。 -
使用
[./deploy.sh](https://github.com/mshakhomirov/pipelinetools/blob/master/api-connectors/stack/bq-paypal-revenue/deploy.sh)在 AWS 中部署您的 Lambda。 -
当 Lambda 部署到 AWS 并正在执行时,例如,由 Cloudwatch 事件触发。AWS Cloudwatch 事件将使用计划每天触发 Lambda。
-
Lambda 将从。/token_config.json 并通过 PayPal 认证
-
循环查看所需的每份 PayPal 报告(中的表格。/config.json) Lambda 会将每批交易以 json 格式保存到 AWS S3 中。
-
这将为您的数据仓库中的一个表提供数据,也就是说,如果您将该文件命名为与您的数据仓库表相同的名称,它可以通过编程方式加载到相关的表中。因此另一个微服务可以从那里获得它(不包括在这个项目中)
-
您可以通过运行以下脚本在 lambda 部署时手动调用它(需要 AWS CLI):
-
app.js
项目结论
传统的 ETL,代表提取、转换和加载,现在已经发展到 ELT。数据从源系统中提取出来,加载到数据仓库中,然后在数据仓库中进行转换。这个实时项目帮助您管理“E”部分,即从外部数据源提取数据。

图片作者@mshakhomirov
- 无论您是一家初创企业还是一家全球性企业,您都已经学会了如何使用无服务器从任意的数据源中提取数据。
- 您已经学习了数据湖、数据仓库和解耦数据存储的概念。
- 您已经学习了如何使用 AWS Lambda 函数创建简单可靠的数据提取管道。
- 所有这些都提供了一个思路或模板,可以很容易地扩展并重用于将来可能需要的任何其他任意数据源。
有用的资源
[1]https://docs . AWS . Amazon . com/AWS count billing/latest/about v2/free-tier-limits . html
[2]https://aws.amazon.com/lambda/
3https://aws.amazon.com/cli/
https://developer.paypal.com/docs
最初发表于https://mydataschool.com。
使用 AWS 提取电子邮件附件
原文:https://towardsdatascience.com/extract-email-attachment-using-aws-624614a2429b?source=collection_archive---------1-----------------------
数据工程—通过处理电子邮件附件创建数据管道

照片由妮可·德·霍斯在爆发
电子邮件是后互联网时代人与人之间最原始的交流方式。电子邮件中包含附件是非常常见的。在大多数情况下,出于个人或专业原因,这些附件应该由用户手动处理。
在当前的云计算时代,不同环境上托管的应用程序可以通过 API 进行通信,也可以通过队列交换数据。内部和云系统也可以通过站点到站点 VPN 连接或像 AWS DirectConnect 这样的专用连接进行连接。
然而,即使在今天的云计算时代;有些情况下,数据是通过带有附件的电子邮件从遗留系统(内部)或遗留应用程序发送的,因此个人可以手动下载这些附件并上传到任何云服务。这些附件可能包含需要进一步提取、处理和分析(或 ETL)的重要信息。以下是一些这样的原因或场景:
- 在内部和云系统之间创建 VPN 或专用私有连接不可用或缺乏可能性(由于技术或组织原因)
- 无法创建基于 API 或队列的与传统 CRM / HR /财务系统的通信
本文的核心目的是提供一种使用 AWS 服务提取电子邮件附件的方法。本文还提供了在 AWS 中创建灵活管道来处理电子邮件附件的选项。使用 AWS 服务提取电子邮件附件涉及多个步骤,下面详细介绍了这些步骤:

使用 AWS 提取电子邮件附件的图示
- 使用亚马逊工作邮件配置专用电子邮件服务器
- 配置亚马逊 SES (简单电子邮件服务)发送一份原始格式的接收邮件到亚马逊 S3 (简单存储服务)的桶中
- 在 python 中创建 AWS Lambda 函数,从原始电子邮件消息中提取电子邮件附件
- 配置 S3 事件通知以调用 AWS Lambda 上传事件(当原始格式的电子邮件被添加到 S3 时)将电子邮件附件发布到另一个 S3 存储桶
# 1 —使用亚马逊工作邮件配置专用的电子邮件服务器
第一个显而易见的问题是——为什么不使用像 Outlook 这样更受欢迎的电子邮件服务器呢?为什么使用亚马逊工作邮件?
有现成的自动化工作流工具,如 Microsoft Flow 或 Zapier。例如,Zapier 有一个现成的自动化工具,可以提取电子邮件附件并直接发送到 AWS S3 公司。这种方法的主要问题(除了需要使用高级订阅)是必须输入 AWS 访问密钥 ID 和 AWS 秘密访问密钥。这给共享外部服务的密钥带来了安全风险,对许多人来说可能不是一个合适的选择。
解决方案是使用亚马逊工作邮件创建一个专用的电子邮件主机。这意味着可以创建一个专用且安全的电子邮件主机,遗留应用程序或遗留系统可以在其中发送带有附件的电子邮件以供进一步处理。鉴于电子邮件正在进入 AWS 生态系统,与 S3 等其他亚马逊服务的集成变得非常容易。
使用 Amazon WorkMail 创建专用电子邮件服务器的步骤
- 登录 AWS 控制台并导航至服务——Amazon WorkMail
- 选择创建组织的选项。Amazon WorkMail 组织为您公司的一组用户提供电子邮件访问权限。他们的新电子邮件地址是根据您为组织选择的域创建的
- 为新电子邮件服务器选择电子邮件域,以关联新电子邮件地址,提供别名并创建组织。有选项来选择电子邮件域,包括现有的路由 53 域,一个新的路由 53 域,一个外部域(像一些托管在 godaddy.com)或一个免费的测试域。在这个具体的案例中,选择“免费测试域”作为邮箱域的选项,输入“ aws-automation ”作为别名(见下文参考)

使用“免费测试域”创建一个亚马逊工作邮件组织
创建专用电子邮件地址以接收来自遗留系统或应用程序的电子邮件的步骤
- 导航到 Amazon WorkMail 并选择新创建的组织—“AWS-automation”
- 选择创建用户的选项。按照提示和创建一个名为“员工培训信息”的用户。假设我们之前输入的组织名称是“aws-automation ”,并且我们正在使用测试电子邮件域;电子邮件地址将被创建为'emp-training-info@aws-automation.awsapps.com'(参见下文以供参考)

根据新的电子邮件域配置用户
如果在创建组织时使用了不同的电子邮件域,比如说——'myorganization.com'(假设正确配置了 Route 53 条目),那么在这种情况下,电子邮件地址可能是emp-training-info@myorganization.com
# 2 —配置亚马逊 SES 向亚马逊 S3 发送原始格式的接收电子邮件副本
假设我们已经使用 Amazon WorkMail 创建了一个电子邮件服务器,内置的 AWS 集成在 Amazon SES 中创建了以下两条记录:
- 测试电子邮件域→'aws-automation.aws-apps.com'被自动验证。可以在亚马逊 SES——域名中找到记录
- 自动创建一个名为“ INBOUND_MAIL 的活动规则集。使用 Amazon SES-rules Sets 下的“查看活动规则集”操作可以找到记录
为活动规则集创建新规则的步骤,该规则集将发送到特定电子邮件地址的电子邮件复制到特定的 S3 存储桶中
- 选择 Amazon SES —规则集—查看活动规则集下的“创建规则”选项
- 添加收件人以个性化规则。在这种情况下,将接收人添加为'【emp-training-info@aws-automation.awsapps.com】T4
- 通过选择原始电子邮件格式应复制到的目标 S3 存储桶,为“S3”添加操作。
- 从下拉选项中选择一个现有的 S3 时段或创建一个时段。在这个具体的例子中,创建一个 S3 存储桶'legacy-applications-email'。存储桶名称是全局唯一的,因此请选择您自己的存储桶名称。(可选)选择一个对象关键字前缀。否则继续下一步
- 提供一个规则名称(比如—put-email-copy-legacy-applications-email-bucket)并创建规则

将特定收件人的传入电子邮件副本(原始)重定向到 S3 存储桶的 SES 规则
现在,发送到已配置的电子邮件地址(emp-training-info@aws-automation.awsapps.com)的传入电子邮件的副本将在已配置的 S3 桶(传统-应用-电子邮件)中创建。然而,以原始电子邮件消息格式复制的消息是不可读的,因此不能提取电子邮件附件。使用 python 包“email”的 python 程序可用于从原始电子邮件消息中提取电子邮件附件。这将在下面讨论。
# 3 —用 python 创建 AWS Lambda 函数,从原始电子邮件中提取电子邮件附件
以下是在 python 中创建 AWS lambda 函数以提取电子邮件附件的步骤:
- 在 AWS 控制台中导航到 Lambda
- 通过提供函数名、运行时和默认执行角色来创建函数。在这种情况下,选择函数名为“提取-电子邮件-附件”,运行时为“Python 3.8”。对于默认执行角色,选择一个预先创建的角色,其中包括 Lambda 和 S3 的权限(这可以在此步骤之前通过 AWS IAM 创建)
- 复制下面提到的代码。代码也可以在https://gist . github . com/sandeepmanchi/365 BFF 15 F2 f 395 eeee 45 DD 2d 70 e 85 e 09:
从原始电子邮件中提取附件并发送到目的地 S3 桶的 Python 代码
上述代码执行以下步骤:
- 根据 S3 存储桶“传统-应用程序-电子邮件”,从新创建的原始电子邮件中提取存储桶名称和对象键。这是通过 Amazon SES 中配置的规则接收传入电子邮件的桶
- 使用电子邮件包,原始电子邮件上的附件和发件人地址被提取出来(第 24-28 行)
- 提取的附件临时存储为 attach.csv,然后上传到选择的目标存储桶,文件名为' attach-upload- <时间戳>。CSV’(第 36–40 行)。在这种情况下,目的地 S3 桶被选择为“提取-电子邮件-附件”。第 37 行的代码还使用电子邮件上的 from 地址在这个 bucket 中创建了一个文件夹。这样,来自不同发件人地址的附件就可以分组到不同的文件夹中
# 4 —配置 S3 事件通知以调用 AWS Lambda(当原始格式的电子邮件被添加到 S3 时),并将电子邮件附件提取到另一个 S3 存储桶
对于在步骤# 2(legacy-applications-email)中创建的保存原始电子邮件消息副本的存储桶,创建一个事件类型为‘Put’的 S3 事件通知,并配置为调用在步骤# 3(extract-email-attachments)中创建的 lambda 函数

调用步骤 3 中创建的 AWS Lamda 的 S3 事件通知
在步骤# 3 中创建的 lambda 函数使用一个目的地桶,提取的电子邮件附件将被发送到该桶。在这个特定的例子中,目的地桶是'extracted-email-attachments'。这个桶应该在创建 lambda 函数之前创建。也建议更改存储桶名称,因为它是全局唯一的。
这就完成了所有的步骤。要进行测试,请向配置的电子邮件地址发送一封带有附件的电子邮件。在这种情况下是 emp-training-info@aws-automation.awsapps.com。发送电子邮件后,导航到 S3 存储区,注意到一个以“attach-upload-*”开头的对象。csv。该对象将使用文件夹前缀创建,其中文件夹名称与从电子邮件中提取的发件人地址相同(参见下文以供参考)。

提取电子邮件并复制到目标 S3 存储桶,按发件人地址分组
这将是作为电子邮件的一部分发送的相同附件。下载并验证内容进行确认。假设文件的内容现在在 AWS 中可用,那么可以通过创建一个定制的数据管道来进一步处理该内容,该数据管道可用于进一步的分析。
构建数据流水线以处理提取的电子邮件附件
B 下图展示了一种潜在的数据管道架构,该架构可以对提取的电子邮件附件执行 ETL 过程,并使其可用于进一步分析。这里假设发送到 Amazon WorkMail 的原始电子邮件包含附件形式的 csv 文件

AWS 中处理提取附件的潜在数据管道
下面是从第 5 步到第 10 步的细分:
-
5 — AWS Glue 将 S3 桶(银桶)视为其输入,读取 csv 文件并转换为拼花格式。可选地,可以使用 AWS Lambda 代替胶水。两者之间的选择取决于数据的数量和性质
-
6 —然后将转换后的数据放入另一个 S3 桶(黄金桶)
-
7 —金桶将成为 S3 数据湖的一部分
-
8、# 9—S3 数据湖可以作为 Athena 和 RedShift 的输入
-
10—Athena 和 Redshift 的数据可以通过 AWS Quciksight 进行分析
这就完成了详细介绍使用 AWS 提取电子邮件附件的逐步过程的文章。还提供了一个示例体系结构,以便使用 AWS 服务通过 ETL 过程进一步处理提取的附件
从文档中提取关键词,一种无监督的解决方案
原文:https://towardsdatascience.com/extract-keywords-from-documents-unsupervised-d6474ed38179?source=collection_archive---------11-----------------------
一种从文档中自动提取关键词的解决方案。用 Python 实现了 NLTK 和 Scikit-learn。

图片由安德鲁·朱提供,路透社旧新闻
假设您手中有数百万(也许数十亿)的文本文档。无论是客户支持单、社交媒体数据还是社区论坛帖子。数据生成时没有标签。你正在绞尽脑汁给那些随机的文档添加标签。
手动标记是不切实际的;给出一个现有的标签列表很快就会过时。雇佣一个供应商公司来做标记工作是非常昂贵的。
你可能会说,为什么不用机器学习呢?比如,网络深度学习。但是,神经网络首先需要一些训练数据。适合您数据集的训练数据。
那么,有没有一个解决方案可以让我们给文档加标签满足:
- 不需要预先请求训练数据。
- 人工干预最小,可自动运行。
- 自动捕捉新单词和短语。
这篇文章记录了我是如何用 Python 提取关键字的,它是如何工作的,walkarounds。
注意,本文中的代码是在 Jupyter Notebook 中运行和测试的。如果您运行一个代码块,但是受到缺少导入包错误的欢迎,那么这个包一定已经在前面的某个地方被导入了。
核心理念
TF-IDF 是一种广泛使用的算法,用于评估一个单词与文档集合中的一个文档的相关程度。
在我的上一篇文章:在 Python 和 scikit-learn 中使用 TF-IDF 测量文本权重中,我使用了一个简单的示例来展示如何计算文档中所有单词的 TF-IDF 值。纯 Python 代码和使用 scikit-learn 包。
基于 TF-IDF,那些唯一且重要的词在某个文档中应该有很高的 TF-IDF 值。因此,理论上,我们应该能够利用文本权重#来提取文档中最重要的单词。
例如,一个谈论 scikit-learn 的文档应该包括更高密度的关键字 scikit-learn ,而另一个谈论“熊猫”的文档应该具有针对熊猫的高 TF-IDF 值。

从文档语料库中提取关键词的步骤
目标文档
因为我不能在这里使用我的日常工作数据库,也要确保你可以在你的本地机器上用最少的努力执行关键字提取示例代码。我发现来自 NLTK 的路透社文档语料库是一个很好的关键词提取目标。
如果您不熟悉 NLTK 语料库,这篇文章可能有助于在不到一个小时的时间内开始使用 NLTK:书籍写作模式分析—使用一个用例开始使用 NLTK 和 Python 文本分析。
下载路透社文集。运行 Python 代码:
import nltk
nltk.download("reuters")
列出我们刚刚下载的语料库中的所有文档 id。
from nltk.corpus import reuters
reuters.fileids()
检查一个文档的内容及其类别。
fileid = reuters.fileids()[202]
print(fileid,"\n"
,reuters.raw(fileid),"\n"
,reuters.categories(fileid),"\n")
路透社的语料库是由重叠的类别组织的。我们还可以通过类别名称获取文档。关于完整的 NLTK 语料库操作,请查看这篇精彩的文章:访问文本语料库和词汇资源
建立忽略单词列表
为了节省时间和计算资源,我们最好排除停用词,如“am”、“I”、“should”。NLTK 提供了一个很好的英语停用词表。
from nltk.corpus import stopwords
ignored_words = list(stopwords.words('english'))
您还可以使用 NLTK 停用词列表中没有的停用词来扩展该列表。
ignored_words.extend(
'''get see seeing seems back join
excludes has have other that are likely like
due since next 100 take based high day set ago still
however long early much help sees would will say says said
applying apply remark explain explaining
'''.split())
构建关键词词汇—单个单词
在使用 TF-IDF 提取关键字之前,我将构建自己的词汇表,包括单个单词(例如“Python”)和两个单词(例如“white house”)。
这里我将使用 scikit-learn 中的CountVectorizer来执行单个单词的提取工作。
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
count_vec = CountVectorizer(
ngram_range = (1,1) **#1**
,stop_words = ignored_words
)
text_set = [reuters.raw(fileid).lower() for fileid in reuters.fileids()] **#2**
tf_result = count_vec.fit_transform(text_set)
tf_result_df = pd.DataFrame(tf_result.toarray()
,columns=count_vec.get_feature_names()) **#3**
the_sum_s = tf_result_df.sum(axis=0) **#4**
the_sum_df = pd.DataFrame({ **#5**
'keyword':the_sum_s.index
,'tf_sum':the_sum_s.values
})
the_sum_df = the_sum_df[
the_sum_df['tf_sum']>2 **#6**
].sort_values(by=['tf_sum'],ascending=False)
代码 #1 ,指定 CountVectorizer 将只计算单个单词。又名,1 克字。你可能会问,为什么不使用ngram_range = (1,2)然后同时获得单个和双元单词呢?这是因为在这里捕获 bigram 将得到类似于"they are"、"I will"和"will be的短语。这些是连接短语,通常不是文档的关键字或关键短语。
另一个原因是为了节省内存资源,由于组合太多,在这个阶段捕获二元短语将使用大量内存。
代码 #2 ,使用 Python 理解将所有路透社文章放在一行代码中。
代码 #3 ,将计数向量结果转换为可读的 Pandas 数据帧。
代码 #4 ,产生一系列包括关键字及其在语料库中总出现次数的列表。
代码 #5 ,将系列转换为数据帧,以便于阅读和数据操作。
代码 #6 ,取只出现 2 次以上的词。
如果你偷看一下the_sum_df[:10]设置的前 10 个结果,你会看到那些最常用的词:

最常用的 10 个词
最常见但无意义的是,我们可以通过 Python 切片轻松地按比例排除这些:
start_index = int(len(the_sum_df)*0.01) # exclude the top 1%
my_word_df = the_sum_df.iloc[start_index:]
my_word_df = my_word_df[my_word_df['keyword'].str.len()>2]
也删除少于 2 个字符的单词,如“vs”,“lt”等。
注意,我用的是.iloc而不是.loc。因为原始数据集是按 TF(词频)值重新排序的。iloc将切片放在索引的索引上(或索引标签的序列上)。但是loc会在索引标签上切片。
构建关键词词汇—两个单词的短语(二元短语)
为了构建二元短语列表,我们不仅需要考虑一起出现的频率,还需要考虑它与相邻词的关系。
例如短语they are,多次出现在一起,但they are只能跟随着有限的词,如they are brothers、they are nice people,这些词具有高的内部粘性,但低的外部连接灵活性。
外部连接灵活性通常可以用信息熵来度量。熵值越高,表示与其他单词一起使用的可能性越高。

对于我们的大脑来说,具有高内部粘性(计数频率)和高外部熵的短语,我们称之为“常用短语”,这些是我们想要添加到提取词汇中的内容。
NLTK 为解决二元短语提取问题提供了类似的解决方案。
from nltk.collocations import BigramAssocMeasures
from nltk.collocations import BigramCollocationFinder
from nltk.tokenize import word_tokenize
text_set_words = [word_tokenize(reuters.raw(fileid).lower())
for fileid in reuters.fileids()] **#1**
bigram_measures = BigramAssocMeasures()
finder = BigramCollocationFinder.from_documents(text_set_words) **#2**
finder.apply_freq_filter(3) **#3**
finder.apply_word_filter(lambda w:
len(w) < 3
or len(w) > 15
or w.lower() in ignored_words) **#4**
phrase_result = finder.nbest(bigram_measures.pmi, 20000) **#5**
colloc_strings = [w1+' '+w2 for w1,w2 in phrase_result] **#6**
代码 #1 ,在这个 Python 理解表达式中,我使用word_tokenize将文档标记为单词列表。输出将是这样的:
[
['word1','word2',...,'wordn'],
['word1','word2',...,'wordn'],
...
['word1','word2',...,'wordn']
]
代码 #2 ,从标记化文档列表中启动 bigram finder 对象。还有另外一个功能from_words()可以处理分词词表。
代码 #3 ,删除频率小于 3 的候选项。
编码 #4 ,删除字长小于 3 或大于 15 的候选字。还有那些在ignored_words列表里的。
代码 #5 ,使用BigramAssocMeasures中的pmi函数测量 2 个单词短语的可能性。你可以在静态自然语言处理基础的第 5.4 节中找到它是如何工作的。和该链接列出所有其他测量功能和源。
编码 #6 ,将结果转换成可读性更好的格式。
通过将BigramAssocMeasures、BigramCollocationFinder替换为TrigramAssocMeasures和TrigramCollocationFinder,您将获得 3 字短语提取器。在 Reuters 关键字提取示例中,我将跳过 3 个单词的短语。我在这里发布了示例代码,以备您需要。
from nltk.collocations import TrigramAssocMeasures
from nltk.collocations import TrigramCollocationFinder
from nltk.tokenize import word_tokenize
text_set_words = [word_tokenize(reuters.raw(fileid).lower())
for fileid in reuters.fileids()]
trigram_measures = TrigramAssocMeasures()
finder = TrigramCollocationFinder.from_documents(text_set_words)
finder.apply_freq_filter(3)
finder.apply_word_filter(lambda w:
len(w) < 3
or len(w) > 15
or w.lower() in ignored_words)
tri_phrase_result = finder.nbest(bigram_measures.pmi, 1000)
tri_colloc_strings = [**w1+' '+w2+' '+w3 for w1,w2,w3** in tri_phrase_result]
tri_colloc_strings[:10]
激动人心的时刻,用 TF-IDF 测量关键词权重
现在,让我们将单个单词和两个单词的短语结合在一起,构建路透社定制词汇列表。
my_vocabulary = []
my_vocabulary.extend(my_word_df['keyword'].tolist())
my_vocabulary.extend(colloc_strings)
让我们发动引擎。请注意,请找一台内存至少为 16g 的机器来运行代码。TF-IDF 的计算需要一段时间,并且可能会占用您的大量内存。
from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer(
analyzer ='word'
,ngram_range =(1, 2)
,vocabulary =my_vocabulary)
text_set = [reuters.raw(fileid) for fileid in reuters.fileids()]
tf_idf = vec.fit_transform(text_set)
result_tfidf = pd.DataFrame(tf_idf.toarray()
, columns=vec.get_feature_names()) #1
在将结果集转换为代码#1 中的 Dateframe 之后,result_tfidf保存所有关键字的 TF-IDF 值:

看看结果
让我们检查其中一篇文章,并与上面的提取器提取的关键字进行比较,以验证有效性。
通过指定fileid索引输出一个原始文件。
file_index= 202 # change number to check different articles
fileid = reuters.fileids()[file_index]
print(fileid,"\n"
,reuters.raw(fileid),"\n"
,reuters.categories(fileid),"\n")
返回 fileid ,原始内容,及其类别。(嗯,很多年前,美国和日本打了一场关税战)
test/15223
WHITE HOUSE SAYS JAPANESE TARRIFFS LIKELY
The White House said high U.S.
Tariffs on Japanese electronic goods would likely be imposed as
scheduled on April 17, despite an all-out effort by Japan to
avoid them.
Presidential spokesman Marlin Fitzwater made the remark one
day before U.S. And Japanese officials are to meet under the
emergency provisions of a July 1986 semiconductor pact to
discuss trade and the punitive tariffs.
Fitzwater said: "I would say Japan is applying the
full-court press...They certainly are putting both feet forward
in terms of explaining their position." But he added that "all
indications are they (the tariffs) will take effect."
['trade']
从我们刚刚酝酿的result_tfidf dataframe 对象中打印出前 10 个关键字。
test_tfidf_row = result_tfidf.loc[file_index]
keywords_df = pd.DataFrame({
'keyword':test_tfidf_row.index,
'tf-idf':test_tfidf_row.values
})
keywords_df = keywords_df[
keywords_df['tf-idf'] >0
].sort_values(by=['tf-idf'],ascending=False)
keywords_df[:10]
十大关键词:

看起来这里的white和house与white house是重复的。我们需要删除那些已经出现在两个单词短语中的单词。
bigram_words = [item.split()
for item in keywords_df['keyword'].tolist()
if len(item.split())==2]
bigram_words_set = set(subitem
for item in bigram_words
for subitem in item)
keywords_df_new = keywords_df[**~**keywords_df['keyword'].**isin**(bigram_words_set)]
上面的代码首先构建了一个单词set,它包含了一个双单词短语中的单词。然后,通过~xxxx.isin(xxxx)过滤掉已经在二字短语中使用的单字。
其他考虑
您拥有的文本语料库越大,TF-IDF 在提取关键词方面的表现就越好。路透社的语料库包含 10788 篇文章,结果表明它是有效的。我相信这个解决方案对于更大的文本数据库会更好。
上面的代码在我的 Macbook Air M1 上运行不到 2 分钟,这意味着每日刷新结果集是可行的。
如果您有数百 GB 甚至 TB 大小的数据。您可能需要考虑用 C/C++或 Go 重写逻辑,还可能利用 GPU 的能力来提高性能。
本文描述的解决方案远非完美,例如,我没有过滤掉动词和形容词。解决方案的主干可以扩展到其他语言。
提取的关键词
让我们再次打印出最终结果。
keywords_df_new[:10]
获得最高的 TF-IDF 值,其余的关键字看起来很适合代表这篇路透社文章。目标达成!

需求的数据层次:从数据到价值的 4 个步骤
原文:https://towardsdatascience.com/extract-load-transform-learn-and-serve-getting-value-from-data-4f08d70bf343?source=collection_archive---------36-----------------------
介绍 ELTLS —提取&加载、转换、学习和服务
每个公司都希望提供高价值的数据洞察,但不是每个公司都准备好或有能力。他们经常相信围绕点击式无代码数据连接器的市场宣传。只要设置好并忘记它,所有困难的工作都会完成——对吗?严酷的事实是,为了让团队得到他们想要的东西,所有数据都必须经过几个关键步骤:高价值的洞察力。
在许多方面,集中数据是最容易的部分。现在有了比以往更多的选择,可以快速轻松地构建管道,从数百个来源接收数据,并将其加载到数据仓库或数据湖中。然而,今天的工具营销忽略了一个主要话题:实现从数据集中化到高价值洞察交付的转变。这就是数据科学的辛苦。高价值的见解不是免费的,公司只能通过测试数据来获得这些见解。
从“我们拥有数据”到“我们拥有价值”的过程非常复杂,可以分为四个关键步骤:
- 提取和加载:将来自多个来源的数据集中到一个数据仓库中
- 转换:通过转换(清理、连接、聚合)来自不同来源的数据来理解数据
- 学习:现在你已经理解了数据,建立机器学习模型。从技术上来说,“学习”只是一种更复杂转换
- 服务:向应用程序和其他系统发送见解和推论,以“实现”数据的价值
这些步骤是特定数据工作的进展,而不是流程或工具的集合。让我们来谈谈如何从“提取和加载”转移到“服务”,以及“按部就班地处理数据”意味着什么
通过它的步伐获取数据
数据团队的任务是构建数据堆栈,以支持我们归类为“数据驱动型公司的需求层次”的业务需求层次结构如下所示:

图片由 Datacoral 提供
业务关键型数据存在于数十个(如果不是数百个)不同的来源中。在某些情况下,Datacoral 和 Fivetran 等服务提供商提供连接器,将数据传输到中央仓库和湖泊中。在其他情况下,工程团队用定制代码构建这些管道。但是,即使数据在流动,也需要专业知识来弄清楚这些数据到底意味着什么。公司不能直接进入数据,开始提供预测和改善业务成果的见解。首先要做的是基础工作。
数据科学的基础工作包括实验、假设检验、标准化、清理、转换、可视化、分析等等。没有对数据的深刻理解,就无法提供见解。让我们来看看这通常是如何进行的。
从“提取和加载”转向“转换”
让我们想象一下,一个小型创业公司刚刚签下了他们的第一批客户。他们将产品使用数据保存在 PostgreSQL 数据库中,并开始考虑分析和从这些数据中获取价值。目前,它们处于数据层次的最底层。他们提升数据层次结构的第一步是从他们的数据库或其他数据源中提取数据,并将其加载到数据仓库中,如 Snowflake。
一旦数据在其仓库中可用,团队就可以开始通过简单的转换来探索和清理数据。由于他们的团队很小,这项工作很可能由他们的一名软件工程师(或非常技术性的产品经理)作为一个附带项目来完成,结果是一个小仪表板,显示用户如何使用初创公司产品的重要指标。在这一点上,创业公司不仅能够从他们的来源提取和加载数据,而且能够转换数据以产生与其产品相关的 KPI 和指标。这已经创造了巨大的价值。当团队的规模和复杂程度增长时,他们可以开始开发机器学习模型,并将数据提供给应用程序。
从“学习”到“服务”
现在,让我们想象一个更大、更成熟的公司。这家公司已经有了从不同来源提取数据的连接器。数据已经整合到数据仓库中。该团队花了几个月的时间来探索和清理他们的数据,他们有简单的仪表板来显示关键指标。团队对数据质量的信任足以依赖这些指标进行内部报告。他们现在确定了预测分析的使用案例,并决定雇佣第一批数据科学家。新的数据科学家能够依靠仓库中干净和充分理解的数据,并通过训练第一个 ML 模型来帮助团队爬上数据层次。
在生产中使用 ML 模型,团队能够服务于内部和外部用例:为他们的销售和营销团队进行客户终身价值(LTV)估计,或者为他们的客户提供省时功能。此时,团队成功地导航了数据层次结构;他们正在从锁定在源系统中的原始数据一路发展到在应用程序中提供洞察力。虽然总是有更多的工作要做,以扩展到更多的数据源、新的用例以及更大的数据量,但这是一个很少有公司能够完成的成功的数据之旅。
作为框架的数据层次结构
构建数据堆栈的想法可能令人生畏。采用数据层次结构作为总体设计框架可以让您放心。它是工具无关的,非指令性的,关注于高级设计和执行过程。
框架的第一层(“提取和加载”)涉及将数据集中到一个仓库或湖中。第二层(“转换”)涉及聚合来自多个来源的数据并发现有意义的见解。第三层(“学习”)利用机器学习模型和人工智能建立在这些见解的基础上。第四层(“服务”)允许来自 AI/ML 工作的数据为内部和外部服务、客户、仪表板等增加价值。
通过识别数据堆栈的哪些部分适合框架的每一层,数据团队可以采用概念性的方法来交付价值。也不是一对一的关系。一些数据工具可以服务于框架的多个层次。例如,Fivetran 可以在“提取和加载”和“转换”层中工作。Datacoral 可以在所有四层中工作。定制代码可以用来服务于“提取和加载”以及“学习”层。关键是使用框架来帮助设计,而不是使用一系列工具来找出价值所在。
从“提取和加载”到“服务”的转变,我们称之为“测试数据”
当采用工具优先的思维方式而不是数据层次的思维方式时,我们经常会看到混乱。不同组件和互连的数量可能看起来像天文数字。(查看我们的数据质量帖子了解更多信息。)对于刚刚起步的小型数据团队来说,找到合适的工具组合来满足特定需求是一件非常困难的事情,而且事关重大。这个工具网络需要继续存在,并使公司在未来两三年内继续发展。工具的功能也需要随着增长而扩展。
通过数据提供有价值的见解
希望提供高价值见解的公司需要测试数据。需求的数据层次提供了“什么”,正如我们在以前的帖子中探讨的关于简化现代数据栈和使用元数据的操作能力来适应未来的所述栈,元数据优先的三层框架提供了“如何”
框架的数据流层代表了从“数据”到“价值”的转变但这仅仅是一个开始。这些想法共同提供了一种以客观和抽象的方式思考数据的方法。
该层次结构清楚地表明,从“拥有数据”过渡到“从数据中获得高价值的洞察力”存在一系列依赖关系这就是数据驱动型公司的运作方式。这绝非偶然。他们通过它的步伐移动数据。这需要专业知识和实验。
透过数据需求层次的镜头来看数据,可以让任何数据团队对他们在层次中的位置、如何评估他们当前职位的优势和劣势以及如何进入下一步有一个有意义的了解。
如果你在寻找如何在实践中检验数据的例子,我们有两个故事可以分享。企业人才获取平台 Greenhouse 提升了数据层次,以便使用 ML 模型来改善他们的面试安排体验并解决他们的“点画意大利面条数据问题”零工经济初创公司 Jyve 利用他们的数据情报通知他们平台上的“Jyvers”并拓展新市场。这两个用例都展示了小数据洞察如何大有作为!
用一行 Python 代码从 PDF 文件中提取表格
原文:https://towardsdatascience.com/extract-tables-from-pdf-file-in-a-single-line-of-python-code-5b572cd9fbe5?source=collection_archive---------9-----------------------
如何使用 Camelot 从 pdf 提取表格并将其转换为 Pandas 数据框架

图片由皮克斯拜的梅根·雷扎辛拍摄
数据科学中的一个标准原则是,更多数据的存在会导致训练更好的模型。数据可以以任何格式呈现,数据收集和数据准备是模型开发流程的重要组成部分。任何案例研究所需的数据都可以以任何格式呈现,数据科学家的任务是将数据转换成所需的格式,以继续进行数据预处理和管道中的其他组件。
许多结构化/半结构化或非结构化数据可以在基于文本的 PDF 文档中以表格格式和图像格式呈现。开发定制的表提取模型需要大量的时间和精力。在本文中,我们将讨论如何使用开源库 Camelot,仅用一行 Python 代码从 PDF 文档中提取所有可用的表格。
卡梅洛特是什么?
Camelot 是一个开源的 Python 库,使开发人员能够从 PDF 文档中提取所有表格,并将其转换为 Pandas Dataframe 格式。提取的表格还可以以结构化形式导出,如 CSV、JSON、Excel 或其他格式,并可用于建模。
Camelot 有一个限制,因为它只能处理基于文本的 pdf,而不能处理扫描的文档。
卡梅洛特是如何运作的?
Camelot 使用两种表格解析技术,即 Stream 和 Lattice,从 PDF 文档中提取表格。人们可以在两种表解析技术之间进行选择。
流:
Stream 是一种解析技术,它使用 PDFMiner 的功能根据空白或边距将字符分组到单词或句子中。流解析技术就像一种基于猜测的技术。
晶格:
Lattice 是另一种解析技术,它不依赖于猜测,而是找到表格中的水平线和垂直线来解析 PDF 文档中的多个表格。Lattice 只能解析单元格之间有分界线的表格。在 PDF 文档中查找表格的格子算法步骤是:
- 使用 Ghostscript 将 PDF 文档转换为图像。
- 应用基于 OpenCV 的形态学变换来获得转换图像中表格的水平和垂直线段。
- 然后,通过对线段(从点 2 开始)和表格像素强度进行“与”运算来检测线交点。
- 通过取线段(从点 2 开始)和它们的像素强度的或来检测表格边界线。
- 通过使用线交点和线段来检测跨越单元或合并单元。
- 然后,由于图像和 PDF 中的尺寸可能不同,检测到的表格的线段和边界线被缩放并映射到 PDF 文档。
- 将表格的线段和边界线放置在适当的(x,y)坐标中后,在表格的像元上找到的单词将被检测并映射到数据框。
要可视化上述每个步骤,请遵循 Camelot 的高级使用文档页面。
安装:
可以使用以下命令从 PyPl 安装 Camelot 和 Ghostscript:
**!pip install "camelot-py[cv]"
!apt install python3-tk ghostscript**
安装后,可以使用以下方式导入 Camelot:
**import camelot**
用法:
导入必要的模块后,使用**camelot.read_pdf()** 功能读取 PDF 文件。
**tables = camelot.read_pdf('table.pdf')**
默认情况下,Camelot 只解析 pdf 文档的第一页,要解析文档多页中的表格,请使用**read_pdf** 函数中的**pages** 参数。
**# pass comma seperated page numbers or page ranges
tables = camelot.read_pdf('table.pdf', pages='1,2,3,5-7,8')**
Camelot 还可以从受密码保护的 PDF 文档中提取表格,只需绕过所需的密码。
**tables = camelot.read_pdf('table.pdf', password='*******')**
**camelot.read_pdf** 是唯一的一行 Python 代码,需要从 PDF 文件中提取所有表格。现在,所有的表都以 Tablelist 格式提取,并可以通过其索引进行访问。
**#Access the ith table as Pandas Data frame
tables[i].df**
要将表格导出为所需格式,可以使用**camelot.export()** 功能,并使用参数**f=’csv’**、**f=’excel’**、**f=’html’**或**f=’sqlite’**、、。
**tables.export('name.csv', f='csv')**
获取关于数据提取情况的解析报告或指标报告
**tables[i].parsing_report****# Output:**
{'accuracy': 99.27, 'order': 1, 'page': 1, 'whitespace': 13.89}
代码:
下图中使用的 PDF 文档是从表、表 1 下载的。
结论:
在本文中,我们讨论了如何从 PDF 文档中提取表格,并将其转换为 Pandas Dataframe,以便进一步用于建模。有各种开源库,包括 Tabula、pdftables、pdf-table-extract、pdfplumber,它们提供了与 Camelot 类似的功能。
卡梅洛特比它的替代品更好,阅读这篇文章来比较卡梅洛特和它的竞争对手的图书馆的结果。
参考资料:
1卡梅洛特文献:【https://camelot-py.readthedocs.io/en/master/
感谢您的阅读
提取新闻中的趋势故事
原文:https://towardsdatascience.com/extract-trending-stories-in-news-29f0e7d3316a?source=collection_archive---------16-----------------------
如何有效提取关键词,聚类海量新闻
如今,每天都有数百万篇新闻文章和博客在网上发布1。新闻数据以几年前无法想象的速度产生。社交媒体平台已经成为网上新闻的主要来源,以满足互联网用户的信息消费需求[2]。然而,也产生了大量重复、重复或垃圾内容的新闻。如何有效地对海量新闻内容进行预处理、组织和分析,是提供良好用户阅读体验的基石。

趋势报道摘录(作者图片)
因此,越来越需要基于文章内容的新闻自动分组,即内容驱动的分组。本文探讨了 NLP 技术,用于聚类在线新闻并提取随时间变化的趋势故事。通常,从大量新闻中获取热门新闻有几项任务。下图说明了数据处理管道(新闻搜索除外):

数据管道(图片由作者提供)
以下部分将详细解释所有这些任务和方法。这个解决方案的所有代码都可以在我的 GitHub 库中找到。
新闻来源和抓取
经常需要从互联网资源中收集数据用于文本分析。Scrapy 是一个流行的工具来建立网页抓取工具。有几篇关于使用 Scrapy 抓取新闻或相关数据的有趣文章[4,5]。然而,新闻数据抓取并不是这里的主要焦点。
本文使用来自 Kaggle 6的财经新闻标题数据集作为例子来说明新闻聚类和趋势故事提取。该数据集拥有 2018 年至 2020 年超过 3 万条新闻标题。每篇文章都包含标题、简短描述和出版时间。数据集中有三个新闻来源,即路透社、卫报和美国消费者新闻与商业频道。
新闻清洁
在线新闻通常包含许多不想要的文本、来自其他语言的单词、特定于提供商的模式等。如果不在管道的早期阶段清理这些文本特征,可能会对下游任务造成干扰。
文本清理通常是特定领域或特定问题的任务。这个数据集中的大部分文章都是用英语写的。非英语字符被简单地删除。
特定于提供程序的文本模式
新闻提供者可能在其文章中有一些模式。例如,该数据集中的 Reuters news 有许多文章都有跟随短语或实体的常见模式。
**-** Facebook’s Zuckerberg to testify before Congress: **source
-** McDonald’s accused of firing worker who sued over COVID-19 claims**: Bloomberg
-** Coty to appoint Chairman Peter Harf as its new CEO**: WSJ
-** Siemens prepares for COVID-19 trough to last 6–9 months**: CNBC**
如果那些模式短语没有被移除,它们可能被识别为文章的关键词,从而导致故事聚类的更多噪声。
在本文中,使用 Python 中的正则表达式清理了那些与提供者相关的模式。
关键词提取
关键词提取是流水线中的主要任务之一。目标是选择几个具有词频的关键词来反映文章的关键信息。关键词通常来自文章中的命名实体和名词短语。
Spacy 是一个开源 Python 库,支持大多数 NLP 应用程序。使用其预先训练的模型,它支持快速 NER(命名实体提取),包括大多数实体,如个人,组织,GPE(国家,城市,州)等。它还支持名词短语的提取。
本文使用 Spacy 作为基础工具来高效地提取实体和名词组块,而不是花费大量精力来训练深度学习模型。
为了提高关键字提取的性能,提供了以下模块:
关键词评分
考虑到关键词在新闻中的重要性,根据关键词类型(实体或名词组块)、关键词位置(标题或内容)和出现次数使用不同的权重。
出现在标题中的关键字被赋予比在内容中更大的权重。一个实体关键词的权重比同一位置的一个名词块要大。计算关键字分数的简单公式如下:

关键词过滤
由于机器学习模型并不完善,Spacy NLP 模型中也存在少量的误分类错误。关键词过滤的目的是去除不想要的单词、错误分类的实体和符号,例如停用词、日期时间、月份名称、介词、形容词、限定词、连词、标点符号、电子邮件、特殊符号# 、@、^ *等。
此外,与新闻提供者的姓名和写作模式相关的关键词也被移除,例如,路透社、汤森路透、美国消费者新闻与商业频道、来源、故事、路透社故事等。
关键词后处理
这一步的目标是将实体与其替代名称或缩写联系起来,以改进下一阶段的关键字相似性计算。有BLINK【7】等 Python 库,用维基百科数据验证提取的实体,用于通用应用。
对于这个 30k+的文章数据集,总共提取了大约 25 万个关键词。最受欢迎的关键词如下所示。

从数据集中提取的热门关键词
前 100 个关键词约占所有提取关键词的 24%,前 500 个约占 40%。为了简单起见,本文通过快速检查这些顶级关键字来构建一个实体链接表。
如图所示,以相同颜色突出显示的关键字被称为同一实体。随后,创建一个简单的查找表,将这些顶级关键字链接到它们的备选名称或缩写。
这种方法中的关键字提取运行得相当快。使用 2.5GHz CPU 和 8GB RAM 的 PC,完成所有 30k+新闻文章大约需要 50 分钟。平均来说,处理一篇文章需要不到 0.1s 的时间。
故事聚类
提取了所有文章的加权关键词后,下一步是将新闻聚类成故事。新闻故事聚类不同于传统的文本分类。对于文本分类,监督学习方法通常用于将文本文档分类到预定义的主题或类别列表中[8]。故事聚类是无监督学习[9]。此外,对于从大量新闻中聚类故事,通常没有固定数量的聚类[9,10]。k 均值聚类方法不适合此应用。
以下小节解释了这种方法中的故事聚类:
关键词矢量化
这一步是将文章的关键词转换成数字表示。本文中的矢量器将提取的关键词的分数视为术语频率。根据要聚类的新闻文章的数量,使用 Sklearn 库中的计数矢量器或哈希矢量器。哈希矢量器对于更大的数据集更有效。
基于关键词的新闻相似度
在对提取的关键词进行矢量化之后,计算余弦相似度来测量两篇文章的关键词之间的相似度。然后为要聚类的文章列表构建相似性矩阵。关键字矢量化和相似度计算的代码如下:
计算带有关键词的文章列表的余弦相似度
新闻聚类
如前所述,新闻聚类是无监督学习。对于聚集大量新闻,通常没有预先确定的故事数量。此外,根据读者的观点,从新闻中聚集的故事可能是主观的。
例如,下面列出的新闻文章都与贸易战有关。然而,如果我们仔细阅读,不同国家之间有贸易战的故事。甚至美中贸易战故事也可能有讨论不同事情或影响的“子故事”。
**-** Mnuchin says U.S. won't start a trade war: Brazil's Meirelles
**-** G20 talks on trade 'constructive,' no concern of trade war: Argentina
**-** U.S., Mexico resume talks to avert tariffs as deadline approaches
**-** Trade war, tariffs pose risks to U.S. and global growth: IMF, Fed officials
**-** EU readies new trade retaliation list before Trump visit
**-** USTR proposes $4 billion in potential additional tariffs over EU aircraft subsidies
**-** China announces new tariff waivers for some U.S. imports
**-** Trump prepares for 'productive' talks with Xi on trade war
**-** Factbox: Winners and losers in Trump's trade war with China
**-** China to slap additional tariffs on $16 billion of U.S. goods
**-** China rare earth prices soar on their potential role in trade war
**-** Oil prices slide as U.S.-China trade war escalates
读者可能对新闻中的故事有不同的看法。因此,在故事的数量上没有明确的答案。本文采用 DBSCAN 方法对新闻文章进行聚类。
参数 eps 是同一聚类中两篇新闻文章的最大关键词距离(1-关键词相似度)。当 eps 接近 0 时,聚类更具内聚性,但存在将相同的故事新闻聚类到不同的聚类中的风险。当 eps 为 1 时,所有新闻文章都属于同一个簇。这里, eps 初始设置为 0.35。
此外,还引入了集群的另一个参数 max_size 。因为有许多文章具有顶级关键词(例如美国、中国、特朗普等),所以那些文章可能形成超大规模的聚类。该算法将使用更小的 eps 在超级聚类中进一步聚类新闻,直到所有聚类大小都小于 max_size 。
这个聚类算法的代码可以在我的 git 仓库中找到。
趋势故事可视化
在新闻文章被聚类之后,趋势故事被直接提取出来。对于任何给定的日期,这里的趋势故事被定义为在过去的某个时间段(例如,过去的两周)发表了更多新闻文章的前几个聚类。
下图显示了从该数据集中提取的 2019 年 11 月至 2020 年 7 月之间的热门新闻。

从数据集中提取的趋势故事
在该图中,通过取集群中文章数量的对数来计算圆的大小。圆圈中的关键词是特定时间段内那些头条新闻的关键词。
结论和讨论
本文介绍了如何从海量新闻中提取热门新闻的 Python 解决方案。该解决方案涵盖了数据清理、关键词提取、新闻聚类和故事可视化。
虽然这个解决方案是针对这个金融新闻应用程序提出的,但是通过对数据清理、关键字过滤和实体链接表进行少量定制,不难扩展到其他领域数据集。
此外,关键字模块是使用 Spacy 库及其预训练模型开发的。这种解决方案更侧重于高效的关键词提取和新闻聚类,而不是需要大量训练工作的深度学习方法。
为了更好的实体链接、内容驱动的聚类等,可以在特定领域知识图的领域中进行进一步的改进。
参考
[2]:社交媒体如何改变了我们消费新闻的方式,https://www . Forbes . com/sites/Nicole Martin 1/2018/11/30/How-Social-Media-Has-Changed-How-We-Consume-News/?sh=6d403f743c3c
[4]:Python 中的网络抓取新闻文章,https://towards data science . com/we B- Scraping-news-articles-in-Python-9dd 605799558
[5]:使用 Scrapy 构建自己的数据集,https://towards data science . com/Using-Scrapy-to-Build-your-Own-Dataset-64 ea 2d 7d 4673
【7】:https://github.com/facebookresearch/BLINK
[8]:Python 中的文本分类,https://towardsdatascience . com/Text-Classification-in-Python-dd95d 264 c 802
[9]:塔里克·阿尔通库先生、索菲娅·亚拉基先生和毛里西奥·巴拉霍纳先生。2018.通过多尺度图划分对新闻文章进行内容驱动的无监督聚类。2018 年 KDD 数据科学、新闻与媒体会议录
[10]:使用层次聚类识别新闻报道中的隐藏趋势,https://towards data science . com/Identifying-hidden-trends-in-news-stories-using-hierarchical-clustering-b 6297 df 795 af
用 Java 从 Excel 和 Outlook 文件中提取数据
原文:https://towardsdatascience.com/extracting-data-from-excel-and-outlook-files-with-java-7979eb64edff?source=collection_archive---------16-----------------------
代码实现+用例&情境约束
过去一年多的冠状病毒爆发已经在行业和个人层面上扰乱了工作场所的运作。对我来说,在医疗保健行业工作意味着亲眼目睹普通医疗保健行业现存的裂缝。更具体地说,信息流的低效率&缺乏稳定的数据管理框架,再加上工作场所中技术专家的稀缺,最终形成了我最终实施的技术解决方案,以实现一些工作流的半自动化。

米卡·鲍梅斯特在 Unsplash 上的照片
对于本文的技术部分,我将分享我实现的 Java 代码片段,这些代码片段也可以在 GitHub 资源库中找到:用 Java 提取数据。样本输入数据和输出也出现在同一个 GitHub repo 中。
免责声明:本文中使用的所有数据都是虚拟数据,用于说明应用程序的功能。
首先,最迫切需要解决的问题是减少手动编译传入数据 Excel 文件和嵌入在日常 Outlook 电子邮件提要中的数据表的需要。因此,最终的技术实现在功能上有望使用户能够将嵌入在单独的 excel 和 Outlook 文件中的所需数据集提取到一个主 Excel 电子表格中。经过一番考虑,我决定用 Java 创建一个应用程序:

作者图片|用于读写 Microsoft Excel 文件的 Java 库列表|请注意,使用的 apache poi 的具体版本是 3.15 。不同版本的 apache poi 有不同的依赖版本。
作者代码片段|上面的代码片段利用 apache poi 库从 Microsoft Excel 数据文件中读取数据,提取数据并写入 CSV 文件,存储在为用户提供的单个 Zip 输出中

按作者分类的图片|用于解析 Microsoft Outlook 文件内容的 Java 库列表
作者代码片段|上面的代码片段利用 msg-parser Java 库从 Microsoft Outlook 数据文件中读取内容,提取内容作为 CSV 文件存储在单个 Zip 输出中供用户使用
最后用 Java 内置的图形用户界面(GUI)库 Java Swing 对应用进行编译打包。

作者图片|最终应用交付的预览,可通过 Java 的内置 GUI 库 Java Swing 与用户进行交互。图片展示了将 Outlook 文件提取为 CSV 格式— (1) 选择多个邮件文件; (2) 处理完 Outlook 文件后,应用程序提示用户将输出保存在 ZIP 存档中;并且 (3) 文件输出保存成功后自动打开。
为了整合应用程序生成的每个档案,创建了另一个单独的模块来读取用户输入的所有 Zip 文件(可以在ZipToExcel _(15-Aug-2021 _ 0234pm)找到最终输出示例)。xlsx
作者代码片段|存储在输入 Zip 文件中的每个 CSV 文件都经过处理并写入单个 excel 文件。请注意,每个 excel 选项卡都是根据每个 Zip 文件创建和命名的。

作者图片|将所有数据整合到单个 excel 文件输出的演示
仅供参考:如果有人想要上述应用程序的副本,请随意从同一个 GitHub 库 ☺ 中检索可运行的 JAR
⚠面临的情况限制概述
可以说,在这整个传奇中,我所面临的最大挑战是与业务用户的沟通,以及在前所未有的限制下工作。虽然 it 开发人员和业务用户之间的观点不一致并不是什么新鲜事,但我认为分享我不得不面对的真实世界的障碍仍然是值得的:
I .运行技术实现的有限平台— 虽然大多数 ETL 过程都是用 Python 实现的,与 Java 相比,Python 是轻量级的,并且具有大量以数据为中心的库,但这种情况只有在预先存在设置的情况下才是有利的。
不幸的是,目前许多公司(更不用说公共医疗保健公司)的情况并非如此,这种情况只适用于那些长期以来建立良好的技术文化并不断得到强化的工作场所。
考虑到最需要技术改进以提高工作效率的地方往往是不仅缺乏技术设施,而且许多工作人员对"技术解决方案"持不接受态度的地方,这是一个极大的讽刺;这反过来导致许多令人不快的连锁反应。客观地说,这些工人中的大多数肯定不缺乏才能或高度——对于公共医疗保健等行业来说,较短的周转时间和分配任务的紧急性质强化了他们更加规避风险的自然倾向,因此回到了他们传统的(通常是手工的)做事方法。
由于对最小设置和安装的严格要求,这导致了使用 Java 编程语言的最本能的选择,因为它的平台无关性。
二世。接受业务用户有限的技术相关知识 —虽然我最初并不打算为应用程序构建 GUI,但我很快意识到少数用户在运行以下命令来执行应用程序时遇到了问题:
java -jar <name of jar file>
按照任何其他编译的 Java 软件。令人惊讶的是, 40%的用户无法打开他们的 Windows CMD/Shell 终端/PowerShell 终端,这给了我很大的打击😨。因此,为了完全避免这个问题,我开始构建应用程序的界面,只需要双击鼠标🖱️就可以执行。
三。总体意见采用可持续的心态来实现持续改进— 通过与不同用户组的互动,任何实际进展的共同点毫无疑问是管理用户期望&迭代用户参与 (我知道,这是老生常谈)。更具体地说,业务流程的任何技术实现都应该分阶段进行,这是有充分理由的;用户工作方法的突然和剧烈变化往往会导致更大的阻力,因为用户会感到非常不熟悉。开发人员自然会就如何“把事情做得更好”进行头脑风暴,而另一方面,业务用户(至少从我遇到的一些情况来看)更倾向于“只要不会给我带来更多的工作,就很好”。尽管要不断提高公共医疗部门(或者说实际上所有部门)当前工作流程的效率,无疑是一条漫长而艰巨的道路,但一点点耐心确实能走很长的路。
非常感谢您的阅读,您可以在我的 GitHub 查看原始源代码和应用程序!❤
链接在:孵化器-geek-cc/data-extraction-with-Java:一个用 Java Swing 和其他 jar 库构建的 Java 应用程序,用于从 Outlook 和 Excel 文件中提取数据。(github.com)
https://geek-cc.medium.com/membership
从 URL 字符串中提取特征向量用于恶意 URL 检测
原文:https://towardsdatascience.com/extracting-feature-vectors-from-url-strings-for-malicious-url-detection-cbafc24737a?source=collection_archive---------5-----------------------
介绍
在网上传播的网络攻击中,恶意网址是相当大比例的攻击媒介。恶意 URL 要么是指向恶意代码静默下载的链接,要么是指向恶意网页、钓鱼网站等的链接。这种 URL 已经成为一种流行的在线危害主机的方式,从而创建了大规模的僵尸网络。例如,在驾车下载中,攻击者将恶意 javascript 嵌入到网页中,当用户访问某个页面时就会执行该脚本。它试图危害用户系统上的浏览器或用户插件。要构建主动模型来识别此类恶意 URL,分析师需要能够使用某些功能。这些特征将从简单的 URL 字符串中提取出来。因此,给定一个简单的 URL 字符串,我们如何提取有用的特征向量来建立预测?
在本文中,我将演示如何从 URL 字符串中提取深入的词汇特征、基于主机的特征和基于内容的特征。这些特征包括从 Shodan、WHOIS、Wayback machine 中提取的特征,从网站的原始 HTML 内容中提取的统计特征,以及 URL 字符串本身的静态特征。本文中提取的特征分为三大类:基于内容的特征、基于主机的特征和词汇特征。本文策划并实现了这些特征作为 URL 特征向量的提取。这些 X 特征可以在恶意 URL 归属问题中用作特征向量,为恶意 URL 检测建立预测模型,或者为日志流中的不良主机建立简单的快速过滤器。
数据
我将使用的数据来自加拿大网络安全研究所。有一个 2016 URL 数据集,根据攻击类型对 URL 进行分类。你可以在他们的网站上下载这些数据,对于这个项目,我们将在这篇文章中使用 11,500 个恶意网址和 35,300 个良性网址。恶意软件 URL 与从 DNS-BH 获得的恶意软件网站相关。因此,这些网址不包含钓鱼网址或恶意嵌入式下载链接,而是指向包含恶意代码或软件的恶意网站的链接。良性网址是从 Alexa 顶级网站收集的。一旦你下载了数据,你应该有两个文件'恶意软件 _ 数据集. csv '和'良性 _ 列表 _ 大 _ 最终. csv '。在我的代码中,我将这些文件重命名为“malware.csv”和“良性. csv”。
URL 功能
从 URL 中提取的特征是确定 URL 是否是恶意的基础。任何学习努力的成功都取决于训练数据的质量,因此也取决于输入模型的特征的质量。我们必须确保提取的特征代表或潜在地表明我们试图建模的问题。这里,我们的意思是所收集的特征的变化必须代表问题的发生或不存在。因此,问题是,URL 字符串的哪些特征可以告诉我们它是恶意的还是非恶意的,或者它是良性的还是非良性的?从 URL 字符串中提取要素有三种信息来源。
- 词法特征:这些是指从文字 URL 字符串中提取的统计特征。例如,URL 字符串的长度、位数、其查询部分中的参数数量、URL 是否被编码等。
- 基于主机的特征:这些是 URL 的主机名属性的特征。这些提供关于网页主机的信息,例如,注册国家、域名属性、开放端口、命名服务器、连接速度、从注册开始的生存时间等。
- 内容特征:从下载的网页 HTML 代码中获取。这些特征捕捉网页的结构和嵌入其中的内容。这些信息包括脚本标签、嵌入对象、可执行文件、隐藏元素等。
我将解释从这些信息源中提取的不同特征,并证明为什么它们与识别恶意或良性 URL 相关。我还执行了一些基本的统计 t 测试,以测试恶意和良性之间这些特征的差异的显著性,从而识别每个类别中的关键区别特征。
词汇特征
词法特征从 URL 字符串中获得。包含词汇特征的动机是基于一个简单的事实,即恶意 URL 看起来不同于良性 URL,因此我们可以提取统计属性来量化这些外观的差异。举个简单的例子,URL“Amazon . com”中“. com”的存在和位置看起来并不可疑,但是,相同的子字符串在“amazon.com.support.info”中看起来是恶意的。另一个经典的例子是黑客常用的策略是 混淆 一个恶意的网址看起来像一个合法的网址。他们通过模仿流行品牌的名字并对其稍加改动来做到这一点(googleinfo.com,apple-support.com)。检测网络钓鱼网址的不同项目一致认为,不同类型的网址表现出不同的字母分布。这里的“字母分布”指的是 URL 字符串中字符的顺序、字符的复杂性等。这些是一些可能被提取的词汇特征。我仅从 URL 字符串中提取了 18 个词汇特征。

词汇特征表
URL 的方案很重要,因为这表明网页上配置了何种安全级别(HTTP/HTTPS),服务器的用途是什么(显示信息(HTTP),传输文件(FTP),通过网站收集的信息是否加密(HTTPS)等)。显示为 IP 地址的主机或域名通常意味着主机没有解析为域名,或者没有正确配置为指向域名。
最常用的词汇特征包括 URL 字符串的一些上述统计属性,如 URL 字符串的长度、模式、路径长度、子目录等。然而,一些不太明显的问题,如在 URL 和 URL 编码中出现某些关键字,也值得包括在内。众所周知,DGA 域名的字母分布比合法域名更加混乱见。合法的域名往往有定义明确的名称,说明一个品牌或产品,所以倾向于不那么杂乱无章。因此,测量 URL 字符串的熵可以告诉我们哪些域名“不太真实”。
这也适用于作为 IP 地址的域名,例如“42 . 50 . 23 . 80/子目录. com”。例如,当恶意网站托管在受损公共网络中的受害者机器上时。其他词汇特征,如‘的存在。com '作为域扩展名,表示。URL 字符串中的“com”。在下表中,我列出了样本中恶意和良性 URL 之间的显著不同的特征。

词汇特征的箱线图
目测,我们看到良性网址的熵更高,一般比恶意网址长。此外,所有的网址中至少有一个句点(如预期的那样),但恶意网址中平均至少有两个句点。类似地,我们可以捕捉某些“红旗”关键字出现在 URL 字符串中的事实。这些关键字可能涉及攻击者在试图欺骗合法页面时使用的关键字,或者涉及黑客将试图操纵的网站上的安全设置的流行术语的关键字。例如,URL 中的关键字“admin”通常表示站点管理用户的身份验证页面。

红色标记关键字的条形图
在我们的示例中,只有恶意 URL 将 IP 作为主机名,并将端口号作为 URL 字符串。正如前面所解释的,恶意 URL 中绝大多数都包含子字符串“admin”。我们还可以探索恶意和良性网址的“域扩展”或“TLD”矩阵。给定一个逻辑矩阵,其中 0 和 1 表示 URL 中是否存在 TLD,我们可以估计良性和恶意文件之间每个 TLD 的平均差异,并返回两组之间有显著差异的文件。我们可以创建一个 TLD 矩阵,并从两组之间的独立 t 检验中提取差异和 p 值。
现在,我们可以排除 P 值小于 0.05 的 TLD(在 95%的置信水平下测试),并绘制恶意和良性 URL 之间 TLD 的排序平均差异和 P 值。我们用最高平均差和最低(最高)显著 P 值作图。

词汇特征平均差异的显著性(恶意——良性)
每个条形的宽度是 Y 轴上恶意和良性 TLD 之间的平均差异。条形按其显著差异的降序排列(因此最小的 P 值是第一个条形)。顶级域名如。信息,。茹,还有。cn 更像是恶意网址的同义词,而良性网址则更容易链接到。com 和。net 域。

基于词汇特征的互相关图
恶意网址长度一般比良性网址 短。相关图显示,虽然良性 URL 的熵可能比恶意字符串略高,但恶意 URL 字符串的长度比良性字符串短。良性字符串也记录更多的数字计数和更多的编码字符。这是可以预期的,尤其是大多数良性网站流行的某些博客或文章命名约定,例如,'【https://blogname.com/article-name/YYYY-MM-DD】'或'' https://Hollywood life . com/2015/05/13/Justin-Bieber-perfect-girl-someone-pusses-spiritically/'。最后,运行'get _ lexical _ features . py'(<<在此获取完整代码)
基于内容的功能
基于内容的特征是从原始内容(即网页的 HTML 和 javascript 内容)中提取的统计信息。这些包括关于页面的原始编码内容的信息,例如空白百分比、页面上的标记数量、脚本数量、脚本中的字符数量,以及 HTML 代码的结构信息,例如某些标签的位置和存在。包含这些基于内容的功能背后的基本原理是捕获在受损页面中发现的页面内容特征,例如,存在注入的内容或被设计为包含恶意代码的页面,例如,存在某些脚本标签或可疑的 HTML 元素。 Prophiler 项目寻找攻击副作用的存在,例如注入页面上不合适的标签。例如,在 SQL 注入攻击中,格式错误的文档或重复的标签等异常情况会出现在原始 HTML 内容中。
这组特征提取的目标是寻找页面上的“可疑”内容。可疑对象可能包括 HTML 标签之间存在纯文本格式的外壳代码,存在双“”、“”和“

基于内容的功能表
如前所述,我们对以下基于内容的特征的均值差异进行了简单的独立 t 检验。

基于宿主特征的均值差异的显著性
良性网站的内容比恶意网站多。 一般来说,我们观察到良性网站的内容总体上比恶意网站多。虽然这对人类来说似乎是显而易见的,但最终拥有量化这一想法的功能还是不错的。最突出的是网页上的独特单词数和恶意网站中每句话的平均单词数。恶意网站也倾向于具有较低数量的 HTML 标签和较大的页面熵。良性站点通常有更多的脚本标记,而恶意站点的区别在于脚本标记比实际内容多。
此外,较高的脚本与正文比率也表明恶意脚本也比良性脚本更加详细或“健壮”。这些脚本观察的主要原因是良性网站上的大多数脚本倾向于外部链接(这仍然算作脚本标签,但不测量熵,因为原始 HTML 内容是空的),而不是写入或注入的恶意或受损网站。然而,良性网站的平均脚本长度仍然比恶意网站略高,因此我们不能完全忽视恶意网站的大部分内容都在脚本标签中这一事实。最后,你可以在这里下载完整的“get _ content _ features . py”。
基于主机的功能
有时有必要超越对 URL 字符串的视觉检查来识别恶意链接传播背后的参与者或参与者组。检查基于主机的功能可以将网络钓鱼 URL 或下载驱动(例如)归因于网站或链接所有者。基于主机的功能从 URL 的主机名属性中获得。这组功能允许我们抓住“谁”、“哪里”、“何时”和“网站如何托管”的某些特征。包含这些参数背后的动机是网站部署策略、存在时间以及恶意和良性网站的声誉存在差异。一个典型的例子是恶意网站隐藏注册信息,网站更新的平均间隔天数,使用同一个主机 IP 创建大量恶意链接,或注册域名和托管网站之间的天数。众所周知,攻击者会利用僵尸网络在多个国家的多台机器上运行。
为了提取有意义的基于主机的特性,我们需要用 python 编写三个库
- 与域名注册信息 (python 的 whois 库)进行交互。这将为我们提供与主机 IP 相关的域名信息。比如什么时候注册的,谁注册的,注册什么时候到期等等。 注意 这是一项免费服务,选择匿名的注册人的信息将保持不变。其他服务在注册服务商决定私有化之前提供关于注册服务商的可靠付费信息。“Domaintools.com”可能提供了关于主机的最可靠的 whois 信息。
- 访问网络上主机的信息 (python 的 shodan 的 API) 。这将为我们提供有关主机的信息,即网站所在的机器(如果可用)。Shodan 提供了一个开放的 API 来访问诸如开放端口、操作系统、web 技术等信息。
- 访问关于网站变化的信息 (python 的 waybackpy 库)。Wayback Machine 是万维网的数字档案。对于任何一个网站,Wayback 机器都在线记录了其存在的所有版本。因此,我们可以使用它来获取有关网站更改的信息,更改频率,自其存在以来的更改总数,第一次更改,最后一次更改等。
以下是提取的 21 个基于主机的特征。

基于主机的功能表
注意: 这些是特定于域的结果,所以我们不需要为每个 URL 运行代码,而是为每个唯一的主机运行代码。不管 URL 字符串上的子域、查询和参数如何,结果都不会改变。“缓存”列表将存储已处理主机的列表,此代码的输出将是 URL 文件中每个唯一主机的记录。
众所周知,当使用相同的漏洞利用工具包设计不同的攻击时,多组恶意行为者共享相同的主机名或在其路径名中具有相似的重复模式。此外,当恶意代码被托管在受害者计算机上时,在受损的公共网络上,网站通常不会与任何域名相关联,而是由 IP 地址引用。域名注册的“生存时间”检查从域名注册(从 whois 数据)到相关网页第一次在网上出现(Wayback 机器)之间的时间。有实验[ref]表明,生存时间较短的域将来很可能会迁移到其他 IP 地址或域。因此,由于恶意网址的生命周期比良性网址短,它们在整个生命周期中的网站更新次数也比良性网址少。
在这里,我们还可以验证恶意和良性 URL 之间的 TTL、网站更新频率和预期寿命是否存在差异。为了证实这种差异,我对每个特征进行了独立的学生 T 检验,比较了两组的平均值。

基于主机的特征的平均差异的显著性(恶意-良性)
与前面的概念相同,在我们的示例中,恶意主机比良性主机有更长的寿命。虽然知名品牌的良性主机(如 Alexa 顶级网站)比大多数恶意主机的寿命更长,但恶意主机在野生环境中的平均存在时间更长,但在其生命周期内站点更新更少。此外,恶意行为者在购买域名后,还需要更长的时间来托管网站,以域名托管生存时间来衡量。

TTL 和连接速度(恶意与良性)
如果我们忽略下图中的主要 ISP 提供商,如 Amazon、Google 和 Cloudflare,我们可以看到有相当多的恶意主机 ISP 提供商没有出现在良性顶部。此外,请注意 ISP 提供商获得良性和恶意信息的频率差异。良性主机泄露注册信息的可能性是恶意主机的 3 倍。

ISP(恶意与良性)
当考虑与恶意 URL 相关联的顶级域时,恶意行为者的 ISP 看起来也可以链接到特定的国家行为者。最后,在这里下载完整的‘get _ host _ features . py’。
总之,恶意 URL 比良性 URL 更长,熵更低。恶意主机比良性主机注册的时间更长,但在其存在期间更新更少。良性 URL 也比良性 URL 有更长注册时间。本文展示了我们如何从 URL 字符串中提取有用的特征向量,还展示了恶意和良性主机和 URL 字符串之间的一些比较。在以后的文章中,我将介绍恶意、网络钓鱼和篡改 URL 属性,以从恶意主机中提取基于主机和基于内容的特征,并开发一种集成方法(Whois 链接、状态属性和基本的无监督学习)来确定恶意 URL 在线分布背后的属性组。
参考
- 《Prophiler:大规模检测恶意网页的快速过滤器》第 20 届万维网国际会议论文集。2011.
从非结构化来源中提取信息
原文:https://towardsdatascience.com/extracting-information-from-unstructured-sources-b9d8bd14e70a?source=collection_archive---------44-----------------------

实践教程
使用 Web-Search、spaCy 和 RegEx 搜索相关的组织地址
问题陈述
最近,我们的一个客户向我们提出了一个有趣的问题。这是一家投资公司,需要计算投资对象的潜在风险。他们需要一个解决方案来帮助生成与特定公司名称相关联的所有地址(或 GPS 坐标)的列表。我们的客户面临的问题是,许多公司可能有许多不同的细分市场,用不同的语言使用不同的名称。另外,很多公司在世界各地都有部门。此外,该公司可能有不同的隐藏位置,与他们相关联,这并没有直接在他们的法律文件。
我们从一个小的概念验证开始,检查是否有一种方法可以解决一个或几个这样的问题。结果必须以地理位置图的形式呈现,并描述与其他顶点的关系以及给定的公司名称。
有趣的挑战是,我们没有得到任何数据或方向。所以我们关注的第一个问题是数据来源。经过短暂的研究,我们将所有可用的相关数据源分为三大类:
- 公共或私人登记处
- 社交网络
- 搜索引擎
考虑到每个数据源的缺点,我们决定为我们的 PoC 使用最后一个类别,因为注册中心和社交网络很难交互。除了难以查找之外,注册中心还提供各种格式的数据。这使得短时间的概念验证很难使用其中的几个。
我们最终选择的方法并不容易成功。由于种种原因,我们不得不尝试一些行不通的方法。我们将简要描述我们做了什么,以及如何指出最有希望的结果和失败的原因。
第一种方法
我们解决这个问题的主要思路是基于使用 空间 。这个框架有一个内置的能力来标记文本中的组织和位置,我们发现这个能力对这个问题非常有用。一旦我们选择了数据源,我们就开始分析与一些随机选择的组织相关的网页。在用 spaCy 标记了一些网页文本后,我们发现了一些模式,这些模式大多出现在涉及公司地址的时候:

考虑到这种模式,我们开始着手以下想法:

我们在网上搜索一个看起来像“
上面描述的实现过程是一个简单的 刺儿头 网络爬虫。在收到一个初始链接列表(深度为 1 的页面)后,爬虫会寻找一个模式,收集链接并继续处理其他页面,将所有结果保存在一个 CSV 文件中。为了检查这种模式,我们使用了逐个标签的滑动窗口方法,这简单地意味着计算适合滑动窗口的特定标签或数字标记的数量。
尽管这种方法很简单,但由于许多原因,它很快就失败了。首先,即使是最初的几个链接(比如 100 个),链接到它们的网页数量也是以千兆字节计算的。这一过程花费了太多时间,产生的有用文本非常少。第二,有许多文本既不包含组织的名称,也不包含其地址,但仍然符合我们的模式过滤器。这主要是因为许多与地址相关的单词被 spaCy 标记为组织或个人,或者根本没有被标记。
我们决定分析收集到的数据,并给它们贴上标签。只有大约 20%的收集文本包含组织名称和地址。另外 80%有很多额外的文本,缺少组织名称或地址中有意义的部分。这种数据很难处理。然而,在分析这些数据时,我们发现 spaCy 与组织名称本身配合得很好。但是我们的模式忽略了大多数这样的事件。我们还发现,几乎每个国家都有不同的地址模板。尽管我们为不同的语言使用了相应的 spaCy 模型,但这样一个简单的模式甚至不能覆盖几个地址模板。
这使我们得出这样的结论:这种方法对于给定的问题并不奏效。
第二种方法
考虑到所有这些信息,我们决定简化我们的 PoC,只关注美国的组织和英文页面。另外,我们决定将问题分成两部分:寻找相关组织和提取单个组织的地址。
为了解决第一个问题,我们决定尝试这样做:如果某个组织在与某个给定公司相关的网页上被提及,那么这个组织以某种方式与该公司相关联。暂时,我们会忽略一个组织与一个公司(对手、子公司、同一个基金会团体等)到底是如何联系的问题。)因为这似乎是一个更复杂的问题。为了加强观察到的关系,我们假设,在一些公司的网页上也必须提到该公司。
提取潜在的组织名称
我们开始使用网络搜索和空间对相关组织进行简单搜索。我们使用了 Bing,但是,你可以使用任何其他可用的搜索引擎。我们将向您展示我们用来收集数据的 Python 代码片段。请注意,我们的代码并不追求优雅,因为它是为 PoC 编写的,当然,可以进行增强或优化。
首先,我们搜索了一个类似这样的术语:“
companies_search = bing_search(“{} companies”.format(COMPANY_NAME))
这就是我们如何收到 10 个搜索结果,足够开始。然后,我们提取每个相应的网页,用空间标记其文本(我们不需要标记 HTML 标签、脚本、属性等等),并且只提取那些被命名为 ORG 的网页。
正如我们已经提到的,我们还通过检查一个给定的公司是否在与找到的命名实体相关的页面上被提到来加强观察关系的能力。为此,我们对每个找到的命名实体进行了搜索。
然后,我们对所有收集的数据进行预处理,并将其保存到 csv 文件中以备将来使用:
现在,为了检查给定的公司是否与已发现的命名实体相关,我们实现了一个 scrapy 爬虫。它使用一个 URL 列表和一个公司的名称来处理每个 URL,并计算某个公司的所有提及次数。请找到它的主要方法,而不是整个初始化,它主要处理文件基础结构:
我们简化了代码,删除了一些需要的 try … ,除了语句或其他非常具体的代码部分。此外,计算提及次数的决定似乎并不完全正确:当给定的公司名称由两个或更多单词组成时,在一些(不是所有)复杂的情况下,这一决定会失败。更合适的方法是使用 spaCy tokenizer 并比较标记。
一旦爬虫完成了工作,我们就得到一个 CSV 文件,其中只包含那些潜在组织的 id,这些组织与给定的公司有着密切的关系。三星的近 1000 个命名实体的列表减少到只有近 600 个。剩下的看起来是这样的:

乍一看,还不错。但是,如果我们仔细看看这个列表,我们会发现很多命名实体根本不是组织名称。例如,三星年度报告也许只是某个文本的标题,而不是一个组织名称。也没有什么可指责的:所有这些命名的实体看起来确实像组织。
我们手工标记收集的数据。我们看到的是,这种方法给出了相当好的回忆。事实上,对于三星公司,我们的名单包含了近 80 个不同的分支机构,法律相关组织以及他们在不同领域的对手。然而,它的准确率很低:近 60%的命名实体不是有效的组织名称(也不是完整组织名称的一部分)
所以我们需要某种模型来过滤掉无效的组织名称。我们采用的方法有点复杂,太大了,无法在这里描述。我们就这个话题单独写了一篇文章。
此外,我们试图建立一个模型,根据组织与给定公司的关系对组织进行分类。该模型应该是基于特定词语和某个公司以及一些命名实体的被提及次数之间的相关性。然而,这种方法并不奏效。
地址提取
使用上述模型,我们过滤掉了不是组织名称的内容。现在我们有了一个与给定公司相关的组织列表,我们需要它们的地址。为了实现这个目标,我们使用类似于 " <组织名称>地址美国"的搜索词对每个组织进行了搜索。它给了我们一个可能包含相关组织美国地址的网页列表。
一般来说,从 HTML 页面中提取地址是相当复杂的。该过程必须包括寻找不同国家通用的不同模式,处理预定义单词列表(如国家名称、州、地区等。),以及理解哪段文字是地址。当您需要提取一个用不同语言书写的地址,或者一个以表格形式显示在网页上的地址时,这就更加困难了,表格中有类似于街道、房屋号码、州、等行。
至于 PoC,我们决定关注最简单的情况——美国地址写在一个标签中。为此,我们构建了一个简单的正则表达式,它考虑了 spaCy 的命名实体、数字、州,或许还有国家的顺序。这里有趣的一点是,我们需要将空间的 organization (ORG)标签包含在其中,因为地址的很多部分都带有这个标签。此外,我们需要为美国各州建立一个自定义的正则表达式。尽管事实上大多数州都有一个合适的上下文,但它们仍然很少被标记为:
这里的 address _ re:n—是一个小数字,后面跟着最多 4 个 spaCy ORG、LOC、FAC 或 GPE 标签((l|f|g|o){0,4}),然后是 state(s)和 zip(z),也许还有 GPE 标签(g?),最有可能是按国家。
我们通过以下方式从该文本中提取地址:
上面所有的代码都是我们用来收集地址的。同样,它的主要方法如下所示:
有了这个,我们通过每个搜索结果,并提取组织的地址。当然,这个解决方案并不完美。许多提取的文本根本不是地址,或者包含大量额外的文本。然而,与提取的数据量相比,这种情况相对较少。所有这些操作产生了如下所示的列表:

我们使用 geopy Python 包来获取地址的 GPS 坐标。
请注意,当您按顺序执行多个请求时,这些地理编码器不能很好地工作。这就是为什么我们添加了随机延迟,只是为了好玩:-)。
现在,在可视化了所有收集的数据之后,我们收到了一张地图,上面标明了与某个给定公司相关的组织的具体位置。

结论和进一步措施
以下是具有最高优先级的后续步骤列表:
- 建立一个模型来分类两个公司之间的关系类型。或者至少在一个给定的公司和一个创建的公司之间。这可以通过多种方式实现,从词频分析开始,就像上面提到的模型,到复杂的 ML 系统结束,这取决于所需的结果和问题的复杂性。
- 增强地址提取。这将包括预定义单词的字典、不同国家的格式和不同的语言标记系统。
- 对现在与提取的地址相关的组织的分析,因为通常有几个相关的公司在同一地址注册或位于同一地址。
- 加强现有方法。很多事情都是以最简单的方式完成的。许多代码可以改进,以获得更准确或更恰当的数据。
也许,中国、日本、韩国和其他国家需要一些特定的语言系统或翻译。
总而言之,我们现在有数据来构建与给定公司相关的实体的双层图。图的第一层是组织层,与给定的公司有某种关系。它可以包括不同类型的子公司、对手、合法关联的公司等。第二层是这些组织的地址层。这个图表可以用来建立一个通用的公司简介,计算投资风险,公司股票价格,等等。
感谢
我要感谢我的同事安迪·博西、米科拉·科兹连科、沃洛季米尔·森德茨基、维亚奇·博西和纳扎尔·萨维琴科富有成效的讨论、合作和有益的建议,以及整个 MindCraft.ai 团队的持续支持。
亚历克斯·辛基夫,
数据科学家,MindCraft.ai
信息技术和数据科学
将 XML 文件中的信息提取到熊猫数据框架中
原文:https://towardsdatascience.com/extracting-information-from-xml-files-into-a-pandas-dataframe-11f32883ce45?source=collection_archive---------3-----------------------
实践教程
用 Python 的 ElementTree 包解析 XML 文件

【www.freepik.com】故事创建的网站载体
现实世界的数据是杂乱的,我们知道这一点。不仅这些数据需要大量的清理,而且我们接收数据的格式也不适合分析。这意味着,在分析开始之前,数据必须经过一系列转换,才能转换成合适的格式——一种易于处理的格式。这种情况大多发生在数据是从网上搜集的或是以文档的形式提供的时候。我遇到了一个非常相似的数据集,它是各种 XML 文件的形式。在本文中,我列出了我破译这些文件并将其转换为可供分析的 CSV 文件的步骤,该文件足够好,可以被摄取到 pandas 的库中以供进一步分析。
T 他的文章是寻找好数据集的完整系列的一部分。以下是该系列中包含的所有文章:
第 1 部分 : 为数据分析任务获取数据集——高级谷歌搜索
第 2 部分 : 为数据分析任务寻找数据集的有用站点
第三部分 : 为深度学习项目创建定制图像数据集
第 4 部分 : 毫不费力地将 HTML 表格导入 Google Sheets
第 5 部分 : 使用 Camelot,从 pdf 中提取表格数据变得很容易。
第六部分 : 从 XML 文件中提取信息到熊猫数据框架中
第 7 部分 : 5 个真实世界的数据集,用于磨练您的探索性数据分析技能
动机
全世界都在集体打新冠肺炎战争,2021 年带来了一线希望。我们都知道许多国家正在开展疫苗接种运动,包括我自己的国家印度,它于 2021 年 1 月 16 日开始了世界上最大规模的疫苗接种运动。但是,疫苗在经过几个阶段和试验后才形成最终形式,只有在它被认为合适后,它才会被授予绿色标志,以供普通人群使用。
我发现了 ClinicalTrials.gov 的https://www.clinicaltrials.gov/ct2/home,这是一个在世界各地进行的私人和公共资助的临床研究的数据库。我认为在网站上查看所有与 COVID 19 研究相关的临床试验将会非常有趣和有益。

来源:https://www.clinicaltrials.gov/ct2/home
然而,试验的数据集由 XML 文件组成,每个 XML 文件对应一项研究。因此,不可能立即进行分析。数据需要首先转换成一种可以读入熊猫数据帧的格式。我已经下载了数据集,并上传到** Kaggle 供其他人查看。这些数据每周更新,为 COVID 疫苗和药物的开发提供了一些很好的见解。**
**
来源:https://www . ka ggle . com/parulpandey/covid 19-临床试验-数据集 |图片由作者提供**
本文的其余部分将处理用 Python 解析这些 XML 文件。但是在此之前,让我们更深入地研究一下 XML 文件及其组成部分。
什么是 XML?

作者图片
XML 代表可扩展标记语言。顾名思义,它是一种 标记语言,通过定义一套机器可读和人类可读格式的规则对文档进行编码。该语言定义了一组规则,用于以特定格式对文档进行编码。让我们看看下面的 XML 文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<class>
<student>
<name>Negan</name>
<age>40</age>
<email>[imnegan@abc.com](mailto:imnegan@abc.com)</email>
<address>
<country>US</country>
</address>
</student>
</class>
每个 XML 文件都有一个树形结构,其中顶部的元素是根元素。然后,子元素被连接到根元素。如果直观地呈现,上面的 XML 文档的树结构将如下所示:

作者图片
- XML 版本 = "1.0 "编码= "UTF-8 "?:
<XML version and the character encoding> - 根元素 :
<Class> **Student**的子元素元素 :<name>,<age>,<email>&<address>- 子元素 :
<country>
ElementTree XML API
现在让我们看看如何解析给定的 XML 文件,并以结构化的方式提取其数据。在本文中,我们将研究[**ElementTree**](https://docs.python.org/3.10/library/xml.etree.elementtree.html#module-xml.etree.ElementTree)一个内置的 Python 库来操作 XML 文件。我们已经了解了数据集。以下是它在 Kaggle 上的一瞥:

来自数据集|作者图像的单个 XML 文件
数据集由几千个 XML 文件组成。每个 XML 文件对应一项研究。文件名是 NCT 号,它是 ClinicalTrials 存储库中一项调查的唯一标识符。
解析 XML 文件
让我们首先导入必要的库,并查看数据集中的文件数量。
**import xml.etree.ElementTree as ETpath = '../input/covid19-clinical-trials-dataset/COVID-19 CLinical trials studies/'files = os.listdir(path)
print(len(files))------------------------------------------------
4663**
数据集中有 4663 个单独的文件。上面的数字对应于数据中 XML 文件的数量,包括临床试验报告的数量。现在让我们看看第一个 XML 文件并打印出值,以理解树的结构。
**# Reading the first filefile_path_file1 = os.path.join(path, list_of_files[0])
tree = ElementTree.parse(file_path_file1)
root = tree.getroot()
print(root.tag, root.attrib)
--------------------------------------------------------clinical_study {'rank': '4406'}**
ElementTree 将整个 XML 文档表示为一棵树,而元素表示这棵树中的一个节点。树的每个部分(包括根)都有一个描述元素的标签。此外,元素还可能包含属性,顾名思义,这些属性是附加的描述符。如果您仔细观察上面提供的 XML 示例,就会发现— 临床研究是根元素,并且有一个等于 4406 的等级属性。****
我们现在可以通过使用一个简单的 for 循环来查看一些其他的子元素。
**for child in root:
print(child.tag, child.attrib)**
所有上述内容似乎是报告的不同部分。还可以通过下面的代码查看整个文档以及树中的所有属性或级别。
**print(ElementTree.tostring(root, encoding='utf8').decode('utf8'))**
然后,我们可以选择在我们的格式化数据中包含所有元素或选择一些元素。这些元素将在数据帧中显示为单独的列。我们现在将初始化两个空数据框,它们将被上述元素填充。我们将在数据集中包括id,overall_status, study_type, start_date, enrollment, condition, location_countries, intervention, title, date_processed 和sponsors。
这给了我们一个格式良好的数据帧,就像我们习惯看到的那样。您也可以将其保存为 CSV 文件,以供将来参考。
**df.head()
df_covid.to_csv('COVID_trials.csv')**

作者处理的数据帧|图像
下一步是什么
现在,您已经有了所需格式的数据,您可以对其进行分析以得出特定的细节,如:
- 这些研究的总体状况如何
- 不同的研究类型
- 审判的时间表
- 参加各种研究的人数,以及更多类似的问题。
您还可以使用自己选择的工具创建一个仪表板,以便一次查看所有细节。我在上面的例子中只提取了很少的属性,但是在报告中有很多。继续对其他人进行实验,以了解更多关于数据的信息。就像提到的那样。 数据集 可供公众使用,有待进一步探索。
参考
- 新冠肺炎临床试验数据集:全球正在进行的新冠肺炎相关临床研究数据库
通过 Gmail API 从媒体每日文摘时事通讯中提取元数据
原文:https://towardsdatascience.com/extracting-metadata-from-medium-daily-digest-newsletters-via-gmail-api-97eee890a439?source=collection_archive---------11-----------------------
消化摘要:用主题建模对我的兴趣进行逆向工程(第 1 部分)

作者图片
我实话实说。我着迷于媒体的推荐系统经常比我自己更了解我。他们推荐的阅读材料是如此的诱人,以至于我希望每天早上醒来都能狼吞虎咽地阅读 10-11 篇完全符合我兴趣的文章。也就是说,生活会碍事。虽然承认这一点很尴尬,但在上班(从家里)之前,我几乎没有时间刷牙和穿上工作服。通常情况下,我会将这些摘要邮件标记为未读,直到清理收件箱的时候,这时我会将一些听起来最有趣的邮件添加到我的中等阅读列表中。
我突然意识到,因为 Medium 非常了解我的兴趣,如果我着手一个项目,利用这些时事通讯作为数据来了解更多关于我的兴趣以及它们是如何随着时间的推移而演变的呢?
这个项目和我的大多数数据科学项目一样,将包括数据挖掘、数据清理、探索性数据分析和可视化的元素。然而,在我的下一篇文章中,我将通过与 LDA 和 BERTopic 讨论主题建模,将事情推向一个新的高度,敬请关注!在本文中,我们将重点讨论如何利用 Gmail API 来解析 Medium Daily Digest 时事通讯或任何其他格式一致的时事通讯。

斯蒂芬·菲利普斯-Hostreviews.co.uk 在 Unsplash 上的照片
工作区开发人员入门和启用 Gmail API
Gmail API 是一个 RESTful API,可用于访问 Gmail 邮箱,甚至发送邮件。它适用于只读邮件提取(即我们将要做的事情)、自动化或程序化的邮件发送、电子邮件帐户迁移以及邮件的组织过滤和排序。为了使用此 API 对您的 Gmail 数据进行授权访问,我们必须获得对 API 的访问权限,并设置适当的凭据和范围。
第一步:在谷歌云控制台中创建一个新项目
- 选择一个描述性的项目名称
- 不需要组织
第二步:启用 Gmail API
- 在谷歌云平台仪表板的左上角,点击菜单☰按钮
- 单击 API 和服务
- 单击启用 API 和服务
- 通过搜索或浏览找到 Gmail API
- 单击启用
步骤 3:创建凭据
- 单击左侧菜单中的凭据
- 单击顶部的创建凭据
- 单击 OAuth 客户端 ID
- 选择桌面应用程序并选择一个客户端名称(没有太大关系)
- 单击 Create,现在不用担心保存客户端 ID 和客户端密码
步骤 4: OAuth 同意屏幕
- 输入您的应用名称和用户支持电子邮件
- 忽略应用程序域部分,因为我们暂时不会通过任何托管网站访问数据
- 输入开发者联系信息
- 单击保存并继续
第五步:范围
- 单击添加或删除范围
- 搜索“gmail.readonly”,点击弹出的范围
- 单击更新
步骤 6:测试用户
注意:有必要添加一个测试用户,因为如果您没有自己的组织,阅读和访问电子邮件是一个受限制的范围
- 在测试用户下,单击添加用户
- 输入您的电子邮件,然后点按“存储”
步骤 7:下载凭据
- 单击左侧菜单中的凭据
- 在 OAuth 2.0 客户端 id 下,单击下载图标
- 将凭证 JSON 文件移动到您的工作项目目录中
- 为了便于使用,将其命名为
credentials.json
Python 快速入门
下面的起始代码很大程度上是仿照Gmail for Developers>Gmail API>Python quick start页面编写的,但是让我们一起浏览一下,设置好我们的服务实例,并开始阅读电子邮件!
首先,我们必须注意导入和范围。如果您正在跟进,您将需要初始化一个名为quickstart.py的新文件,并粘贴以下代码行。
接下来,我们必须完成授权流程并存储我们的访问/刷新令牌!注意,这一步需要我们之前下载的credentials.json文件。下面的函数返回一个服务实例,我们将使用它来访问 Gmail API 中的方法。

展示 Gmail 标签|作者图片
在验证我们可以访问经过身份验证的用户数据之前,让我们先讨论一下标签。鉴于每日摘要电子邮件倾向于模板化并通过自动邮件服务发送,我们可以通过将感兴趣的电子邮件组织到单个标签分组中来使我们的生活变得更容易。为此,请在 Gmail 搜索栏中输入搜索词,选择全部,然后单击标签(🏷)图标将现有标签或新标签分配给所有选定的邮件。对于所有符合搜索标准的未来简讯,您可以设置一个高级 Gmail 过滤器,自动将它们分配到同一个标签。
当调用上述方法时,它将打印用户 Gmail 帐户中所有标签的列表。这个列表可能看起来类似于、INBOX、SENT、IMPORTANT、TRASH、DRAFT、SPAM、STARRED等。值得注意的是,标签 id 并不总是与标签名称一致。例如,我新创建的MEDIUM标签的 id 是Label_793897032304597046。我们希望保留这个 id,因为我们需要它来获取我们感兴趣的电子邮件。
if __name__ == '__main__':
service = get_service()
get_all_email_labels(service)
要执行这段代码,我们必须在快速入门中运行上述两个函数。为此,您只需打开一个终端窗口,并通过键入以下命令来执行包含所有代码片段的完整脚本:
python quickstart.py
如果是第一次执行 quickstart,您可能会看到弹出如下提示。它们是为了让你确认,虽然你不是谷歌认证的开发人员,但你希望继续并允许应用程序/项目查看你的电子邮件。


作者提供的图片
The authentication flow has completed. You may close this window.
如果一切顺利,您应该会看到包含上述消息的页面。这意味着我们可以走了!
数据采集和清理
为了获取数据,我们可以编写一个简单的函数get_data()来获取带有指定标签 id 的所有用户消息。
执行 API 调用
获取电子邮件元数据的代码大致如下:
results = services.users().messages().list(userId='me', labelIds = ['XXXXXXXX'], maxResults=500).execute()
以下代码行为所有提取的消息编译一个元数据列表:
messages = results.get('messages', [])
因为这种类型的 API 调用返回的最大结果数永远不会超过 500,所以我们将从 API 调用中收集下一个页面标记,并实现一个简单的 while 条件。
要调用这个方法,我们可以从quickstart模块导入它,并指定我们想要获取中等的每日摘要电子邮件。在引擎盖下,这将找到正确的标签 id。
from quickstart import get_service, get_data
service = get_service()
messages = get_data(service, 'Medium Daily Digest')
print(len(messages))
就这样,596 条信息被获取了!我们能打印一份出来吗?嗯……不完全是。打印第一条消息实际上会返回以下元数据:
{'id': '17b590fa1e590dfa', 'threadId': '17b590fa1e590dfa'}
这意味着需要进行更多的 API 调用来检索电子邮件内容本身。要获得实际的消息,我们必须进行以下 API 调用:
msg = service.users().message().get(userId='me', id=message['id'], format='full').execute()

作者图片
啊…混乱!这不完全是我们希望的精确提取的内容,但它是一个起点。原来我们的 msg 对象实际上是一个字典,它有以下键:dict_keys([‘id’, ‘threadId’, ‘labelIds’, ‘snippet’, ‘payload’, ‘sizeEstimate’, ‘historyId’, ‘internalDate’])。
打印出来的msg['snippet']实际上很整洁,但是限制在 200 个字符左右。因此,必须使用'payload'键提取所有有价值的信息。

作者图片
有趣的是,电子邮件正文数据是 MIME 消息部分,这实质上意味着数据存储为 base64url 编码的字符串。要解码它,我们可以使用base64 Python 库。完整的数据采集代码流程如下所示:
使用正则表达式解析电子邮件正文
专注于我们上面实验的示例电子邮件消息,让我们解码正文数据,并获得一些关于文章元数据在每日摘要中如何格式化的直觉。下面是与本文开头所包含的我的每日文摘截图相对应的邮件正文的转储:
Today's highlights
NLP Profiler: Profiling datasets with one or more text columns
Creating high-level insights from text dataset using NLP Profiler
Himanshu Sharma (https://medium.com/@hmix13?source=email-e353ddb0c125-1629269257172-digest.reader-7f60cf5620c9-9b791193db89----0-59------------------e4b04afc_f3d7_492e_98c5_a3c1bbb1304a-1-dbdc0eb5_2de9_432f_a90c_bcf2300858ca)
in Towards Data Science (https://medium.com/towards-data-science?source=email-e353ddb0c125-1629269257172-digest.reader-7f60cf5620c9-9b791193db89----0-59------------------e4b04afc_f3d7_492e_98c5_a3c1bbb1304a-1-dbdc0eb5_2de9_432f_a90c_bcf2300858ca)
·5 min read
Data Cleaning in Python with NumPy and Pandas
The first utilities that an aspiring, python-wielding data scientist must learn include numpy and pandas. All…
Mike Flanagan (https://medium.com/@mike-flanagan?source=email-e353ddb0c125-1629269257172-digest.reader-29038077e4c6-3776f9a6e77f----1-98------------------e4b04afc_f3d7_492e_98c5_a3c1bbb1304a-1-dbdc0eb5_2de9_432f_a90c_bcf2300858ca)
in CodeX (https://medium.com/codex?source=email-e353ddb0c125-1629269257172-digest.reader-29038077e4c6-3776f9a6e77f----1-98------------------e4b04afc_f3d7_492e_98c5_a3c1bbb1304a-1-dbdc0eb5_2de9_432f_a90c_bcf2300858ca)
·5 min read
A data scientist has a look at Japan's Coronavirus analytics
After having been post-posted until 2021, the summer olympics are being sponsored by Tokyo, Japan from 23…
Tracyrenee (https://medium.com/@tracyrenee61?source=email-e353ddb0c125-1629269257172-digest.reader-f19413a43ae4-4f3a1dbfd5e8----2-98------------------e4b04afc_f3d7_492e_98c5_a3c1bbb1304a-1-dbdc0eb5_2de9_432f_a90c_bcf2300858ca)
in MLearning.ai (https://medium.com/mlearning-ai?source=email-e353ddb0c125-1629269257172-digest.reader-f19413a43ae4-4f3a1dbfd5e8----2-98------------------e4b04afc_f3d7_492e_98c5_a3c1bbb1304a-1-dbdc0eb5_2de9_432f_a90c_bcf2300858ca)
·7 min read
我们希望从这种非结构化但格式松散的数据中提取的字段是标题、副标题、作者、出版物和会议记录(例如,“X 分钟阅读”)。我们想忽略所有链接,因为抓取文章内容是不道德的,如果我有时间自己阅读所有这些文章,我早就这样做了。
为了提取想要的信息,我们将使用我们邪恶的敌人,他有时也是我们充满爱心和支持的朋友:regex。regex 是正则表达式的缩写,是指定搜索模式的字符序列。当我们可能不知道我们希望提取的具体文本,但我们知道它将呈现给我们的一般模式时,它可能是强大的。将这一概念应用到我们当前的任务中,每篇文章的信息都遵循以下一般结构:
[TITLE][SUBTITLE][AUTHOR] (link to profile) in [PUBLICATION] (link to article within publication)
·[MINUTES] min read
在正则表达式的世界中,我们可以通过两个强大的步骤提取这些信息:
第一步:通过替换删除所有链接( **sub** 方法)
text = re.sub(r'\(https?:\S+.*\)', '', msg_body, flags=re.MULTILINE)
第二步:通过 **findall** 方法进行模式匹配
# \r\n means we expect a line break
# (.*) is a capture group, meaning we want to store the filler textre.findall('(.*)\r\n\r\n(.*)\r\n\r\n(.*) \r\n in (.*)\r\n*·(.*) min read', text, re.MULTILINE)

作者截图
在用 regex 快速地打了两下之后,我们已经获得了我们想要获得的所有 5 个字段的数据!又快又无痛,对吧?老实说,派生正则表达式模式来完成这个任务实际上是相当痛苦的。谢天谢地,regex101 帮我解决了问题,并帮助我对问题进行了分类。我强烈推荐在那里制定正则表达式,并准备了一个方便的测试用例来使用。
组装的数据帧
包含从 API 获取的文章中经过正则表达式解析的信息的数据如下所示:
[Timestamp('2021-08-18 11:40:00+0000', tz='UTC'), 'NLP Profiler: Profiling datasets with one or more text columns', 'Creating high-level insights from text dataset using NLP Profiler', 'Himanshu Sharma', 'Towards Data Science ', '5']
[Timestamp('2021-08-18 11:40:00+0000', tz='UTC'), 'Data Cleaning in Python with NumPy and Pandas', 'The first utilities that an aspiring, python-wielding data scientist must learn include numpy and pandas. All…', 'Mike Flanagan', 'CodeX ', '5']
[Timestamp('2021-08-18 11:40:00+0000', tz='UTC'), "A data scientist has a look at Japan's Coronavirus analytics", 'After having been post-posted until 2021, the summer olympics are being sponsored by Tokyo, Japan from 23…', 'Tracyrenee', 'MLearning.ai ', '7']
只需一行代码,我们就可以组装一个由这些文章元数据组成的pandas数据框架。
df = pd.DataFrame(data, columns = ['Date', 'Title', 'Subtitle', 'Author', 'Publication', 'Minutes'])
接下来是一个艺术作品,包含来自每封电子邮件的正则表达式匹配的解包元组值。

作者图片
一览式可视化


媒体每日文摘中出现的前 10 位出版物和作者|由
我们可以从上面简单的matplotlib条形图中看到,阅读推荐文章的前 3 位出版物是关于数据科学、更好的编程和创业。这是有意义的,因为我对媒体的兴趣和互动倾向于数据科学相关的应用程序和软件开发技巧和工具。在我的推荐阅读清单中,前三名最多产的作者是里士满·阿拉克、马卡姆·海德和戴夫·格什格恩。我记得 Parul Pandey 是第一个让我迷上 Medium 的作者,所以很高兴看到她出现在这个前 10 名的名单中,还有 TDS 团队经常发布的综述和亮点!

作者创建的可视化
制作“分钟”列的直方图,我们可以发现中等文章的长度/阅读时间分布遵循一个非常标准的钟形曲线,稍微向左倾斜。中篇文章最常见的长度是 4 分钟左右。

作者创建的可视化
按出版物细分阅读时间,我们可以观察到,面向数据科学的文章通常篇幅较长,因为它们倾向于深入研究更多的技术细节,并夹杂着大量引发评论的图表和数字。锻造物品的长度通常在 3-5 分钟之间,元素物品的长度通常在 5-7 分钟之间,有一些异常值。
反文字云的视角
如果你是一个热衷于数据科学的读者或数据可视化的爱好者,我相信你已经偶然发现了数百篇使用 Python wordcloud库的文章。在我看来——我是作为一个犯了这种错误的人这么说的——对于那些被自然语言处理(NLP)吓倒的人来说,生成一个词云是一种回避策略。wordcloud 甚至没有触及可以通过新的和快速出现的自然语言处理技术利用的洞察力的表面,如主题建模、标记化、词条化、命名实体识别、文本摘要、单词嵌入等。但是为了完整起见,我们来做一个 wordcloud。

作者生成的 Wordcloud
酷毙了。单词 Python 、机器学习和数据科学在语料库中出现频率最高。希望这不会让任何人感到震惊。在我的下一篇文章中,我将挑战自己不要满足于文字云。相反,我将通过主题建模的方式对我的中等兴趣进行逆向工程——我也将向您展示如何做到这一点!
结论
这就结束了使用 Gmail API 从 Medium Daily Digest 电子邮件中提取文章元数据的教程。我希望你在阅读的过程中学到了一些新的东西。我想强调的是,我有意地决定避开一个以摘要为导向的项目,因为在我们生活的当前世界中,似乎一切都在被合成、优化,并且变得对我们更方便。我的目标不是避免阅读中等推荐的文章,而是通过人工制品来分析我自己的行为数据,人工制品是我通过算法整理的符合我最近兴趣的文章列表。我希望更多地了解我的兴趣在过去几年中是如何发展的,我希望你能决定一起来。
所有源代码可在以下 GitHub repo 获得:
https://github.com/sejaldua/digesting-the-digest
利用自然语言处理抽取实体间的关系
原文:https://towardsdatascience.com/extracting-relations-among-entities-using-nlp-b3d773c709ce?source=collection_archive---------12-----------------------
示例和如何进行头脑风暴

照片由帕里什·弗里曼在 Unsplash 上拍摄
在这篇文章中,我们介绍了使用 NLP 提取命名实体之间关系的问题。我们用越来越复杂的例子来说明这个问题,并在这个过程中思考解决这些问题的方法。
让我们开始吧。
考虑这些句子。
John Doe works at Google.
Apple is located in Cupertino.
我们想要检测这些句子中的各种实体。他们是
Person name = John Doe
Company = Google
Company = AppleCity = Cupertino
幸运的是,这项任务(命名实体识别)的 NLP 已经足够成熟,可以高质量地完成。参见1。
合乎逻辑的下一步是能够使用 NLP 推导出这些实体之间的关系。在我们的案例中,我们希望得到
works_at(person:john doe,company:google)
located_in(company:apple, city:cupertino)
很明显,自动化不仅检测实体,而且检测它们之间的关系的过程具有相当大的价值。使用这种方法,我们可以从非结构化文档中提取结构化数据(这种文档有很多)。然后可以对结构化数据执行强大的查询。例如给问答系统供电。
这个元用例有许多特殊的应用。例如挖掘特定垂直领域中的文档以揭示该垂直领域中的实体和它们之间的关系。类似地,挖掘一家公司的文档来揭示其工程或业务流程中的内幕。
第一次观察
让我们仔细看看我们的句子。利益关系嵌入在的工作中,位于。看起来我们应该可以用 NLP 来检测这些。先给这个问题起个名字:关系短语提取。
我们对这个句子做了词性分析,在https://parts-of-speech.info/

灰色是名词,绿色是动词,紫色是介词
正如我们所看到的,词性标签携带着识别关系短语的强烈信号。还要记住,命名实体识别将已经标记了这些句子中的命名实体,这将进一步增强我们的信心,即在这些例子中,关系短语对应于词性标记<动词> <介词>的序列。
为了更生动地说明这一点,让我们描述一下标记化的句子以及它们的标记序列,如下所示。
Tokens: John Doe works at Google
POS tags: noun noun verb preposition noun
NER tags: person person — — companyTokens: Apple is located in Cupertino
POS tags: noun verb verb preposition noun
NER tags: company — — — city
显然,在这些例子中,将 POS 标签中的信息与 NER 标签中的信息结合起来可以很好地识别关系短语。
一个更详细的例子
接下来考虑这句话,摘自[2]。
Read The Adventures of Sherlock Holmes by Arthur Conan Doyle online or in your email
被命名的实体是一本书的书名和一个人的名字亚瑟·柯南·道尔。前者比后者更难识别。这是因为书名更长,有更多样的宇宙。有趣的是,这本书的书名里有一个人的名字。
现在,让我们把识别书名的复杂性埋在引擎盖下。如果你很好奇,请阅读一些更详细的方法,如1中 NER 背景下的 HMMs。
假设这两个实体被正确识别,我们注意到关系短语仍然位于它们之间。也就是说,这个句子的结构是
<words> book-title by person-name <words>
下面是这句话相关部分的词性分析。

紫色表示介词
夹在两个实体中间的单词是一个介词,这一点令人放心。它支持由从这个形成关系短语。
让我们利用这个机会,通过更深入地‘钻研】一下。很容易想象,在一个巨大的语料库中,我们可能会遇到无数的
book-title by person-name
从这些例子中,我们也将能够推断出书名总是在之前加上,并且人名总是跟在其后。因此,原则上,单词 by 可以用作实体本身的附加预测符,在我们的例子中是书名和人名。考虑用由编写的替换的。后者对书名和人名实体的预测强度现在更高。
接下来,我们来看看
Wimbledon is a tennis tournament held in the UK in the first two weeks of July every year.
这一个可以通过实体识别来“解析”(有点广义的解释)为
**Sports event name** is a **sport tournament** held in the **location** in the **time**.
前三个实体相当清晰,可以通过适当训练的 NER 方法检测出来。第四个,时间,不太脆。在我们的例子中,它的值是每年七月的前两周。为这样一个不太清晰的实体培养一个 NER 可能很难。相反,我们可以考虑识别更细粒度的基于时间的实体。如月。有了这样的探测器,我们就可以部分推断出 时间中的为* 月份 : 七月。*
现在让我们看看位置。用分等级的方式对待它是有意义的。具体来说,要将位置分解成更精细的实体,如国家、州、城市……我们可以识别这些实体,并使用分类法将它们联系起来。如国家 是-个 地点,州 是-个 地点,州 是-个 国家,等。(注意,一个特定的州可能与多个国家有多个 is-in 关联。)
好了,现在我们已经讨论了实体,我们能推断出它们之间的关系吗?肯定是下面这种 is-a 关系。
***Sports event name** is-a **sport tournament***
其他的需要更多的思考。首先,为了方便起见,我们将含有实体变量的句子缩写如下。
***SEN** is a **ST** held in the **L** in the **T**.*
在我们的例子中,我们看到SThold-inL 会是一个错误的推论。是森 = 温网在 L = 英国举行,而不是 ST = 网球赛?
一个值得探讨的想法是从形成三个假设开始,以关联规则的形式表达3
*ST=tennis tournament, held-in ⇒ L=UK
SEN=Wimbledon, held-in ⇒ L=UK
SEN=Wimbledon, ST=tennis tournament, held-in ⇒ L=UK*
在上图中,将XY视为**
*IF X THEN Y*
给定一个巨大的语料库,如维基百科,分解成单独的句子,然后我们可以计算每个关联规则的所谓支持度和置信度。一条规则的支持度XY 就是 X 成立的句子数。这条规则的置信度是P(Y|X)。
从这样的分析中,我们可以合理地推断出,STST=网球锦标赛并不意味着它在英国举行,而 SEN = 温布尔登总是意味着它在英国举行。
接下来考虑涉及 T (时间)的关系中的。我们可以同样对待。我们的候选关联规则集有点大。在X**Y, Y 永远是每年七月的前两周T=。 X 是{ 森 = 温网, ST = 网球锦标赛, L = 英国 }的任意非空子集**
将这个问题框架化为一个涉及挖掘高置信度关联规则的问题,让我们可以使用为这个问题开发的高度可伸缩的算法。例如 FP 增长或先验算法。参见3。如果我们要对维基百科中的所有句子进行这种分析,我们可能真的会面临规模问题,许多句子中有两个以上的实体(我们也是如此)。
最后,让我们把注意力转向下面这句话。
*In 2019, the men’s singles winner was Novak Djokovic who defeated Roger Federer in the longest singles final in Wimbledon history.*
这个句子是一个很好的例子,它说明了如何超越仅仅从句子的内容来联系句子中出现的实体。相反,我们将推断(或者更准确地说,收集证据)可能通过将本句中的信息与合适的全球知识模型相结合而推断出的关系。
我们实际上不会指定知识模型的形式。相反,如果我们也能使用这样一个模型,我们将思考我们能从这句话的信息中推断出什么。
从
*Novak Djokovic who defeated Roger Federer*
我们可以提取
*defeated(Person Name: Novak Dkokovic, Person Name: Roger Federer)*
以前面讨论的方式。
现在考虑一下稍后出现在同一个句子中的单词温布尔登。假设其被正确识别为体育赛事名称。我们现在可以将这些人的名字与这个事件联系起来。如果是这样,假设我们的知识图谱知道温网是一个网球赛事,我们也可以推断诺瓦克·德科科维奇和罗杰·费德勒是网球运动员。
我们在上一段的最后一句列出推论,主要是为了说明我们能推断出什么样的东西。对于这些特定的断言——这些人是网球运动员——在维基百科的其他句子中可能有更有力、更直接的证据。
最后,让我们注意一下,我们甚至可以从这些人的名字中推断出他们的性别。在句子的前面提到的男子的可以被看作是确凿的证据。
延伸阅读
- https://towards data science . com/named-entity-recognition-in-NLP-be 09139 fa 7b 8
- https://www . cs . CMU . edu/~ nbach/papers/A-survey-on-Relation-extraction . pdf
- https://en.wikipedia.org/wiki/Association_rule_learning
利用全球太阳图集提取太阳能潜力
原文:https://towardsdatascience.com/extracting-solar-potential-using-global-solar-atlas-e9cbcae06ca8?source=collection_archive---------20-----------------------
如何使用光栅图像计算粒度级别的太阳能潜力

在 Unsplash 上科学高清拍摄的照片
1.介绍
随着野火、洪水、全球变暖和其他自然灾害事件的增加,向可再生能源过渡的压力越来越大。许多国家已经承诺减少化石燃料的消耗,并转向替代能源,如太阳能、风能和水能,这些能源对气候造成的温室气体排放为零。与化石燃料不同,它们也很丰富,而且取之不尽用之不竭。
为了帮助各国实现可持续发展的目标,世界银行集团与 Solargis 合作发布了全球太阳能图集(GSA)[【https://globalsolaratlas.info/】。它为世界上任何地点或地区提供了快速、简便的太阳能潜力获取途径。光伏(PV)产量估计值由模拟模型使用太阳辐射和气象数据生成。鼓励读者参考技术文件,以了解如何计算估计值的更多信息(https://documents 1 . world bank . org/curated/en/529431592893043403/pdf/Global-Solar-Atlas-2-0-Technical-report . pdf)。这些初步估计可以帮助决策者、研究人员和商业太阳能公司做出更好的决策。在这篇博文中,我将演示如何使用 GSA 提供的 GIS 数据层来提取任何地区的光伏潜力数据。使用这些估计值的一个注意事项是,它们没有考虑可能影响一个地区光伏潜力的许多因素,因此,应该使用更详细的估计工具来获得对发电量的精确估计。
2.工作流程
GIS 数据的粒度允许我们估计任何地区的光伏潜力——无论大小——如地区、州、国家等。在这些光栅文件中捕获的像素级信息可以很容易地在 Google Earth Engine (GEE)中聚合,以在几秒钟内得出估计值。
有四个主要步骤:
- 下载 GIS 数据。PVOUT GeoTiff 文件是我们正在寻找的。
- 将 GIS 数据上传到 GEE 代码编辑器中的资产。本质上,我们将数据加载到 GEE,以利用 Google 云计算架构的能力来加速我们的分析。
- 用矢量形状覆盖光栅图像。使用 GEE 函数根据矢量边界对像素级别信息进行分组,并通过对其求和,将它们简化为每个多边形边界的 PV 潜力的单一统计数据。
- 将 GEE
FeatureCollection转换为 pandas 数据框架,并计算单位面积的 PV 潜力。
3.在 GEE 中编码并导出结果
我们将从导入earth engine和geemap 库开始。接下来,我们初始化并验证到 GEE 的连接。我们在邦/UT 级别为印度导入 FAO-GAUL 矢量图层。我们还导入了太阳能潜力的栅格,并通过平均将像素级别信息聚合到州/UT 级别。栅格中的像素值代表光伏电站日总发电量的长期年平均值,光伏电站采用独立式固定安装的晶体硅(c-Si)模块,以最佳倾斜度安装,以最大化年光伏发电量。
# Import the relevant libraries
import ee
import geemap
import pandas as pd# Initalize Earth Engine
try:
ee.Initialize()
except:
ee.Authenticate()
ee.Initialize()# Accessing state level polygon boundaries of India using FAO dataset
india = ee.FeatureCollection("FAO/GAUL/2015/level1") \
.filter(ee.Filter.eq('ADM0_NAME', 'India'))# Access the solar potential raster image saved as GEE asset
solarpot = ee.Image("users/skantbksc/pvpot")# Add reducer output to the Features in the collection.
solarPotFC = solarpot.reduceRegions(
collection = india,
reducer = ee.Reducer.mean(),
);# Sort the dataframe by PV potential per unit area
solarPotdf[['ADM1_NAME','mean']].sort_values(by='mean', ascending = False)

各邦/中央直辖区的平均太阳能潜力:作者图片
4.结论
光伏输出取决于几个因素,如落在光伏组件上的太阳辐射量、倾角、气温、天空中的灰尘/气溶胶量、云、光伏组件表面的清洁度、靠近光伏系统的外部障碍物造成的阴影等。虽然将所有变量准确地纳入模拟可能是不可能的,但 GSA 列出了使用历史太阳辐射、温度和气象数据的模拟练习的结果。GSA 以 GIS 数据层的形式提供这些结果,可以使用 GEE 对其进行分析。在使用 FAO-GAUL 数据集的印度矢量数据的图示中,我们看到印度西部各邦的太阳能潜力相对比东部和东北部各邦更丰富。
参考
- [数据/信息/地图]摘自“全球太阳能地图集 2.0”,这是一个基于网络的免费应用程序,由 Solargis s.r.o .公司代表世界银行集团开发和运营,利用 Solargis 数据,由能源部门管理援助计划(ESMAP)提供资金。更多信息:https://globalsolaratlas . info
- 吴(2020)。geemap:一个 Python 包,用于与 Google Earth 引擎进行交互式地图绘制。《开放源码软件杂志》,5(51),2305 页。https://doi.org/10.21105/joss.02305
- Gorelick,m . Han cher,m . Dixon,m . Ilyushchenko,s .,Thau,d .,& Moore,R. (2017)。谷歌地球引擎:面向所有人的全球地理空间分析。环境遥感。
使用 Python 从 Spotify API 提取歌曲数据
原文:https://towardsdatascience.com/extracting-song-data-from-the-spotify-api-using-python-b1e79388d50?source=collection_archive---------2-----------------------
利用 Spotify 保存在其图书馆中的数据,并将其用于我们的机器学习项目

sgcdesignco 在 Unsplash 上拍摄的照片
本文是展示我们使用 Spotify 的百万播放列表数据集【1】构建音乐推荐系统的四篇系列文章中的第一篇。本文详细介绍了从 Spotify 的 API 中提取数据,从构成数据集的唯一歌曲标识符中提取数据。本系列的其他文章如下:
- 第一部分:(本文)
- 第二部分:EDA 和集群
- 第三部分:用 Spotify 搭建歌曲推荐系统
- 第四部分:用 Flask 部署 Spotify 推荐模型
介绍
Spotify 在内部保存了大量关于其歌曲的数据,我们可以通过 Spotify API 访问这些数据。Spotify API 是一个很棒的公共工具,允许使用 Spotify 丰富的音乐数据来构建多种系统。在本文中,我们通过 Python 的 Spotipy 包学习使用这个 API 从唯一的歌曲标识符中提取数据。
这个项目我们需要的进口如下:
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
我们可以用 Spotify API 做什么?
Spotify API 非常强大,让我们可以访问 Spotify 上任何歌曲或艺术家的大量信息。这包括描述音频“感觉”的特征,如变量“活跃度”、“声音度”和“能量”,以及描述艺术家和歌曲受欢迎程度的特征。如果我们想对数据进行更高级的分析,我们还可以从这个 API 中获得更高级的信息,例如歌曲中每个节拍的预测位置。
Spotify 的其他功能,如推荐引擎和搜索,也可以通过 Spotify API 使用。要了解更多关于 Spotipy 包所基于的 Web-API 的信息,您可以在网站上查看。此外,您可以在这里使用控制台来测试 API 的功能,这可能有助于您修复自己的实现。
这篇文章将涵盖什么?
本文将介绍通过 Spotify 使用 Spotify web API 的基础知识。这包括从获取访问令牌和认证,到从播放列表中的歌曲中提取特征,给定其相关联的 URI(统一资源标识符)。
API 键
如果您以前没有使用过 API,那么使用各种密钥进行身份验证和发送请求可能会有点令人生畏。我们要看的第一件事是获取要使用的密钥。为此,我们需要一个面向开发者的 Spotify 账户。这与 Spotify 帐户相同,不需要 Spotify Premium。从这里,转到仪表板并“创建应用程序”。现在,我们可以访问使用 API 所需的公钥和私钥。
现在我们有了一个应用程序,我们可以获得这个应用程序的客户端 ID 和客户端密码。对于我们的应用程序,这两者都需要通过 Spotify web API 的认证,并且可以被认为是应用程序的一种用户名和密码。最佳实践是不要共享这两个密钥,尤其是不要共享客户端密钥。为了防止这种情况,我们可以将它保存在一个单独的文件中,如果您使用 Git 进行版本控制,那么应该忽略这个文件。

这些是各自的客户端 ID 和客户端密钥,需要它们来验证我们的应用程序。作者图片
使用 Spotipy 认证
我们可以使用 Spotipy 库执行两种类型的身份验证。首先,我们可以在不考虑特定用户的情况下进行身份验证。这允许我们访问 Spotify 的一般功能,并查看播放列表。如果没有这个,我们就看不到特定用户的统计数据,比如他们的关注列表和所听音乐的统计数据。
要在不登录帐户的情况下进行身份验证,我们只需要 id、客户端和密码。然后,我们可以用下面几行代码创建我们的“Spotify”对象:
#Authentication - without user
client_credentials_manager = SpotifyClientCredentials(client_id=cid, client_secret=secret)
sp = spotipy.Spotify(client_credentials_manager = client_credentials_manager)
要使用帐户进行身份验证,我们需要提示用户登录。这是使用包的“spotipy.utils”部分中的“prompt_for_user_token”方法完成的。由于我们没有在这个项目中使用它,所以就不做探讨了,但是可以在 Spotipy 包【3】的文档中读到更多关于它的内容。
使用 Spotify 对象
这两种类型的认证创建相同的 Spotify 对象,只是创建方法不同。这意味着相同的类方法可用于任一种身份验证方法,与当前用户相关的方法除外。现在,使用这个对象,我们可以与 Spotify API 进行交互,以获取我们想要的信息。
URI 到底是什么?
使用 Spotify API 的一个重要组成部分是使用统一资源标识符,指向 API 中的每个对象。我们需要一个 URI 来执行 API 引用 Spotify 中对象的任何功能。任何 Spotify 对象的 URI 都包含在其可共享链接中。例如,从 Spotify 桌面应用程序中找到的全球热门歌曲播放列表的链接是:
https://open.spotify.com/playlist/37i9dQZEVXbNG2KDcFcKOF?si=77d8f5cd51cd478d "
该链接中包含的 URI 是“37i 9 dqzevxbng 2 kdcfckof”——如果我们将它与 API 一起使用,那么我们将引用全球热门歌曲播放列表。您可能还会看到以“spotify:object_type:uri”格式列出的 URI,这也是一种有效的方式,如果有的话,这是一种更有效的引用对象的方式。使用这些 URIs,我们将提取播放列表中歌曲的特征,并进而从这些歌曲中提取一系列特征,这样我们就可以创建一个数据集来进行分析。
从播放列表中提取曲目
我们将用于从播放列表中的轨道提取特征的第一种方法是“playlist_tracks”方法。该方法从播放列表中获取 URI,并输出包含该播放列表所有信息的 JSON 数据。幸运的是,Spotipy 包为我们对此进行了解码,因此我们可以相当容易地以 python 化的方式解析这些数据。
我们希望在这里提取轨迹数据,以便从中获取特征。这可以通过以下代码段来完成,该代码段提取给定播放列表中每首歌曲的 URI(在我们的示例中仍然是全球前 40 名):
playlist_link = "[https://open.spotify.com/playlist/37i9dQZEVXbNG2KDcFcKOF?si=1333723a6eff4b7f](https://open.spotify.com/playlist/37i9dQZEVXbNG2KDcFcKOF?si=1333723a6eff4b7f)"
playlist_URI = playlist_link.split("/")[-1].split("?")[0]
track_uris = [x["track"]["uri"] for x in sp.playlist_tracks(playlist_URI)["items"]]
当我们在这里时,我们还可以提取每个曲目的名称、它所属的专辑的名称以及该曲目的流行度(在这种情况下,我们预计它会很高—我们正在查看全球最流行的歌曲)。从艺术家那里,我们可以找到一个流派(尽管不是无懈可击的——艺术家可以创作多种流派的歌曲),以及一个艺术家流行度评分。
for track in sp.playlist_tracks(playlist_URI)["items"]:
#URI
track_uri = track["track"]["uri"]
#Track name
track_name = track["track"]["name"]
#Main Artist
artist_uri = track["track"]["artists"][0]["uri"]
artist_info = sp.artist(artist_uri)
#Name, popularity, genre
artist_name = track["track"]["artists"][0]["name"]
artist_pop = artist_info["popularity"]
artist_genres = artist_info["genres"]
#Album
album = track["track"]["album"]["name"]
#Popularity of the track
track_pop = track["track"]["popularity"]
从轨迹中提取特征
现在我们已经有了轨迹 URIs 的列表,我们可以从这些轨迹中提取特征,以便执行我们的分析。Spotify 根据对音频的分析,列出了每首歌曲的这些功能。我们可以通过 spotify 对象“audio_features(uri)”的一个方法来访问它们。这给了我们一个主要是数字特征的列表,我们可以用它来进行分析。
sp.audio_features(track_uri)[0]

以上代码的结果。图片作者。
其他用途
你可以用这个对象做很多其他事情,包括建立和编辑播放列表,控制你自己的 Spotify 播放,以及访问 Spotify 中对象的许多不同方面。我们在本文中只涉及了其中的一小部分,但是您可以在 Spotipy 包的文档中读到更多,这里是 3。
此项目的用途
在这个项目中,Spotify API 用于从百万播放列表数据集 1中给我们的数据中提取一组特征(上面展示的那些)。我们只使用这个数据集中的 1000 个播放列表的子集,因为整个数据集确实很大。在这个项目的链接 Github 库中,我们使用一个脚本为此编写一个函数,返回一个给定轨道 URI 的特性列表。
结论
Spotify 保存了大量内部数据,并允许我们通过他们的 API 访问这些数据。当我们想要使用自己的数据来构建数据集进行分析时,这是非常有用的。在百万播放列表数据集【1】中,能够提取所包含歌曲的特征是非常有用的,这样我们可以更好地理解歌曲之间的关系,并执行聚类以构建我们自己的推荐引擎。
同样,本文是我们使用 Spotify 的百万播放列表数据集构建推荐引擎系列的第 1 部分。本系列的其他文章链接如下:
- 第一部分:(本文)
- 第二部分: EDA 和聚类
- 第三部分:用 Spotify 搭建歌曲推荐系统
- 第四部分:用 Flask 部署 Spotify 推荐模型
在以后的文章中,我们将探索数据集,并基于提取的特征创建一个基于聚类的推荐模型。
这个项目的 Github 库链接到这里:
https://github.com/enjuichang/PracticalDataScience-ENCA
参考资料/进一步阅读
1 Spotify / AICrowd,百万播放列表数据集(2018),https://www . ai crowd . com/challenges/Spotify-Million-Playlist-Dataset-challenge
[2] Spotify,面向开发者的 Spotify,https://developer.spotify.com/
3普拉米尔,斯波特比文献公司,https://spotipy.readthedocs.io/en/2.19.0/
[4]普拉米尔,斯波蒂皮代码库,【https://github.com/plamere/spotipy
使用 5 行代码提取图像和视频中的对象。
原文:https://towardsdatascience.com/extraction-of-objects-in-images-and-videos-using-5-lines-of-code-6a9e35677a31?source=collection_archive---------4-----------------------

Joanna Nix-Walkup 在 Unsplash 上拍摄的原始照片
计算机视觉是计算机看到和识别物体的媒介。计算机视觉的目标是使计算机能够分析图像和视频中的对象,解决不同的视觉问题。对象分割为方便地分析图像和视频中的对象铺平了道路,为不同领域做出了巨大贡献,如医学、自动驾驶汽车中的视觉以及图像和视频中的背景编辑。
PixelLib 是一个为在现实生活应用中轻松集成图像和视频分割而创建的库。PixelLib 采用了强大的对象分割技术,使每个人都可以使用计算机视觉。我很兴奋地宣布,PixelLib 的新版本使计算机视觉中的对象分析比以往任何时候都更容易。PixelLib 使用分段技术,通过五行代码实现图像和视频中的对象提取。
安装 PixelLib 及其依赖项:
安装 Tensorflow 与:(PixelLib 支持 tensorflow 2.0 及以上版本)
- pip3 安装张量流
安装 PixelLib 与
- pip3 安装 pixellib
如果已安装,请使用以下工具升级至最新版本:
- pip3 安装 pixellib —升级
使用掩模 R-CNN COCO 模型提取图像中的对象
import pixellibfrom pixellib.instance import instance_segmentationsegment_image=instance_segmentation()
segment_image.load_model(“mask_rcnn_coco.h5”)
line 1–4:我们导入了 PixelLib 包,创建了 实例分段类 的一个实例,并加载了预训练的 Coco 模型。从这里下载模型。
segment_image.segmentImage("image_path", extract_segmented_objects=True, save_extracted_objects=True, show_bboxes=True, output_image_name="output.jpg")
这是代码的最后一行,在这里我们用下面的参数调用函数 segmentImage :
图像路径:这是要分割的图像的路径。
extract _ segmented _ objects:该参数告诉函数提取图像中分割的对象,设置为 true 。
保存提取对象:这是保存提取的分割对象的可选参数。
这是用边界框显示分段对象的参数。如果设置为 false,则仅显示分段掩码。
输出图像名称:这是保存输出图像的路径。
样本图像

来源:保罗·克鲁格(CCO)
物体提取的全部代码
输出图像

从图像中提取物体



注:图像中的所有对象都被单独提取并保存为一幅图像。我只展示了其中的一些。
Coco 模型中特定类的分割
我们使用一个预先训练的掩模 R-CNN coco 模型来执行图像分割。coco 模型支持 80 类对象,但在某些应用中,我们可能不想分割它支持的所有对象。因此,PixelLib 使得过滤未使用的检测和分割特定类别成为可能。
修改了用于分割特定类别的代码
target_classes = segment_image.select_target_classes(person=True)
除了我们调用了一个新的函数 select_target_classes、 来过滤未使用的检测并只分割我们的目标类 person 之外,它还是相同的代码。
segment_image.segmentImage("sample.jpg", segment_target_classes=target_classes, extract_segmented_objects=True,save_extracted_objects=True, show_bboxes=True, output_image_name="output.jpg")
在 segmentImage 函数中我们引入了一个新的参数叫做segment _ target _ classes, 来对目标类执行分割从select _ target _ classes函数 中调用。

哇!我们只能检测到这张照片中的人。
如果我们只对检测照片中人的交通工具感兴趣呢?
*target_classes = segment_image.select_target_classes(car=True, bicycle = True)*
我们把目标阶级从 人 改为 车 和 自行车 。

漂亮的结果!我们在这张照片中只发现了自行车和汽车。
注意:如果您过滤 coco 模型检测,将提取图像中分割的目标类别的对象。
基于 Coco 模型的视频对象提取
PixelLib 支持提取视频和相机馈送中的分段对象。
样本视频
*segment_video.process_video("sample.mp4", show_bboxes=True, extract_segmented_objects=True,save_extracted_objects=True, frames_per_second= 5, output_video_name="output.mp4")*
还是一样的代码,只是我们把函数从 segmentImage 改成了process _ video。它采用以下参数:
- 这是显示带有边界框的分段对象的参数。如果设置为 false,则仅显示分段掩码。
- 每秒帧数: 该参数设置保存的视频文件每秒帧数。在这种情况下,它被设置为 5,即保存的视频文件每秒将有 5 帧。
- extract _ segmented _ objects:该参数告诉函数提取图像中分割的对象,设置为 true 。
- 保存提取对象:可选参数,用于保存提取的分割对象。
- 输出 _ 视频 _ 名称: 这是保存的分段视频的名称。**
输出视频
*从视频*中提取 物体



****注:视频中的所有对象都被提取出来,单独保存为一幅图像。我只展示了其中的一部分。
视频中特定类别的分割
PixelLib 可以过滤未使用的检测,并分割视频和摄像机馈送中的特定类别。
**target_classes = segment_video.select_target_classes(person=True)segment_video.process_video("sample.mp4", show_bboxes=True, segment_target_classes= target_classes, extract_segmented_objects=True,save_extracted_objects=True, frames_per_second= 5, output_video_name="output.mp4")**
用于检测的目标类别被设置为人,并且我们能够仅分割视频中的人。
**target_classes = segment_video.select_target_classes(car = True)segment_video.process_video("sample.mp4", show_bboxes=True, segment_target_classes= target_classes, extract_segmented_objects=True,save_extracted_objects=True, frames_per_second= 5, output_video_name="output.mp4")**
分割的目标类别设置为汽车,我们只能分割视频中的汽车。
视频中分割特定类别和对象提取的完整代码
用于在相机馈送中分割特定类别和对象提取的完整代码
**import cv2 capture = cv2.VideoCapture(0)**
我们导入了 cv2 并包含了捕捉相机帧的代码。
**segment_camera.process_camera(capture, show_bboxes=True, show_frames=True, extract_segmented_objects=True, save_extracted_objects=True,frame_name="frame", frames_per_second=5, output_video_name="output.mp4")**
在执行分段的代码中,我们替换了要捕获的视频文件路径,也就是说,我们正在处理摄像机捕获的帧流。我们添加了额外的参数来显示相机的帧:
- show_frames: 这是处理分段摄像机帧显示的参数。
- 帧名:这是显示的摄像机帧的名称。
使用用 PixelLib 训练的定制模型提取图像中的对象
PixelLib 支持定制分割模型的训练,并且可以提取用定制模型分割的对象。
**import pixellib
from pixellib.instance import custom_segmentation segment_image = custom_segmentation()segment_image.inferConfig(num_classes=2, class_names=['BG', 'butterfly', 'squirrel'])
segment_image.load_model("Nature_model_resnet101.h5")**
line 1–4:**我们导入了 PixelLib 包,创建了 自定义 分段类 ,调用了推理配置函数(InferConfig)并加载了自定义模型。从这里下载定制模型。自定义模型支持以下两个类:
- 蝴蝶
- 松鼠
**segment_image.segmentImage("image_path", extract_segmented_objects=True, ave_extracted_objects=True, show_bboxes=True, output_image_name="output.jpg")**
我们调用相同的函数 segmentImage 用于 coco 模型检测。
使用自定义模型进行对象提取的完整代码
样本图像

来源:作者:维基公共网站(CCO)的彼得·切林
输出

从图像中提取物体

使用用 PixelLib 训练的定制模型提取视频中的对象
样本视频
使用自定义模型在视频中提取对象的完整代码。
输出
提取对象
****
使用自定义模型在摄像机输入中提取对象的完整代码
阅读本文,了解如何使用 PixelLib 训练一个定制模型。
**
访问 PixelLib 的官方文档
通过以下方式联系我:
邮件:olafenwaayoola@gmail.com
推特: @AyoolaOlafenwa
脸书:阿尤拉·奥拉芬娃
Linkedin: 阿尤拉·奥拉芬娃
查看这些关于如何利用 PixelLib 在图像和视频中进行语义分割、实例分割和背景编辑的文章。
[## 用 5 行代码实现图像分割
towardsdatascience.com](/image-segmentation-with-six-lines-0f-code-acb870a462e8) [## 用 5 行代码改变任何图像的背景
towardsdatascience.com](/change-the-background-of-any-image-with-5-lines-of-code-23a0ef10ce9a) **
简而言之,极值理论有各种应用
原文:https://towardsdatascience.com/extreme-value-theory-in-a-nutshell-with-various-applications-3260b6a84316?source=collection_archive---------13-----------------------

图片由 Bálint Szabó on Unsplash 提供
实践教程
在 18 世纪,当统计学被发现用来回答与赌博获胜几率相关的问题时,正态分布是一个非常令人满意的工具。对于其他各种情况,您可能有兴趣研究大事件的影响,以进一步了解和未来的预期,正态分布不会做工作!。许多数据都符合这一描述,例如,您需要研究重大财务损失的影响并获得其发生概率的财务数据。由于这种事件很少发生,正态分布会忽略它,因为它不会发生,而极值理论(EVT) 似乎可以通过突出数据的极端部分来解决这一问题,并单独建模以回答与极端事件相关的问题。
正如统计学中的任何表达都有【理论】这个词一样,给人一种装满复杂/未触及内容的黑匣子的印象,这与 EVT 的名声是一样的。在这篇文章中,我们将预览各种应用程序的 EVT 的简化介绍,最后你会对 EVT 有个大概的了解,为什么以及何时需要使用它?!。
概观
这篇文章内容如下
- EVT 简介。
- 列出了实现 EVT 的不同应用程序。
- 与 EVT 相关的主要软件包,应用于 YouTube 趋势视频数据。
介绍
“In cauda venenum”是你在劳伦斯·德·汉和安娜·费雷拉所著的《极值理论:导论》一书中看到的第一句话,这句话很能表达应用 EVT 时你将要处理的数据的性质。极端数据通常有更重要的尾部信息,反映真实的行为。峰度是简单统计中最适合检测极端数据的度量,其中高峰度表示重尾分布,而低峰度表示相当轻的尾分布。峰度仍然不足以获得关于尾部有多重的准确信息,端点的估计(如果可能的话),..等。

作者图片
根据 EVT,对于被视为和分析为极端数据的数据,其样本最大值必须具有极限分布。从统计学上来说

弗雷歇、罗纳德·费雪、伦纳德·蒂皮特、理查德·冯·米塞斯和鲍里斯·格涅登科建立的 EVT 理论和基本原理。它们规定了样本最大值的一组非退化极限分布,

显然,这类分布取决于一个主要参数,即所谓的极值指数(EVI) ,这是理解极限分布性质的关键参数。EVI 将极值分布的一般类别分为三个子类:

作者图片
- 正 EVI 表示要和分布的无限端点,那表示你正在和重尾分布打交道。
- 零 EVI 意味着分布端点等于无穷大,这是指轻尾分布。
- 负 EVI 是指端点为负的 EVI 的可逆分布,表示短尾分布。
通常,极端分析从相对较大的数据开始,然后缩小规模,只分析极端观察值。选择这些观测值的方法主要有两种,分别是块最大值法和峰值超过阈值 (POT)法。块最大值法 将数据分成若干块,得到每个块的最大值。它需要非常大的数据集来拥有足够数量的块。 POT 方法是对极端事件建模的更现代的方法,它通过指定某个 高阈值 并在分析中考虑该点以上的所有观察值。在盆栽法中,找到阈值总是很关键的,有许多方法可以找到阈值,例如 Hill 图。
应用程序
从介绍中,你可能对使用极端分析的案例有所了解。简而言之,当您有兴趣查看数据中可能从未发生过的极端/不规则事件时,峭度这一简单工具可能会给出提示。但是如果还不清楚,也不用着急!在这里,我会给你几个真实的应用和他们的结论,以及 EVT 是如何被纳入分析。

图片由 Hermes Rivera 在 Unsplash 上拍摄
一、人类寿命的极限
此应用程序考虑 1986 年至 2015 年间死亡的荷兰居民的死亡年龄数据。基于这些数据,他们想确定人类寿命的极限?!。使用 POT 方法,通过最大似然估计器估计,女性和男性的 EVI 均为负值,这强烈表明年龄分布存在有限终点。然后,终点估计为女性 124 岁,男性 125 岁。关于分析和数据的详细信息,你可以查阅《T2 通过极值理论对人类寿命的限制》一文。

图片由布拉登·科拉姆在 Unsplash 上拍摄
二。极限运动记录
为了回答这个问题,收集了关于跑步、投掷和跳跃的运动记录数据,每项特定运动的最终记录是多少?!。他们首先通过矩估计量来估计 EVI,对于大多数事件,矩估计量变成负值,这表明具有有限的终点。然后基于估计的 EVI 来估计终点。更详细的内容可以在论文 中通过极值理论 记录田径运动。

由 Thom Milkovic 在 Unsplash 上拍摄的图片
三。堤坝高度
这被认为是 EVT 最著名的应用之一。在荷兰,众所周知,几乎 40%的国土低于海平面。确保国家免受 1953 年发生的那种可能的洪灾是非常重要的。然后,EVT 需要回答一个重要的问题:在一年内发生洪水的概率很小的情况下,堤坝应该有多高?!。通过收集 100 年的风暴数据,他们通过估计堤坝高度的极端分位数来回答这个问题,假设洪水的概率是 0.0001。

杰夫·亨德里克斯在 Unsplash 上拍摄的图片
四。摩天大楼
另一个有趣的应用是对摩天大楼的数据建模,并检查它们的高度和层数的限制。全球摩天大楼的数据来自高层建筑和城市人居委员会(CTBUH)。摩天大楼的数量分布符合对数线性模型。进行 EVT 分析以预测极限高度和楼层数。 用极值理论预测城市天际线 文中有详细的分析和结果。

图片由 Pepi Stojanovski 在 Unsplash 上拍摄
五、风险管理
在这里,我不会举出一个具体的应用,因为有几个与保险和银行领域的风险管理相关的应用使用 EVT。a 一个关键工具是风险价值和预期短缺,这两个工具都用于根据极端情况评估偿付能力。对于这些领域还有更多其他的 EVT 工具和实现,可以查看 极值理论作为风险管理工具 进行进一步的讨论和应用。
其他应用可以在诸如网络、分类、..等。
R 包和 YouTube 数据上的实现
现在到了“你需要知道自己怎么做!”为此,我将给出 R 中与 EVT 相关的一些重要函数的源代码,然后我将讨论实际数据的实现。r 因为一个繁荣的统计软件包是一些准备好的与极端相关的软件包的好地方,你可以检查由 Christophe Dutang 和 Kevin Jaunatre 的极端值分析的几个软件包的更新列表。该资源包含与单变量和多变量极值分析相关的主要软件包,其中包含 EVI、极值分位数、模型和重要图的不同估计量。
对于应用程序方面的额外说明和数据处理,我将简要分析 YouTube 上大多数热门视频的真实数据,以了解其分布的尾部。

图片由 Unsplash 上的 NordWood Themes 提供
我从 Kaggle 那里获得了关于 YouTube 上不同国家热门视频的数据。该数据包含关于每个视频的观看次数、喜欢、不喜欢和评论的信息。我主要关心查看视图分布在尾部的特殊表现,并估计其 EVI,以查看所选国家视图分布的尾部有多重。


不同国家 YouTube 上最热门视频的经验性分布
经验分布图显示了数据的一般形状和每个国家的一些极端观察结果,但它没有给出关于极端观察或尾部沉重的明确答案。为了更清楚地了解情况,我估计了每个国家的峰度。

选定国家的峰度
所有峰度都非常高,这可能反映了重尾数据,但由于它主要集中在离群值上,因此最高的峰度可能不反映最重的尾部。现在,为了真正检查尾部的重量,我将估计每个国家的 EVI,以便更清楚地了解所选国家中尾部最重的分布。

EVI 对选定国家的估计
我用 POT 方法用最大似然估计量来估计 EVI。第二行指的是阈值以上的观察次数,被认为是样本量的 10%。估计结果表明,所有被选国家都具有重尾分布,端点为无穷大。从 EVI 估计中,我们可以看到,EVI 最高的最重的尾部在法国,而日本的值较低,尽管日本的峰度较高,这再次支持了之前的陈述,即峰度不足以得出关于数据尾部的结论。基于获得的结果,我有兴趣计算一个非常著名的极端措施,称为预期短缺。它提供了给定视图超过非常高的分位数时的期望视图数, 换句话说,它给出了那些超过非常高的视图数(比如大于视图的第 99 个分位数)的视频的期望视图数。

选定国家的预期短缺估计数
如上表所示,英国的最大预期浏览量估计为 1.42 亿,而日本的浏览量最少。如果你点击了👉在这里,您将到达用于产生先前结果的数据和代码。
FAANG 公司正在重新定义数据科学原型
原文:https://towardsdatascience.com/faang-companies-are-redefining-data-science-archetypes-a1285241b599?source=collection_archive---------17-----------------------
意见
如何有目的地管理你的职业发展

信用:Pexels.com
注意:这篇文章的灵感来自我在 Meta 的经历,本质上是一篇基于观点的文章!
我越来越多地看到数据科学行业出现了四种原型。(1)人工智能研究员,(2)产品科学家,(3)ML 工程师,以及(4)分析工程师。如果你没有把这些原型中的一个作为目标,你可能会放慢你进入职业领域的速度,或者在这个领域里成长缓慢。
原型
1.人工智能研究者
我从这里开始,因为这可能是天真的,是大多数人听到“数据科学”时的想法。这个领域的特点是博士级别的人才。这不是错误;研究以为名。不一定需要 CS PhD 经济学、心理学等。也很受欢迎!
解决方案开发时间表相当缓慢。想想几个月到几年,而不是几天到几周。为每个问题建立一个定制的研究驱动的解决方案是不切实际的。因此,这种原型侧重于推动基准任务的发展水平,它理想地支持大量的下游任务。想想“脸书先知”
如果你只有学士或硕士学位,或者你对面向客户的工作感兴趣,这种原型并不适合你。
其他名称:应用科学家、人工智能科学家
2.产品科学家
这种数据科学的特点是全球战略。你的目标不是将某个过程自动化,而是在全球用户-产品生态系统的背景下理解这个过程。统计学在这里比 ML 更重要(因为 ML 不太容易解释。)你可能会问的问题是‘以性别为条件会有意义地改变年龄与产品使用的关系吗?’统计模型常用于缩小似是而非的假设搜索空间。在假设形成之后,实验被广泛用于验证假设和驱动产品策略。
这里的理想候选人有心理学、经济学或任何统计学为主的社会科学学科的学士或硕士背景。在这里,解释是王道,同时大量接触 SQL 查询、数据仓库等。生产数据库很少将所有关键变量很好地收集在一起。使用 Spark 之类的工具查询海量数据,同时针对查询重要性进行优化,这是一件非常好的事情。
其他名称:产品分析师、统计学家
3.ML 工程师
其次,对于人工智能研究人员来说,这种数据科学的味道是人们在听到“数据科学”时往往会分享的一种极其常见的模式这个原型是关于将 ML 模型作为产品特性部署到生产中的。例如,建立面部识别模型,并集成到社交媒体应用程序中。
解释并不是最终的目标——准确性和性能才是王道。ML 工程师通常不会问这样的问题,这个特性值得开发吗?它将如何吸引客户群?’(这些由产品科学原型回答。)相反,ML 工程师会问,‘什么预先训练好的模型(即伯特、YOLO 等。)可以与迁移学习和可用于培训和启动的数据以及高效和准确的基于 ML 的产品功能一起利用?'
正如在 1 中所讨论的。,从零开始为任何一次性任务设计一个真正新颖的模型成本太高。相反,这个原型将利用现有的 API,并专注于内聚集成。如果你想从头开始设计模型,并真正关注其内部机制的为什么和如何,这个原型可能不适合你。
其他名称:软件工程师
4.分析工程师
这种原型的特点是构建数据管道和引人注目的可视化/仪表板。这个原型在 CS 主题方面有很强的学士水平背景,使用 Spark 这样的工具对真正海量的数据执行 ETL。
这个原型不像产品科学家那样了解客户/产品/业务,但是,知道哪些聚合指标对业务最重要是关键。例如,产品科学家需要将大部分精力放在测试假设、估计(潜在的)参数,以及消除/更新没有数据支持的假设和用户故事上。然而,有潜在的无限数量的假设,适当的统计分析可能是缓慢或昂贵的。关键指标的简单可视化可以减少产品科学家的搜索空间;商业头脑使得分析工程师不可或缺。
其他名称:数据工程师、商业智能工程师
找到合适的
找到合适的衣服并不容易。但是问自己几个问题。
1.你对模型还是应用感兴趣?
如果是模型,把搜索范围缩小到人工智能研究员或者 ML 工程师。如果你对应用更感兴趣,把搜索范围缩小到产品科学家。这个问题对验证你是否适合做分析工程师没有太大帮助。
2.你更喜欢理解模型工作的原因还是解决持续的问题?
如果你真的想深入研究模型如何工作的具体细节,那么你真的应该追寻人工智能研究者的原型。但是,如果您想通过部署的产品特性来解决特定的问题,ML-Engineer 角色更适合您。
3.你更喜欢理解用户-产品生态系统中的用户行为模式,还是更热衷于开发可扩展的基础设施来支持这一目的?
如果你喜欢理解和推理,Product Scientist 是适合你的地方。但如果你对基础设施方面更感兴趣,选择分析工程师。
分手建议
1.漏斗
把你的职业生涯加班当成一个漏斗。开始宽,结束窄。如果你还在读本科,没有必要马上选择一个原型,只要尽可能多地全面接触就行了。在你的职业生涯中,试着找到上述问题的答案,并集中在最适合你的原型上。开始缩小和无法缩小是两个错误,它们会阻碍你的成长,或者让你陷入你不喜欢的角色。
2.FAANG vs non FAANG
这些原型是以法昂为中心的。初创公司和大型但历史悠久的公司可能不会从原型的角度来看待数据科学。你可能会发现,在一家更小/更老的公司,你被要求成为一名多面手。这是找到最吸引你的原型的好方法;然而,如上所述,作为多面手工作可能会阻碍你的成长——除非你对管理比对方向贡献更感兴趣。同样,你可能会发现自己在一家规模更小/历史更久的公司里担任人工智能研究员,却没有博士学位。
感谢阅读,如果你喜欢我的内容,请订阅!😃
基于 Python 的人脸标志点检测
原文:https://towardsdatascience.com/face-landmark-detection-using-python-1964cb620837?source=collection_archive---------2-----------------------
dlib 和 mediapipe 库的比较。

德米特里·秋科夫在 Unsplash 上的照片
介绍
人脸标志点检测是一项计算机视觉任务,我们希望从人脸中检测和跟踪关键点。这项任务适用于许多问题。
例如,我们可以使用关键点来检测人的头部姿势位置和旋转。有了这个,我们就可以追踪司机是否注意了。此外,我们可以使用关键点来更容易地应用增强现实。基于这项任务,我们可以产生很多解决方案。
值得庆幸的是,我们不必详细理解面部标志检测的概念。我们可以使用预先构建的库,如 dlib、OpenCV 和 mediapipe。在本文中,我将向您展示如何使用 dlib 和 mediapipe 实现人脸地标检测。
没有进一步,让我们开始吧!
基于 Dlib 的人脸标志点检测
Dlib 是一个应用机器学习和计算机视觉解决方案的库。这个库是基于 C++语言的,但是我们可以使用像 Python 这样的语言来使用这个库。通过使用这个库,我们可以应用的解决方案之一是面部标志检测。现在让我们进入实现阶段。
安装库
安装库可能会成为一个问题。如果我们没有好的向导,安装库可能需要几天时间。Dlib 就是其中之一。因为它使用 C++作为主要语言,所以我们必须安装 C++工具来安装库。
安装它需要几个步骤。以下是步骤:
- 首先,安装 CMake。你可以在这里下载软件https://cmake.org/download/。如果您使用的是 Windows,请先找到 CMake 文件路径。然后,在环境变量中将路径设置为可执行路径。
- 然后,安装带有 C++依赖项的 Visual Studio。你可以在这里 下载软件 。对于依赖关系,您可以查看下面的截图:

截图由作者捕捉
- 安装 Visual Studio 后,下一步是安装 Python。为了使您的安装更简单,我推荐您安装 Anaconda。你可以在这里 下载 。对于 Python 版本,我建议您使用 3.6.6 版本以避免任何错误。
- 最后,使用 pip 安装 CMake、dlib 和 OpenCV 库。下面是执行该操作的命令:
**pip install cmake
pip install dlib
pip install opencv-contrib-python**
导入库
安装完库之后,下一步是将它们导入到我们的代码中。我们将导入 OpenCV 来从网络摄像头获取输入,导入 NumPy 来进行数值计算,导入 Dlib 来检测人脸的关键点。
下面是实现这一点的代码:
初始化对象
现在让我们初始化几个变量。我们将初始化三个必须需要的变量:
- 用于检测一个或多个面部的检测器。我们在变量内部设置了dlib . get _ frontier _ face _ detector函数。
- 用于从面部检测关键点的预测器。我们在变量内部设置了 dlib.shape_predictor 函数。该功能需要一个预先训练好的模型位置作为参数,您可以在这里 下载 。
- cv2。VideoCapture 用于从网络摄像头捕捉图像的对象。此外,我们为从网络摄像头捕捉图像设置了一个值为 0 的参数。
让我们编写初始化变量的代码:
人脸标志检测机制
正如你从上面看到的,我们通过使用预训练模型来初始化面部标志检测器。该模型基于集合回归树,因为该模型将预测连续的数字。你可以在这里 阅读关于 车型的详细信息。
该模型在 iBUG-300 W 数据集上进行训练,其中包含图像及其对应的 68 个面部标志点。一般来说,这些标志点属于鼻子、眼睛、嘴和脸的边缘。你可以在这里 下载数据集 。
下面是面部标志位置的可视化:

这张图片是由来自 CMU 的 Brandon Amos 创建的
实现人脸标志点检测
现在你知道面部标志检测算法是如何工作的了。现在让我们实现算法。为了实现这一点,您可以查看下面的代码以及每行代码的解释:
通过将所有代码组合成一个,现在让我们来尝试代码!如果代码没有任何错误,网络摄像头将显示结果以及关键点。我的情况是这样的:

该图像由作者捕获。
基于 Mediapipe 的人脸标志检测
Mediapipe 是一个实现基于 ML 的计算机视觉解决方案的工具。这个工具是由谷歌开发的。
这个工具包含各种计算机视觉解决方案,如人脸检测,姿态估计,物体检测等等。
这个库的优点是你可以在很多平台上应用这些解决方案,比如 web、移动、PC 等等。
在上一节中,我已经向您解释了如何使用 dlib 实现面部标志检测。现在让我们使用 Mediapipe 实现面部标志检测。
该机制
该库使用 BlazeFace 模型来检测面部标志。BlazeFace 是一个深度学习模型,已经针对智能手机等低规格设备进行了优化。因此,我们可以实时使用该模型。
BlazeFace 包含两个主要步骤。首先,该模型检测图像上的一个或多个人脸。第二,图像通过使用回归检测大约 468 个面部关键点。
与 dlib 库不同的是,这个模型检测的是 3D 坐标。这些 x 和 y 坐标是根据图像比例归一化的。通过进行屏幕和模型 x 坐标之间的相对计算来检索 z 坐标。更多详情 可点击 阅读。
下面是一个面的展平网格及其相应的索引:

图片摘自 TensorFlow 的 GitHub 资源库 。
实现人脸标志点检测
一般来说,实现面部标志检测的管道与 dlib 库相同。它从导入库、初始化对象、检测人脸及其标志开始,并完成。
下面是实现这一点的代码:
如果您正确实现了代码,图像将显示在您的计算机上。以下是我的结果预览:

该图像由作者捕获。
比较
我们已经使用 dlib 和 mediapipe 浏览了人脸标志检测库。我们可以说两个库都很好用。因此,我们可以快速构建我们的解决方案。
然而,它们之间存在差异。dlib 库需要 C++依赖它。这就是为什么我们需要 CMake 和 Visual Studio 来安装这个库。
此外,这个库需要一个特定的 python 库。因此,如果没有受支持的 Python 版本来运行库,就必须创建一个虚拟环境。
另一方面,安装 mediapipe 更容易。你只需要从 pip 安装就可以了。因此,在使用 mediapipe 时,您不必担心更多的安装问题。
在这种情况下,dlib 只能检测关键点的 2D 坐标。另一方面,媒体管道可以检测关键点的 3D 坐标。因此,您可以使用 mediapipe 库中的这些关键点来估计头部姿势。
结束语
干得好!现在你知道如何使用 Python 实现人脸标志检测了。我已经向您展示了用于实现该解决方案的库,如 dlib 和 mediapipe。
我希望它能帮助你实现一个计算机视觉解决方案。还有,我希望它能成为你构建更复杂应用的基础。
如果你对我的文章感兴趣,你可以在 Medium 上关注我,看更多这样的文章。还有,如果你有什么问题,可以在 LinkedIn 联系我。
谢谢你看我的文章!
参考
[1]https://www . pyimagesearch . com/2017/04/03/face-landmarks-DLI b-opencv-python/
https://www . analyticsvidhya . com/blog/2021/07/face-landmark-detection-simplified-with-opencv/
使用 darknet 的 YOLOv3 进行面具检测
原文:https://towardsdatascience.com/face-mask-detection-using-darknets-yolov3-84cde488e5a1?source=collection_archive---------9-----------------------
新冠肺炎:如何使用 YOLOv3 构建面具检测器的教程。*为了便于推断,视频流和图像都可以使用。
这篇文章旨在为那些想要训练来自 YOLO 家族的物体探测器的人提供完整的指南(一步一步)。由于疫情,这样的任务似乎很热门。
对于本教程,我将使用 YOLOv3 ,这是 YOLO 家族最常用的版本之一,它包含了用于实时场景的最先进的对象检测系统,它的准确性和速度令人惊讶。YOLOv4、YOLOv5 等新版本可能会获得更好的结果,在我的下一篇文章中,我还将尝试这些架构,并与您分享我的发现。
假设你已经掌握了使用深度学习技术的物体检测,特别是你知道关于 YOLO 的基础知识,让我们开始我们的任务…

在 Unsplash 上安舒 A 的照片
你可以在我的 Github repo 上找到这个上传的项目。
环境🌠
为了实现这个项目,我利用了 Google Colab 的资源。我的第一个预处理步骤实验是在我的笔记本电脑上进行的,因为它们的计算成本不高,但是模型是在 Colab 上使用 GPU 进行训练的。

在 Colab 上通过编辑 - > 笔记本设置有人可以激活 GPU,我法师作者
资料组📚
首先,为了构建一个掩膜检测器,我们需要相关数据。此外,由于 YOLO 的性质,我们需要带边界框的注释数据。一种选择是通过从网上或通过拍摄朋友/熟人的照片收集图像来建立我们自己的数据集,并使用特定的程序如 LabelImg 手工注释它们。然而,这两种想法都非常乏味和耗时(尤其是后者)。另一个选项,也是目前为止对我来说最可行的,是使用公开可用的数据集。我从 Kaggle 中选择了人脸面具检测数据集,并将其直接下载到我的 Google Drive 中(你可以在这里查看如何做)。下载的数据集包含两个文件夹:
- 图像,包含 853。png 文件
- 注解,包含 853 个对应。xml 注释。
下载数据集后,我们需要将。xml 文件转换成。更准确地说,我们需要创建 YOLO 格式来训练我们的模型。下面显示了一个示例:
假设这是一个只包含 3 个边界框的图像的注释(这可以从中的 … 跨度的数量看出)。xml 格式。
为了创建一个. txt 文件,我们需要从每一个 5 件事。xml 文件。对于 an 中的每个 <对象>…</对象> 。xml 文件取数类(即名称>…</名称> 字段),以及边界框的坐标(即
<class_name> <x_center> <y_center> <width> <height>
然而,为了实现这一点,我创建了一个脚本来获取每个对象的上述 5 个属性。xml 文件并创建相应的。txt 文件。 ( 注: 更多关于转换的方法的解析步骤可以在我的脚本中找到)。
例如,image1.jpg必须有一个关联的image1.txt,包含:
1 0.18359375 0.337431693989071 0.05859375 0.10109289617486339
0 0.4013671875 0.3333333333333333 0.080078125 0.12021857923497267
1 0.6689453125 0.3155737704918033 0.068359375 0.13934426229508196
而这正是上面的转换。xml 文件转换为. txt 文件。 ( 注: 将对应的图像分组到同一个文件夹中至关重要。txt 注释)。
当然,在继续训练之前,我们需要绝对确定转换是正确的,并且我们将为我们的网络提供有效的数据。为此,我创建了一个脚本,它获取一个图像及其对应的。txt 注释,并显示带有基本事实边界框的图像。对于上述示例,图像如下所示:

maksssksksss0.png 来自 Kaggle 的公开发布的面具检测数据集
这是当我们知道到目前为止我们做得很好,但让我们继续下去…
火车测试分裂❇️
为了在训练阶段训练和验证我们的模型,我们必须将数据分成两组,训练组和验证组。比例分别为90–10%。因此,我创建了两个新文件夹,将 86 张图片及其相应的注释放入 test_folder,将其余 767 张图片放入 train_folder。
再忍耐一下,我们需要一些最后的润色,我们已经准备好训练我们的模型了 😅

布鲁斯·马斯在 Unsplash 上的照片
克隆暗网框架⬇️
下一步是通过运行以下命令克隆 暗网回购:
!git clone [https://github.com/AlexeyAB/darknet](https://github.com/AlexeyAB/darknet)
之后,我们需要下载预训练模型的权重,以便应用迁移学习,而不是从头开始训练模型。
!wget https://pjreddie.com/media/files/darknet53.conv.74
darknet53.conv.74 是 YOLOv3 网络的主干,它最初是为 ImageNet 数据集上的分类而训练的,并扮演提取器的角色。为了使用这一点进行检测,在训练之前,随机初始化 YOLOv3 网络中存在的附加权重。但是当然,他们会在训练阶段得到他们应有的价值观。
最后一步🚨
我们需要创建 5 个文件来完成我们的准备工作,并开始训练模型。
- face_mask.names :创建文件 _ 。包含问题类别的名称。在我们的例子中,原始 Kaggle 数据集有 3 个类别:带 _ 掩码、不带 _ 掩码和掩码 _ 磨损 _ 不正确。为了简化任务,我将后两个类别合并成一个。因此,对于我们的任务,我们根据某人是否恰当地佩戴了她的/他的面具,将分为两个类别:好和坏。
1\. Good
2\. Bad
- face_mask.data :创建一个 _ 。数据文件,包含与我们的问题相关的信息,将在程序中使用:
classes = 2
train = data/train.txt
valid = data/test.txt
names = data/face_mask.names
backup = backup/
注: 如果没有备份文件夹,就创建一个,因为每 1000 次迭代后都会保存权重。这些实际上是你的检查点,以防意外中断,从那里你可以继续训练过程。
- face_mask.cfg :这个配置文件必须根据我们的问题进行调整,即我们需要复制 yolov3.cfg,将其重命名为 _。cfg 并应用如下所述的修正:
- 将生产线批次更改为批次=64
- 将行细分改为细分=16 ( 注: 如果出现内存不足问题,将该值增加到 32 或 64 )
- 将输入尺寸更改为默认的宽度=416 ,高度=416。( 注 :就我个人而言,我从这个分辨率开始,对我的模型进行了 4000 次迭代的训练,但为了实现更准确的预测,我 提高了分辨率 ,并继续了 3000 次迭代的训练过程)。
- 将行 max_batches 改为(#classes * 2000),这样我们的任务 ( 注 )就有 4000 次迭代:如果你只有一个类别,你不应该只训练你的模型 2000 次迭代。建议 4000 次迭代是模型的最小迭代次数)。
- 将生产线步长更改为 max_batches 的 80%和 90%。对于我们的例子,80/100 * 4000 = 3200,90 / 100 * 4000 = 3600。
- 使用 ctrl+F 并搜索单词“yolo”。这将把你直接带到 yolo_layers,在那里你想做两件事。更改类别数量(对于我们的案例类别=2)并更改[yolo]线上方两个变量的过滤器数量。这个变化必须是过滤器=(类+ 5) * 3 ,也就是说,对于我们的任务,过滤器= (2 + 5) * 3 = 21。在我们的。cfg 文件,有 3 个 yolo_layers,因此你应该做上述的变化 3 次。
4. train.txt 例如,我的 train.txt 文件的一个片段如下所示:
*/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss734.png
/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss735.png
/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss736.png
/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss737.png
/content/gdrive/MyDrive/face_mask_detection/mask_yolo_train/maksssksksss738.png
...*
( 注: 正如我前面提到的。png 文件应该与它们对应的。txt 注解
因此,我们的项目结构如下:
***MyDrive**
├──**darknet**
├──...
├──**backup**
├──...
├──**cfg**
├──face_mask.cfg ├──...
├──**data**
├──face_mask.data
├──face_mask.names
├──train.txt
├──test.txt├──**face_mask_detection**
├──**annotations** *(contains original .xml files)* ├──**images** *(contains the original .png images)* ├──**mask_yolo_test** *(contains .png % .txt files for testing)* ├──**mask_yolo_train** *(contains .png % .txt files for training)*
├── show_bb.py
└── xml_to_yolo.py*
让我们开始训练吧📈
在我们编译模型之后,我们需要更改相关的权限,如下所示:
*!chmod +x ./darknet*
最后,我们可以从跑步开始训练:
*!./darknet detector train data/face_mask.data cfg/face_mask.cfg backup/face_mask_last.weights -dont_show -i 0 -map*
标志-map 会通过打印出平均丢失、精度、召回、平均精度(AP)、平均精度(mAP)等重要指标来通知我们训练的进度。

作者图片
然而,控制台中的 mAP 指示器被认为是比损耗更好的指标,所以在 mAP 增加时进行训练。
( 注: 训练过程可能需要 多个小时……这是正常的。对于这个项目,为了训练我的模型到这一点,我需要大约 15 个小时。但是我在大约 7 个小时内完成了 4000 个步骤的训练,对这个模型有了第一印象。
测试(和讨论)时间到了🎉
是的…模型已经准备好展示了!!!让我们尝试一些它从未见过的图像。为此,我们需要运行:
*!./darknet detector test data/face_mask.data cfg/face_mask.cfg backup/**face_mask_best.weights***
你是否注意到我们使用了 face_mask_best.weights 而不是face _ mask _ final . weights?幸运的是,我们的模型将最佳权重(map . 587.16%被达到)保存在备份文件夹中,以防我们对其进行比它应该的更多的时期的训练(这可能会导致过度拟合)。
下面显示的示例取自像素,是高分辨率图像,用肉眼看,我可以说它们与来自不同点的训练/测试数据集非常不同,因此它们具有不同的分布。我选择这样的图片是为了看看这个模型的概括能力有多强。



(左)模特对一张照片的预测夏洛特梅来自派克斯 | (中)模特对一张照片的预测蒂姆道格拉斯来自派克斯 | (右)模特对一张照片的预测来自安娜什韦茨来自派克斯
在上述示例中,模型是准确的,并且对其预测相当有信心。值得注意的是,右边的图像没有将模型与地球上面具的存在混淆。它揭示了这些预测不仅仅是基于面具的存在,还基于它周围的环境。
**
(左)模特对一张照片的预测金德尔传媒发自佩克斯 | (右)模特对一张照片的预测亚历山大·帕萨里克发自佩克斯
这两个例子清楚地表明,被描绘的人没有戴面具,而且模特也很容易识别这一点。
**
(左)模特对一张照片的预测诺玛·莫滕森来自派克斯 | (右)模特对一张照片的预测生活要紧来自派克斯
在上面的两个例子中,我们可以在两个类别都出现的情况下测试模型的性能。该模型甚至可以在模糊的背景中识别人脸,这一事实令人钦佩。我还观察到,与其后面的预测(在模糊区域上为 100%)相比,其不太确定的最前面的预测(在清晰区域上仅为 38%)可能与被训练的数据集的质量有关,因此它似乎在一定程度上受到影响(至少它不是不准确的😅 ).
最后一次测试🐵
当然,YOLO 的一大优势是它的速度。出于这个原因,我还想向您展示它在输入视频时是如何工作的:
*!./darknet detector demo data/face_mask.data cfg/face_mask.cfg backup/face_mask_best.weights -dont_show vid1.mp4 -i 0 -out_filename res1.avi*

作者对视频流、图像的推断
结论👏
这是我的第一个分步教程,介绍如何在自定义数据集上使用 YOLOv3 构建自己的检测器。希望你觉得有用。请随时给我反馈或询问任何相关问题。
非常感谢您的宝贵时间!一会儿见…😜
脸书人工智能相似性搜索
原文:https://towardsdatascience.com/facebook-ai-similarity-search-7c564daee9eb?source=collection_archive---------11-----------------------
FAISS 简单指南

在 Unsplash 上 NeONBRAND 拍摄的照片
准确、快速且节省内存的相似性搜索是一件很难做到的事情——但如果做得好,它非常适合我们无穷无尽(并呈指数增长)的庞大数据仓库。
相似性搜索如此之好的原因是,它使我们能够搜索图像、文本、视频或任何其他形式的数据——而不会在我们的搜索查询中变得过于具体——这是我们人类不太擅长的事情。
我将使用图像相似性搜索的例子。我们可以拍一张照片,然后搜索相似的图片。其工作原理是首先将每张图像转换成一组自动生成的特征,这些特征用数字向量表示。
当我们比较两幅相似图像的矢量时,我们会发现它们非常相似。
现在,如果我们拍照,并搜索其他类似的图像——我们真的不想将我们的查询向量与数据库中的每一个向量进行比较。想象一下用谷歌图片搜索来做这件事——你可能要等上一段时间。
相反,我们希望找到一种更有效的方法——这正是脸书人工智能相似性搜索(FAISS)提供的方法。
向量
我们高效相似性搜索的故事从向量开始——事实上有很多向量。
如果我们取三个向量:

我们可以很有把握地说,矢量 a 和 b 比 c 彼此更接近。这可以用一个简单的三维图表来显示:

在我们的例子中,我们的向量有更多的维度——多到无法想象。但是我们仍然可以使用相同的距离度量来计算两个高维向量之间的接近度和/或相似度。其中一些是:
- 欧几里德距离(度量大小)

- 点积(测量方向和大小)

- 余弦相似性(测量方向)

FAISS 利用欧几里得距离和点积来比较向量。
给定我们的矢量 a ,和 b 。我们将欧几里德距离计算为:

欧几里德距离计算
近似最近邻居
给定我们的欧几里德距离度量,我们可以使用一组向量来创建最近邻(NN)图。
NN 是 FAISS 的重要组成部分,它是我们在索引中构建核心‘距离’属性的方式。然而,由于 维数灾难 ,NN-search 的计算量很大。
该过程包括计算两个向量之间的欧几里德距离,然后是另外两个向量,依此类推——最近的邻居是它们之间距离最短的那些。
神经网络搜索最基本的方法是强力穷举搜索——计算所有元素之间的距离。
现在,如果我们有几百万个索引向量,或者十亿个(FAISS 就是为这个数字而构建的),这可能会成为一个繁重的操作,我们会为了准确性而牺牲速度和内存效率:
**Brute Force**Good Accuracy
Bad Speed
Bad Memory (eg a lot of memory is required)
因此,我们可以执行非穷举搜索,不搜索索引中的所有元素和/或转换向量以使它们更小(因此比较起来更快)。
因为这种非穷举(近似)NN 搜索使用稍微修改的向量或受限的搜索区域,所以它返回一个近似最佳结果,而不一定是绝对最佳结果。
**Approximate NN**Reasonable Accuracy
Good Speed
Good Memory
然而,这种精度下降非常轻微,被视为性能优势的公平权衡。
FAISS 如何提高搜索效率
首先,FAISS 使用了我们已经学过的所有智能 ANN 图形构建逻辑。但是失败的不止这些。
第一个效率节省来自于 GPU 的有效使用,因此搜索可以并行而不是串行处理计算,从而大大加快速度。
此外,FAISS 在索引过程中执行三个附加步骤。预处理步骤,随后是两个量化操作——用于倒排文件索引(IVF)的粗量化器,以及用于矢量编码的细量化器。
对我们大多数人来说,最后一段是无意义的胡言乱语——所以让我们分解每一步,理解每一步的意思。
预处理
我们用我们希望 FAISS 索引的向量进入这个过程。第一步是将这些向量转换成更友好/有效的格式。FAISS 在这里提供了几种选择。
- PCA——使用主成分分析来减少向量的维数。
- L2 范数——L2——归一化我们的向量。
- OPQ —旋转我们的向量,以便它们可以由精细量化器更有效地编码—如果使用乘积量化(PQ)的话。
- 填充(pad )-用零填充输入向量,直到给定的目标尺寸。
这个操作意味着,当我们将查询向量与已经嵌入的向量进行比较时,每次比较都需要更少的计算,从而使事情变得更快。
倒排文件索引
下一步是我们的倒排文件(IVF)索引过程。同样,有多种选择——但每一种都旨在将数据划分到个相似的簇中。
这意味着当我们查询 FAISS 时,我们的查询被转换成一个向量——它将与这些分区/聚类质心进行比较。
我们将相似性度量与我们的查询向量和每个质心进行比较——一旦我们找到最近的质心,我们就可以访问该质心内的所有完整向量(并忽略所有其他向量)。
我们立即显著缩小了所需的搜索区域,降低了复杂性,加快了搜索速度。
这也被称为“非穷举”搜索组件——例如,允许我们避免“穷举”(比较 一切 )搜索的组件。
向量编码
最后一步是对每个向量进行索引之前的最后一个编码步骤。这个编码过程由我们的精细量化器执行。这里的目标是减少索引内存大小并提高搜索速度。
有几个选项:
- 平面-矢量按原样存储,不进行任何编码。
- PQ —应用产品量化。
- SQ —应用标量量化。
值得注意的是,即使使用平面编码,FAISS 仍然会非常快。
所有这些步骤和改进结合起来,创建了一个令人难以置信的快速相似性搜索引擎——在 GPU 上仍然是无与伦比的。
关于 FAISS 的这篇文章到此为止!FAISS 是一个非常酷的工具,你可以在这里阅读更多信息,在这里阅读 FAISS repo。
你可以在本笔记本中找到 FAISS 的 Python 实现。
我希望你喜欢这篇文章。如果你有任何问题或建议,请通过推特或在下面的评论中告诉我。如果你对更多类似的内容感兴趣,我也会在 YouTube 上发布。
感谢阅读!
来源
🤖带变压器的 NLP 课程
*除非另有说明,所有图片均出自作者之手
脸书和微软数据科学 SQL 面试问题
原文:https://towardsdatascience.com/facebook-and-microsoft-data-science-sql-interview-questions-175a3e0fdf43?source=collection_archive---------11-----------------------
这是来自脸书和微软的一个硬数据科学 SQL 面试问题,测试你查找和细分用户以及将聚合表连接在一起的能力。让我们像在面试中一样解决这个问题。

由卢卡·布拉沃在 Unsplash 上拍摄
脸书和微软的数据科学职位竞争激烈,很难找到。在工作中编写 SQL 将涵盖你在实践中要处理的所有问题。在本文中,我们将演练如何解决来自微软和脸书的数据科学 SQL 面试问题。我们还将为您提供一些关于如何在此类数据科学面试中找到解决方案的提示。
作者通过 YouTube 发布的视频
数据科学 SQL 面试问题
新老用户 计算新老用户的份额。以比率形式输出月份、新用户份额和现有用户份额。
新用户定义为当月开始使用服务的用户。现有用户是在当月开始使用服务并在之前的任何一个月使用过服务的用户。
假设日期都是从 2020 年开始的。
问题链接:https://platform.stratascratch.com/coding-question?id=2028
解决这个面试问题的五步框架
这个框架可以用于任何数据科学 SQL 面试问题。这里有一个解决问题时你应该采取的步骤的列表,无论是在面试还是在工作中。
- 探索数据
a) 只有在我们工作的时候,因为大多数时候你不会有可执行的想法清单假设 - 列出假设有助于你缩小解决方案的范围。它将帮助您识别边缘情况,并将您的解决方案限制在您的假设范围内。
- 概述方法
a) 把方法一步一步地写下来。每一步都是一个逻辑语句或业务规则。
b)与面试官确认这些步骤。通常,如果面试官发现了一些错误,他们会提出来,这很好,因为你甚至可以在写一行代码之前就解决问题。
c)如果你愿意,你可以提到你实现的解决问题的功能。 - 增量编码
a) 将解决方案分成逻辑部分,并就每个部分进行沟通,这样面试官就知道你在做什么。b)你的代码需要具有逻辑性和良好的结构。最重要的一点是,不要为每个“代码块”放置超过 1 个逻辑语句或业务规则。代码块可以定义为 CTE 或子查询,因为它是自包含的,并且与代码的其余部分相分离。 - 优化代码这通常只是与面试官的一次谈话,但在工作中,如果代码会被重复使用,你通常会重构代码以提高运行效率。
数据探索
表格模式:

图片来自 StrataScratch
数据集:

图片来自 StrataScratch
当我们研究数据时,我们可以看到“time_id”是 YYYY-MM-DD 中的一个日期,因此我们可以从该日期字段中提取月份,以查找在该给定月份使用服务的用户。
“user_id”是一个常规的用户列表,将用于统计每月使用该服务的用户数量。
“event_type”确实是一种服务类型。我们可以忽略此列,因为我们正在考虑所有服务。在对面试官的假设中说明这一点很重要。如果您正在寻找特定的事件和服务,您需要在您的解决方案中使用这个专栏。幸运的是,我们不需要这么做,否则解决方案会复杂得多。
假设
- Time_id 表示用户何时使用服务。所有数据都是 2020 年的。这将有助于我们识别新用户和现有用户。
- 我们只需要 User_id 来识别用户。
- 用户每次使用服务时,都会记录在表中,因此用户会在表中多次列出
- Event_type 是服务,但这不是解决方案所必需的,因为我们考虑的是所有服务或事件
大纲方法
在开始编码之前概述你的方法是非常重要的。写出你的方法的主要好处是从面试官那里得到确认,这些步骤是正确的。如果面试官发现你的方法或逻辑有问题,那么你当然可以在写代码之前改正它们。
现在,让我们来看看这个数据科学 SQL 面试问题的方法:
- 找到新用户,新用户被定义为第一次开始使用服务的用户。我可以使用 min()来查找用户第一次使用服务的日期。
- 按月计算所有使用过服务的用户。一旦我们减去新用户,这将为我们提供现有用户。
- 按月加入新用户和所有用户表。
- 通过将新用户数除以所有用户数来计算份额。计算现有用户的份额仅仅是 1 和新用户的份额之差。
增量编码
现在,在你的方法中应该有以下步骤。对它们中的每一个进行编码,并在现有代码的基础上构建,然后检查查询是否会在每次添加逻辑时运行。以下是步骤:
- 计算新用户
- 计算所有用户
- 连接表格
- 计算用户份额
让我们来完成这些步骤。
1。找到新用户,新用户被定义为第一次开始使用服务的用户。
我们可以通过从“time_id”列中找到每个用户的最小日期来找到它,这给了我他们开始使用服务的日期。
SELECT user_id,
min(time_id) as new_user_start_date
FROM fact_events
GROUP BY user_id
2。通过从日期中提取月份并计算唯一用户数,按月计算新用户数
SELECT date_part('month', new_user_start_date) AS month,
count(DISTINCT user_id) as new_users
FROM
(SELECT user_id,
min(time_id) as new_user_start_date
FROM fact_events
GROUP BY user_id) sq
GROUP BY month
要按月聚集用户,我们可以使用 date_part()函数并从“time_id”中提取月份。知道所有的日期都在 2020 年内是有帮助的,因为我知道所有的月份都来自 2020 年。如果我们在这个数据集中有几年,我们不能使用 date_part(),因为不同年份的月份会混在一起。我们必须使用 to_char()函数并按 MM-YYYY 聚合数据。
3。计算每个月的所有用户(现有用户和新用户)
一旦我们减去新用户,这将为我们提供现有用户。
SELECT date_part('month', time_id) AS month,
count(DISTINCT user_id) as all_users
FROM fact_events
GROUP BY month
4。按月份将两个表连接在一起
with all_users as (
SELECT date_part('month', time_id) AS month,
count(DISTINCT user_id) as all_users
FROM fact_events
GROUP BY month),
new_users as (
SELECT date_part('month', new_user_start_date) AS month,
count(DISTINCT user_id) as new_users
FROM
(SELECT user_id,
min(time_id) as new_user_start_date
FROM fact_events
GROUP BY user_id) sq
GROUP BY month
)
SELECT
*
FROM all_users au
JOIN new_users nu ON nu.month = au.month
5。计算用户份额
with all_users as (
SELECT date_part('month', time_id) AS month,
count(DISTINCT user_id) as all_users
FROM fact_events
GROUP BY month),
new_users as (
SELECT date_part('month', new_user_start_date) AS month,
count(DISTINCT user_id) as new_users
FROM
(SELECT user_id,
min(time_id) as new_user_start_date
FROM fact_events
GROUP BY user_id) sq
GROUP BY month
)
SELECT
au.month,
new_users / all_users::decimal as share_new_users,
1- (new_users / all_users::decimal) as share_existing_users
FROM all_users au
JOIN new_users nu ON nu.month = au.month
如果我们运行这个查询,我们现在就有了新用户和现有用户每月的份额数。
输出:

图片来自 StrataScratch
这里需要注意的一点是,现有用户的计算方法是从新用户的份额中减去 1。此外,请记住在执行除法之前,将份额计算转换为小数或浮点数据类型。
最佳化
一个经常被问到的问题是,是否有优化代码的方法。面试官在测试你的 SQL 理论,所以即使没有办法优化代码,你也应该说点什么。
看看我们的解决方案,没有办法进一步优化这些代码。
有时,您可以使用 case 语句删除一个连接,但这在我们的方法中不起作用,因为我们需要标识整个数据集的最小日期,case 语句将逐行执行。因此,您需要一个子查询或 CTE 来执行该操作。
您还需要执行相同的子查询/CTE 操作来按月份查找所有用户,因此我们无法优化这部分代码或两个子查询的连接。
即使没有办法优化代码,但你仍然放弃了一些 SQL 知识,这将通过面试官对你是否了解 SQL 理论的评估。
结论
这是一个很难回答的数据科学 SQL 面试问题,因为你试图找到用户第一次使用服务的时间。使用 min()函数作为识别新用户的方法并不总是显而易见的。您还使用了高级 SQL 函数从日期字段中提取日期成分,比如月份,这使得问题变得更加复杂。但是有必要学习如何在数据科学中操作日期,因为大多数分析都有日期成分。
这个数据科学 SQL 面试问题的诀窍是使用一个框架来组织你的想法,因为有多个步骤。一旦你提出你的假设,比如知道所有的日期都是从 2020 年开始的,并且你正在考虑你的解决方案中的所有服务,这个问题就变得容易解决了。你所需要做的就是用逻辑和简洁的步骤来组织你的方法,并编写代码。
在编码前练习框架,练习整理思路,回答复杂的问题会变得容易很多。
- 如果你对数据科学面试问题和解释感兴趣,可以查看 StrataScratch。
- 在我的 Youtube 频道 上观看面试问题的解释和技术话题的深度指导。
- 要获得更多有用的资源,请查看我的其他文章,如**脸书面试问题指南 和 成为数据科学家的必备数据科学技能。
最初发表于【https://www.stratascratch.com】。
脸书数据科学家采访指南
原文:https://towardsdatascience.com/facebook-data-scientist-interview-guide-edb331a78c7c?source=collection_archive---------28-----------------------
我们将为您带来一份详细的分析,告诉您当您获得梦寐以求的脸书数据科学面试机会时,您会有什么样的期待

作者创造的形象
脸书是世界上最大的上市公司之一,也是全球技术领导者。话虽如此,脸书成为数据科学的领导者并吸引了世界上一些最有技能的数据科学家也就不足为奇了。根据他们的网站,“脸书的数据科学家进行大规模、全球性的定量研究,以更深入地了解人们如何相互交流以及他们周围的世界。”
作为领先的科技巨头和 FAANG 公司之一,脸书将数据科学置于其活动的核心也就不足为奇了。我们通过使用脸书进行的所有这些图片、视频、消息、喜欢、评论和许多其他活动都会被不断地存储和分析,以便改进脸书的产品(及其所有附属公司),并保持该公司在过去十年中享有的社交媒体市场主导地位。
考虑到收集到的大量数据,以及脸书在数据科学上的主导地位和重视程度,我们决定进行分析,并提供一份关于数据科学面试流程的指南。我们在脸书收集了近 200 个真实的采访问题,我们为你带来了一个详细的分析,告诉你当你得到你梦寐以求的脸书的采访时,你应该期待什么。
分析的描述和方法
本文的目标是分析在脸书数据科学面试中被问到的各类问题。此外,本文将分析这些问题中出现的最突出的技术概念,以帮助读者(通过示例)理解我们数据库中许多问题的背景和方法。
在我们博客的早些时候,我们发表了一篇文章,对过去 4 年从 80 家不同公司收集的 900 多个数据科学面试问题进行了分析。在该分析中,脸书是提问最多的公司,也是高度重视编码问题的领先组织之一。
在过去的 4 年里,我们从 Glassdoor、Reddit、Blind App 和 Indeed 等来源收集了总共 193 个真实的脸书数据科学家采访问题。以下是我们团队对问题的分类:

作者创造的形象
我们研究中的问题类型数据是通过将问题分成预先确定的类别而生成的。这些类别是由我们的专家对采访经历描述进行分析后得出的。谈到脸书,我们的团队已经确定了八个不同类别的问题。它们是:算法、商业案例、编码、建模、概率、产品、统计和系统设计。我们将在文章中更详细地解释这些类别。
脸书大学的数据科学面试
在这里,我们将更详细地介绍我们的分类方法,并解释如何选择和构建类别。此外,我们将分析所有问题,看看哪些类别在脸书数据科学面试过程中更普遍。最后,我们将通过现实生活中的例子来了解脸书面试中每个类别的问题。这将有助于你对脸书面试有一个大致的了解,也给你一些你可能会被问到的问题的具体知识。
脸书问题类型细分
让我们来看看为本文收集的所有脸书面试问题:

作者创造的形象
正如我们之前提到的,正如我们在图表上看到的,脸书非常重视数据科学编码面试问题;这一类别几乎占了我们数据库中所有问题的一半(46%)。既然如此,为了帮助你更好地准备脸书大学的面试,我们将在文章中更多地强调这类问题。另外两个突出的类别是算法和产品,分别占所有问题的 17%和 15%。统计学占我们数据库中所有问题的 6%,这是有 10 多个例子的列表中的最后一个类别,正如你在上一节的图表中看到的。系统设计占 5%,商业案例和概率各占 4%,而建模是最不占优势的类别,3%的此类问题在脸书数据科学面试中被问到。
编码
根据我们的分类标准,我们将编码问题定义为所有类型的问题,包括通过代码进行分析和数据操作。这种通过代码的操作通常是使用数据科学中一些最流行的语言来完成的,比如 SQL(最常见的)、Python 和 r。
脸书特有的编码问题示例如下:
“了解脸书的高级员工(即更有经验的员工)数量是否高于美国的员工数量。如果老年人的数量更高,则输出为“更多老年人”。否则,输出为‘更基于美国’。”

截图来自 StrataScratch
我们将在 Postgres 中讨论这个问题的可能的解决方案,这样你可以在面试中更熟悉如何处理类似的问题:
- 首先,使用两个内部查询来查找老年人的数量和美国雇员的数量;
- 然后用公式 SUM(CASE WHEN … THEN 1 ELSE 0 END)统计学长人数;
- 之后,使用 COUNT()函数对美国的雇员进行计数。考虑在美国工作的员工的“地点”列的值为“美国”;
- 使用 WHERE 子句来应用条件;
- 然后,在 TRUE 上使用 LEFT JOIN 合并两个查询的结果;
- 最后,使用公式 CASE WHEN … THEN … ELSE … END 找到最终输出。
以下是来自我们平台的 Postgres 的完整解决方案:
SELECT
CASE
WHEN n_seniors > n_usa_based
THEN 'More seniors'
ELSE 'More USA-based'
END AS winner
FROM
(SELECT
SUM(CASE WHEN is_senior THEN 1 ELSE 0 END) AS n_seniors
FROM
facebook_employees) seniors
LEFT JOIN
(SELECT
COUNT(*) AS n_usa_based
FROM
facebook_employees
WHERE
location = 'USA'
) us_based
ON TRUE
由于编码是所有面试问题中最常见的一类,让我们再看几个与编码相关的例子。一个这样的例子是:
“计算 2019 年 3 月每个客户的总收入。每个订单的收入通过 order_quantity 乘以 order_cost 来计算。输出收入和客户 id,并根据收入按降序对结果进行排序。

截图来自 StrataScratch
一个可能的解决方案可以在这里找到。
这一类别的最后一个例子是:
“找出脸书上每个用户的受欢迎程度。人气百分比定义为用户拥有的好友总数除以平台用户总数,再乘以 100 转换成百分比。输出每个用户及其受欢迎的百分比。按用户 id 升序排列记录。“用户 1”和“用户 2”列是朋友对。

截图来自 StrataScratch
这个问题的答案可以在这里找到。
脸书数据科学公司的面试中会问一些编码问题,以测试候选人解决问题的能力、对所用编程语言的技术知识以及创造力;所有这些都是你在脸书每天最可能需要的。正如我们在上一节中看到的,知道如何回答编码问题的重要性怎么强调都不为过,因为它们几乎占所有面试问题的一半。
点击此处查看更多最近在脸书面试中问到的数据科学面试问题— 脸书数据科学面试问题
算法
关于算法的问题被归类为需要解决数学问题的所有问题,主要是通过代码,使用上面提到的一种编程语言。这些问题涉及一步一步的过程,通常需要调整或计算才能得出答案。你可能会被要求解释你的答案或者实际执行代码。
算法问题的一个例子是使用 Python 或 r 计算一个数的平方根。这些问题对于测试解决问题和数据操作的基本知识非常重要,这些知识可以用于解决工作中的复杂问题。正如我们在上图中看到的,在脸书的面试中,与算法相关的问题占所有问题的 17%。
产品
与产品相关的访谈问题被归类为需要通过数据评估产品/服务的所有问题。这些类型的问题旨在测试候选人是否能够将数据科学原理应用于现实生活中的日常问题,即脸书作为一家公司所经历的问题。下面是一个针对脸书的产品问题的例子:
“如果 iOS 上 70%的脸书用户使用 Instagram,而 Android 上只有 35%的脸书用户使用 Instagram,你会如何调查这种差异?”

截图来自 StrataScratch
正如我们在上面的图表中看到的,产品问题占脸书面试中所有问题的 15%。由于脸书的产品非常复杂,而且数据丰富,这是完全有道理的。能够回答与产品相关的问题是非常重要的,因为这显示了你对脸书产品的了解,以及你适应公司日常工作的难易程度。
统计学
统计面试问题被归类为所有需要统计理论和相关概念知识的问题。问这些问题是为了测试受访者对数据科学任务中使用的基本理论原理的了解程度。
下面是一个来自我们平台的问题的例子,归类在统计下:
“方差的期望值是多少?”

截图来自 StrataScratch
回答这个问题的一种方法是这样的:
- 设 X 为随机变量;
- 我们知道 E(a) = a,如果 a 是常数
- 我们也知道 E(E(X)) = E(X),因为 E(X)是常数。
- 既然 Var(X) = E(X ) — E(X),那么:
E(Var(X))\ \ = E(E(X)—E(X))\ \ = E(E(X))—E(E(X))\ \ = E(X)—E(X)—E(X)\ \ = Var(X)
另一个统计相关问题是:
“在墨西哥,如果取平均值和年龄中位数,哪个会高,为什么?”

截图来自 StrataScratch
与统计相关的问题非常重要,因为能够理解分析的理论和数学背景是每个公司都在寻找的东西。这表明候选人对手头的问题有更深入的了解,而不仅仅是解决问题的实践经验。
系统设计
系统设计问题被归类为所有与设计技术系统相关的问题。问这些问题是为了分析你在解决问题和创建系统以使顾客/客户受益时的个人过程。来自我们平台的系统设计问题的一个例子是:
“给定脸书成员在脸书上互为好友/解除好友的数据,找出给定的一对成员当前是否是好友。”

截图来自 StrataScratch
了解系统设计对于数据科学家来说非常重要;即使你的角色不是设计一个系统,你也很可能在一个已建立的系统中扮演一个角色,并且需要知道它是如何工作的,以便履行你的日常职责。
概率
我们对概率问题的分类是所有只需要概率论和概念的理论知识的问题。面试官会问一些与概率相关的问题,以了解你在完成通常在工作场所执行的复杂数据任务的方法和概率使用方面的知识。
一个概率问题的例子是:
“从 52 张牌中抽出不同颜色或形状的牌的概率是多少?”

截图来自 StrataScratch
商业案例
业务案例问题已被分类为涉及案例研究的问题以及与业务相关的通用问题,这些问题将测试一些数据科学技能。在脸书的面试中,他们只占所有问题的 4%;然而,它们可能非常重要,因为你可能会被问到一个与公司运营特别相关的商业案例。如果你能回答这个问题,你就能解决脸书在被雇佣前面临的问题。
下面是一个针对脸书的商业案例问题的示例:
“你如何将脸书用户名字中使用的昵称(皮特、安迪、尼克、罗伯等)映射到真实姓名?”

截图来自 StrataScratch
知道如何回答这些问题的重要性是巨大的,因为大多数面试官希望候选人在雇佣他们之前知道如何应用数据科学原理来解决脸书的具体问题。
建模
建模类下的面试问题都是回归和机器学习相关的问题。尽管建模问题在所有问题中所占的比例最低(3%),但根据您的角色,它们可能至关重要。事实上,对于脸书的一些职位来说,构建模型可以构成你的大部分任务。
我们平台上的一个建模问题的例子是:
“你如何测试现在拥有更多朋友是否会增加脸书会员在 6 个月后仍然是活跃用户的可能性?”

截图来自 StrataScratch
在脸书数据科学访谈中测试的技术概念
在本节中,我们将讨论一些在脸书数据科学访谈中测试过的最常见的技术概念。正如我们从分析中看到的,脸书非常重视编码问题,以及通常包含编码成分的算法问题。考虑到这一点,难怪最突出的技术概念来自这些领域。
连接
在脸书数据科学访谈中测试的最常见的技术概念是使用连接(在 SQL 中)来合并表和列,以便对数据执行进一步的分析。下面是我们平台上这类问题的一个例子:
“当用户尝试通过 2FA(双因素认证)登录平台时,脸书会发送短信。为了成功 2FA,他们必须确认他们收到了 SMS 文本消息。确认文本仅在发送之日有效。不幸的是,数据库存在 ETL 问题,好友请求和无效的确认记录被插入到日志中,这些日志存储在“fb_sms_sends”表中。这些消息类型不应出现在表中。幸运的是,“fb_confirmers”表包含有效的确认记录,因此您可以使用该表来识别由用户确认的 SMS 文本消息。计算 2020 年 8 月 4 日确认短信的百分比。”

截图来自 StrataScratch
要回答这个问题,您需要过滤掉“fb_sms_sends”表中的无效确认和好友请求记录。您将使用电话号码和日期键来连接表;需要一个左连接来保存发送的消息总数的计数。因为您正在处理整数,所以需要在 SELECT 子句中将数据类型转换为浮点数或小数。详细回答(有代码)可以在这里找到。
案例陈述
在脸书数据科学访谈中,另一个经常被提及的重要技术概念是 CASE 语句的使用(及其所有变体)。例如,你可能会被问到这样一个问题:
“通过基于‘喜欢’倾向建立‘喜欢’分数,发现‘喜欢’的数量是如何增加的。“喜欢”倾向被定义为每个朋友在所有反应中给出喜欢的概率(即,喜欢的数量/所有反应的数量)。在相应的日期和海报旁边输出总倾向。根据喜欢分数按降序对结果进行排序。在“facebook_reactions”表中,“poster”是发布内容的用户,“friend”是看到内容并做出反应的用户。“facebook_friends”表存储了成对的好友。”

截图来自 StrataScratch
这个问题的答案(使用 CASE WHEN 语句)可以在这里找到。
UNION ALL 运算符
我们将在本文中讨论的脸书数据科学访谈中最后一个流行的技术概念是 UNION ALL 操作符。这个概念主要用于组合某些数据集,以便同时对所有数据集进行分析。这样一个问题的例子:
“找出脸书数据中心总能耗最高的日期。输出数据以及所有数据中心的总能耗。”

截图来自 StrataScratch
这个问题的答案可以在我们的平台上找到。
结论
本文对脸书数据科学面试问题的全面分析旨在帮助你成为脸书数据科学团队的一员。我们设法将所有 193 个问题分为 8 个不同的部分,并对每个类别进行了详细的检查,同时对问题本身进行了总体分类。为了更好地理解面试过程,我们介绍了经过测试最多的技术概念,以及面试问题的具体示例。编码问题受到了特别的重视,因为它们几乎占了脸书面试中所有问题的一半。
随着脸书不断发展并保持其社交媒体市场的主导地位,可以理解为什么该公司不断扩大其在世界各地的数据科学团队,以及为什么他们通过每次招聘吸引更多的应用程序。我们希望这篇文章在你成为脸书数据科学家的过程中成为一个有价值的资源,我们祝你在未来的工作中一切顺利。
原载于https://www.stratascratch.com。
脸书·戴伊特:最先进的图像分类,卷积数为 0,不到最先进数据集的 1%
原文:https://towardsdatascience.com/facebook-deit-a-promising-new-technique-for-image-classification-6516f039b4bb?source=collection_archive---------35-----------------------
一种有前途的图像分类新技术

布雷特·乔丹在 Unsplash 上的照片
几周前,脸书发布了一个新的 ML 模型(数据高效图像转换器,DeIt),它仅使用 ImageNet 数据集(120 万张图像)就实现了最先进的图像分类性能。最先进的视觉变形金刚只有使用数亿张图像才能达到这种性能1。脸书如何实现这一点是最有趣的,因为他们没有使用任何卷积或大型数据集。
【DeIt 是如何工作的?
几乎每天都有很多很棒的机器学习论文发布。我选择这个来回顾的原因是它使用了一些有趣的技术。
其中一个技巧是注意力和变形金刚,我不想详细介绍,因为有很多关于它们的文章。然而,我将给出一个快速的概述,以便我们可以适当地探索 DeIt。
视觉变形金刚
在过去的几年里,变形金刚和注意力一直主导着机器学习领域。他们从 NLP 开始,现在转向图像。
视觉变形金刚使用多头自我关注层。这些层基于注意机制,该机制利用查询、键和向量来“注意”来自不同位置的不同表示的信息。
一个经典的图像变换模块从一个普通的前馈网络开始,然后是一个多头自关注层。一个有趣的地方是,前馈网络使用了一个名为高斯误差线性单元的激活函数,其目的是通过将一些激活随机乘以 0 来正则化模型。
本文解决了可视转换器的一些问题,例如:
- 它在 3 亿张图像上进行了训练(JFT-300M 1)
- 这 3 亿张图片是一个私人数据集
- 它不能很好地概括。
好了,现在我们已经介绍了基础知识,让我们开始看看这篇论文的特别之处。
新推出的绝招:蒸馏令牌。什么是蒸馏?

在 Unsplash 上科学高清拍摄的照片
知识提炼指的是模型压缩的思想,通过一步一步地教会一个较小的网络,使用一个更大的已经训练好的网络来做什么。“软标签”指的是在每个卷积层之后由更大的网络输出的特征图。然后,较小的网络被训练来学习较大网络的确切行为,尝试在每个级别复制其输出(不仅仅是最终损失)。
来源:普拉卡·加内什
这很有趣,就像在现实世界中我们有老师一样,在 ML 中我们有更大更小的网络模仿更大的网络向他们学习。
典型的视觉变形器使用称为类标记的可训练向量的概念。这种令牌试图取代卷积神经网络中的传统池层。它提高了模型的性能,并扩展了图像补片中的信息。
脸书添加了一个提取令牌,它在开始时与这个类令牌和其他初始嵌入进行交互,以增强模型的自我关注机制。该令牌是在训练期间学习的可训练向量。
其目标是最小化教师的 softmax 和学生模型的 softmax 之间的 Kullback-Leibler (KL)散度(这被称为软蒸馏)。关于 KL 散度,你需要知道的是,它衡量的是两个分布之间的差异。
所以本质上,这个蒸馏令牌试图最小化学生网络和教师网络的信息差异。这是一个相当令人印象深刻和新颖的策略!
他们还通过尝试添加一个类令牌(而不是提取令牌)来验证1这个新令牌的有用性。结果是更差的性能。
注意,这里的教师网络是一个卷积神经网络。
结果

来源: Github (Apache 2.0 许可证)
这篇论文最棒的一点是,脸书发布了完整的代码、数据集、论文,以及几乎所有的东西。他们发布了 3 种不同尺寸的不同型号。从图中可以看出,即使与最好的和最新的网络之一 EfficientNet 相比,它们的性能都相当好。
总之,我认为这是脸书成功的三大诀窍:
- 视觉变形金刚和注意力的力量
- 通过提取令牌用补丁嵌入替换单词嵌入
- 不依靠回旋
最终想法:
完美的模型是不存在的,我确信这个模型有一些瑕疵。然而,看看顶级人工智能研究人员在做什么还是挺有趣的。我希望你得到了蒸馏令牌技巧背后的直觉,这样你就可以在你的 ML 项目中发明你自己的技巧了!
我不想一头扎进数学(尽管我热爱数学),这样这篇文章才能适合更多的读者。如果你对此感兴趣,并想查看他们的更多结果,我建议看一看论文。
如果你想定期收到关于人工智能和机器学习的最新论文的评论,请在这里添加你的电子邮件并订阅!
https://artisanal-motivator-8249.ck.page/5524b8f934
参考文献:
1训练数据高效的图像转换器&通过注意力进行提炼。Hugo Touvron 和 Matthieu Cord 和 Matthijs Douze 和 Francisco Massa 和 Alexandre Sablayrolles 和 Hervé Jégou。2021 年 arxiv
脸书·DETR:变形金刚潜入物体探测世界
原文:https://towardsdatascience.com/facebook-detr-transformers-dive-into-the-object-detection-world-39d8422b53fa?source=collection_archive---------17-----------------------
变形金刚已经统治了自然语言处理和图像识别,现在又统治了物体检测

Samule 孙在 Unsplash 上的照片
最近大多数伟大的机器学习论文都是基于变形金刚。它们是强大有效的机器学习模型,已经证明它们值得花时间和精力来优化。最近,脸书发表了一篇新论文,使用变压器在对象检测方面优于最先进的更快的 RCNNs。
我们的方法简化了检测流程,有效地消除了对许多手工设计组件的需求。新模型在概念上很简单,不需要专门的库。我们表明,它明显优于竞争基线。训练代码和预训练模型可在 https://github.com/facebookresearch/detr.获得
来源: arxiv
本文研究了当前对象检测解决方案的弱点,如消除近似重复预测的后处理步骤1,并提出了使用变压器提供的注意机制的新解决方案。本文还介绍了一个端到端的框架,没有任何可定制的层,代码易于使用和复制。
我们都已经看到了变形金刚对自然语言处理和图像分类的影响,所以看到它被应用于物体检测是令人兴奋的,这通常比经典的图像识别更具挑战性。自我注意机制的力量有助于放松当前对象检测解决方案的约束。事不宜迟,让我们开始深入了解这个模型是如何工作的吧!
先决条件
在我们开始之前,我想介绍一些我认为非常有趣的基本思想,这些思想对于理解这个模型是必不可少的,并且通常也非常有用。
一个二部图(或二部图)是一个图,它的顶点可以分成两个不相交的和独立集合 U 和 V,使得每个边将 U 中的一个顶点连接到 V .顶点集合中的一个顶点,并且通常被称为图的部分。
来源:维基百科
第一个是二分匹配。这源于图论,而图论是计算机科学的基础之一。二分图匹配是指选择二分图中的一组边,使得没有两条边共享一个节点。如果你对图论不是那么感兴趣,只要理解图匹配算法在神经网络中可以是相当强大的。
第二个有趣的概念是锚盒。在目标检测中,主要有两种类型的模型:单阶段和两阶段。两阶段模型使用称为选择性搜索的算法从一幅图像(本质上是子图像)中产生一组提议,然后尝试使用经典 CNN 对每幅图像进行分类。然而,单级模型(更有效)使用了一个更新颖的概念,称为锚盒。
锚框与区域提议相反,它们是一组预定义的具有一定高度和宽度的边界框,网络使用它们来逼近它正在寻找的对象。虽然这听起来可能有点受限,但事实证明锚盒比区域提议更有效、更快速。
最后一个概念是非最大抑制(NMS) ,这是物体检测的基础。NMS 用于从许多重叠的框中选择一个边界框。因为想想看,如果你想找一只猫,会有几个盒子从多个角度盖住那只猫。然而,高质量的对象检测模型将使用 NMS,其计算框的并集 上的 交集,并从定义的阈值中选择最佳框。
好了,现在我们已经完成了理论,让我们开始吧。
端到端管道

照片由 JJ 英在 Unsplash 上拍摄
该模型从 CNN 开始提取特征,然后将这些特征馈送到转换器,然后将转换器的输出馈送到前馈网络,进行最终预测。我已经回顾了许多最近流行的论文,这条管道被频繁地修改,例如,它最近也被用在这里:
我们的损失在预测的和地面真实物体之间产生最佳的二分匹配,然后优化特定于物体(包围盒)的损失。
来源: arxiv
他们的主要损失函数是基于二分匹配,更具体地说是基于 匈牙利算法 ,这是一种“在多项式时间内解决分配问题的组合优化算法”[2]。我可以写一整篇关于匈牙利算法的文章(但我不会这么做)。这种方法的主要好处之一是,它只是产生一组预测而不是一个列表,这意味着产生的盒子是唯一的(这节省了大量的后期处理时间)。它还允许他们直接进行盒预测,而不是根据一些最初的猜测进行预测(当然还有一些正则化)。
至于第一步,CNN 被用来将图像的尺寸降低到最基本的尺寸,这被称为激活图。然后,1×1 卷积进一步降低这些维度,并且在折叠特征图之后,我们得到变换器期望作为输入的序列。但是在移动到转换器之前,图像还被补充了位置编码(以保留图像的结构)。
至于变形金刚,为了提高效率,他们调整了经典的变形金刚解码器模块,将对象并行解码,而不是顺序解码。这是可能的,因为转换器是排列不变的,并且因为我们已经传递了位置编码,所以即使在并行处理流水线中也可以安全地存储图像结构。
使用自身和编码器-解码器对这些嵌入的关注,该模型使用所有对象之间的成对关系来整体推理所有对象,同时能够使用整个图像作为上下文。
来源: arxiv
这是本文中介绍的我最喜欢的优化之一。他们不仅使用了现代模型,如变压器,而且他们还设法改进,使他们能够分解图像,并行处理它而不丢失它的结构。
结果

来源: arxiv
我不想谈论太多的结果,因为这都是数据,可以很容易地在论文中检查,但我认为可以肯定地说,结果相当好。他们还使用 Pytorch 实现了这篇论文,并在这里提供了代码。代码看起来并不冗长复杂,他们居然用不到 100 行代码在这里介绍了一个 demo !
最终想法
最近的对象检测模型最好的一点是,它们不需要你编写代码来训练你的模型,你只需运行“python train.py
如果你想定期收到关于人工智能和机器学习的最新论文的评论,请在这里添加你的电子邮件并订阅!
https://artisanal-motivator-8249.ck.page/5524b8f934
参考文献:
1利用变压器进行端到端物体检测。尼古拉斯·卡里翁、弗朗西斯科·马萨、加布里埃尔·西纳伊夫、尼古拉斯·乌苏尼尔、亚历山大·基里洛夫和谢尔盖·扎戈鲁伊科。
https://en.wikipedia.org/wiki/Hungarian_algorithm
脸书刚刚推出了最酷的增强库:Augly
原文:https://towardsdatascience.com/facebook-just-launched-the-coolest-augmentation-library-augly-3910c05db505?source=collection_archive---------11-----------------------
基于 Augly 的图像增强

左:原始图像,右:用 Augly 增强后(图像由作者创建)
脸书最近发布了一个增强库,在同一屋檐下结合了几种形式(音频、图像、视频和文本)。数据扩充是一种非常常见的技术,用于增加标记训练数据的大小和多样性,这也有助于建立稳健的模型。这里我只关注我在图像上测试的几个增强功能,但是这个库也可以用于文本和音频。
Augly 更致力于包括脸书在内的社交媒体平台的变革。因此,除了常见的裁剪、翻转之外,其他增强功能包括真人对图像/视频共享平台进行的非常逼真的转换,如叠加图像、表情符号、文本等。根据脸书的新闻稿,其中一个重要的应用是—
检测特定内容的精确副本或近似副本。例如,同一条错误信息可能会以稍微不同的形式重复出现,例如一张被裁剪了几个像素的图像,或者用滤镜或新文本覆盖进行了增强。通过用海量数据增强人工智能模型,它们可以学习发现何时有人上传已知侵权的内容,如歌曲或视频。
Augly 是为了阻止人们通过转换(阅读“增强”)数据来逃避自动检测系统而开发的,同一库也用于评估 deepfake 检测模型。
使用 Augly 进行图像放大:
以下是我在 8 月份用来测试一些图像增强功能的几个例子。要在 Colab 中尝试这个,首先,我们需要安装 Libmagic 和 Augly
!pip install augly!apt-get install libmagic-dev!pip install python-magicimport augly.image as imaugs
现在,我们准备使用 Augly,但在此之前,我们需要加载一个示例图像。让我们这样做
from skimage import datacamera = data.astronaut()
因为 Augly 使用的是 PIL 格式,所以我们来转换一下
from PIL import Imagecamera_pil = Image.fromarray(camera)camera_pil.show()

图 1:使用 Skimage 加载的原始图像。[此图片可在此处获得,并且没有已知的版权限制。]
这是我们最初的图像。顺便说一下,她叫艾琳·柯林斯,1992 年被选为宇航员,1995 年首次驾驶 STS-63 航天飞机。
首先让我们从一些相对简单的增强功能开始,比如改变视角
aug_perspective = imaugs.PerspectiveTransform(sigma=20.0)camera_pil_perspective = aug_perspective(camera_pil)camera_pil_perspective.show()

图 1 的透视变换(作者创建的图像)
这里的sigma是指目标坐标分布的标准偏差,并且sigma 值越高,变换越强烈。
我们也可以随机改变长宽比,如下所示—
aug_aspect = imaugs.RandomAspectRatio()camera_pil_aspect = aug_aspect(camera_pil)camera_pil_aspect.show()

在图 1 上随机改变纵横比(图片由作者创建)
我们也可以通过增加一些饱和度来增强图像
aug_saturation = imaugs.Saturation(factor=5.0)camera_pil_sat = aug_saturation(camera_pil)camera_pil_sat.show()

增加图 1 中原始图像的饱和度(由作者创建的图像)
现在让我们转向一些可以使用 Augly 添加的独特转换。首先,我试着叠加条纹,转换后的图像如下—

在原始图 1 上叠加条纹(图片由作者创建)。
用于上述转换的代码是—
camera_pil_overlay_stripes = imaugs.overlay_stripes(camera_pil, line_type=’dashed’, line_opacity=0.5, line_color=(120, 0, 200), line_angle=25.0)camera_pil_overlay_stripes.show()
叠加文字呢?这也是可能的—

在原图 1 上覆盖迷因文字(图片由作者创建)
我们也可以添加表情符号—
camera_pil_overlay_emoji = imaugs.overlay_emoji(camera_pil)camera_pil_overlay_emoji.show()

在原图 1 上叠加表情符号(图片由作者创建)。
我们还可以将图像转换成 Instagram 上的截图——

将 Instagram 截屏叠加在原始图 1 上(图片由作者创建)。
这个的代码块如下—
import osimport augly.utils as utilsaug_overlay = imaugs.OverlayOntoScreenshot(template_filepath=os.path.join(utils.SCREENSHOT_TEMPLATES_DIR, “mobile.png”))camera_pil_overlay = aug_overlay(camera_pil)camera_pil_overlay.show()
通过将叠加图像mobile.png更改为web.png ,我们可以得到另一个变换—

将 Instagram 网页的屏幕截图叠加在原始图 1 上(图片由作者创建)。
我个人认为,这种转变非常酷,正如脸书在新闻稿中提到的那样,它们有助于理解人类与社交媒体的互动。我相信对语音和语言处理感兴趣的人也会发现这个库非常有用。
感谢脸书将这个库开源,感谢乔安娜·比顿 & 佐伊·帕基波斯,我相信他们是这个项目背后的主脑。
你可以在我的笔记本里找到这里使用的所有代码,我把它们放在参考资料里了。保持坚强,干杯!!
页(page 的缩写)Augly library 的合著者之一 Joanna B. 在这篇文章中留下了非常友好的评论。所以,多亏了她!正如我在帖子中提到的,这里是她的 GitHub。
参考资料:
[1]8 月脸书新闻发布会
【2】八月:GitHub
3 链接到我的笔记本获取此处使用的代码。
脸书和微软数据科学家编码问题
原文:https://towardsdatascience.com/facebook-microsoft-data-scientist-coding-questions-94c8ae942a90?source=collection_archive---------21-----------------------

作者原创图片
对于所有有志成为数据科学家的人来说,编码测试通常是你在招聘过程中首先遇到的事情之一。任务通常因公司而异,但是一旦你进行了足够多的编码测试,你就会开始注意到类似的技能被测试。
今天,我们将探讨如何利用过滤、分组和聚合技术来回答脸书和微软编码测试中出现的一些真正的 Python 任务!
这是我们今天要分解的两项任务:
1.脸书:用户评论活跃度分布
2.微软:按日期进行用户分组分析
我以前也写过关于顶级 SQL 问题的文章,所以请在这里的(第一部分)和这里的(第二部分)查看它们!
这里有一个对 StrataScratch 的人们的大喊,它启发了今天的问题——如果你正在寻找一个你可能在编码测试中实际面对的现实生活问题的宝库,请检查它们!
问题 1:评论数量的分布
在这个问题中,我们有两个数据集:
fb_users ,以下列:
- id (int64)
- 名称(字符串)
- 加入时间(日期时间)
|----+----------+---------------------|
| id | name | joined_at |
|----+----------+---------------------|
| 1 | James | 2020-06-30 00:00:00 |
| 2 | John | 2020-06-29 00:00:00 |
| 3 | Zac | 2020-05-21 00:00:00 |
...
fb_comments ,以下列:
- user_id (int64)
- 正文(字符串)
- 创建时间(日期时间)
|---------+------------------+---------------------|
| user_id | body | created_at |
|---------+------------------+---------------------|
| 1 | wow, great... | 2020-07-01 00:00:00 |
| 2 | happy birthd... | 2020-06-28 00:00:00 |
| 3 | thank you eve... | 2020-06-22 00:00:00 |
...
根据 2018 年至 2020 年间加入脸书的用户数量,编写一个查询来计算 2020 年 1 月的评论分布。输出应该包含评论的数量以及在 2020 年 1 月发表评论的用户数量。例如,你可以计算有多少用户发表了 1 条评论、2 条评论、3 条评论、4 条评论等等。2020 年 1 月。输出中的左栏是评论的数量,右栏是用户的数量。从注释数量最少到最多对输出进行排序。
更复杂的是,可能会有一个 bug,用户帖子的日期在用户加入日期之前。您需要从结果中删除这些帖子。(例如,参考上面 user_id 2 的条目,其中注释日期在用户的加入日期之前)
当处理需要处理数据以呈现某种形式的分布的问题时,将任务分解成一系列步骤通常是有用的。许多受访者都熟悉在数据处理过程中需要的各种函数、连接和过滤器,但最具挑战性的是决定处理数据时要采取的步骤的确切顺序。我要先加入数据吗?还是我先过滤数据?这有关系吗?
向后工作
开始思考这类问题的一个好方法是逆向工作。我们的预期输出要求我们根据评论的数量返回用户的频率。首先忽略应该在问题的上下文中应用的条件和过滤器,我们知道,为了获得这个结果,我们首先需要获得一个包含每个用户的评论数量的先验表。为了创建这个中间表,我们需要获得一个 prior 表,其中包含每个用户的每个评论的一行。请注意,分发任务总是涉及在最后阶段应用 groupbys,这正是我们打算应用于每个中间数据集的。但是当我们逆向工作时,我们如何知道何时停止呢?
诀窍是问自己,已经反向推导的数据集是否已经在问题的上下文中直接可用,或者通过不涉及分组的某种形式的数据操作(如连接)间接可用。
在这个例子中,我们的中间数据集要求每个用户的每个评论占一行,结果我们已经有了原始数据集,只是没有应用必要的条件。这个数据集是 fb_comments ,每个评论占一行,但既不过滤 2020 年 1 月的评论,也不过滤 2018 年至 2020 年加入的用户的评论。
为了引入关于用户加入日期的信息,我们必须将 fb_users 数据集与 fb_comments 数据集合并,因为变量 joined_at 最初包含在用户数据集中。这里,我们使用一个内部连接来合并数据集,因为我们打算为每个评论保留每一行,即使用户在数据集中有多个评论。受访者通常会有这样的误解,即内部连接要求被连接的键(在本例中是各自数据集中的“id”和“user_id ”)分别是唯一的。这不是真的。事实上,现实中发生的事情是,当连接重复的键时,结果类似于您从交叉连接、或笛卡尔积中获得的结果。事实上,将内部连接看作是交叉连接的简化更有用,只是以匹配键值为条件。在下图中,我们看到内部连接中的重复键产生匹配键的所有组合。

作者插图
在问题的上下文中,我们不必担心连接后的重复结果,因为我们知道 fb_users 数据集中的 id 变量已经是惟一的了。使用 pandas 数据框架,我们的第一步是生成 user_comments 作为内部连接产品。
user_comments = pd.merge(fb_users, fb_comments, how='inner', left_on=['id'], right_on=['user_id'])
现在我们已经介绍了用户加入日期的信息,我们可以过滤属于从 2018 年起加入的用户的评论。我们还想考虑到评论日期在加入日期之前可能出现的数据错误。这些简单的过滤器可以使用:
**# filtering out comments that are dated before user joined** user_comments = user_comments[user_comments['created_at'] >= user_comments['joined_at']]**# only including comments from users who joined 2018 onwards** user_comments = user_comments[user_comments['joined_at'] >= '01-01-2018']
接下来,我们可以过滤 2020 年 1 月发布的评论。我们可以选择直接使用现有的日期时间变量 created_at 以下面的方式过滤值,该方式利用了 between 方法:
user_comments = user_comments[user_comments['created_at'].between('2020-01-01', '2020-01-31')]
或者,我们可以使用strftime方法将日期时间变量转换为指定格式为“%Y-%m”的字符串(例如,“2019–12”)。我们称这个新变量为' created_yr_mnth '。随后,我们可以通过筛选“created_yr_mnth”的值等于“2020–01”来选择 2020 年 1 月的注释。
user_comments['created_yr_mnth'] = user_comments['created_at'].dt.strftime('%Y-%m')data = user_comments[(user_comments['created_yr_mnth'] == '2020-01')]
注意:在应用了与日期相关的过滤器之后,只使用 strftime 是有意义的,因为我们希望保留 datetime 数据类型,以便应用 between 和≥过滤器。
这样,我们获得了我们的中间数据集 df ,我们可以开始实现我们在本练习开始时确定的两个 groupbys。
首先,我们根据 id 对数据进行分组,以便统计每个用户的评论数量。我们重置索引并调用新的变量,其中每个用户的评论数为 comment_cnt 。
data = data.groupby('id')['user_id'].count().reset_index(name='comment_cnt')
最后,我们通过按这个 comment_cnt 分组来计算对应于每个 comment_count 值的用户数量,从而获得结果。不要忘记根据任务的要求,根据 comment_cnt 对的值进行排序!
result = data.groupby('comment_cnt')['id'].count().reset_index(name='user_cnt').sort_values('comment_cnt', ascending=True)
你们中的一些人可能想知道过滤的顺序是否真的重要——在这个问题的上下文中,它实际上基本上无关紧要。例如,我们可以在合并关于用户的数据之前先过滤 2020 年 1 月的评论,这实际上可能是一种更有效的方法,因为合并将涉及更少的基于关键字的匹配行。我们可以根据 2018 年至 2020 年的用户加入日期进行同样的过滤。
然而,重要的是理解在这种情况下,过滤必须在分组之前完成。相反,如果我们决定过滤掉那些评论日期早于他们加入日期的用户,我们可能会错过那些有一些有效评论的用户。因此,在这种情况下,我们必须认识到过滤是在评论级别进行的,而不是在用户级别,因为 groupby 是在用户级别进行的,所以过滤应该在 groupby 完成之前进行。然而,情况并非总是如此,我们很快就会在问题 2 中看到这一点。
问题 2:付费与免费增值
这个问题的相关数据集是:
ms_user_dimension:
- user_id (int)
- acc_id (int)
|---------+--------|
| user_id | acc_id |
|---------+--------|
| 1 | 716 |
| 2 | 749 |
...
ms_acc_dimension:
- acc_id (int)
- 付款客户(字符串)—'是'/'否
|---------+----------------|
| acc_id | paying_customer|
|---------+----------------|
| 716 | no |
| 749 | yes |
...
ms_download_facts:
- 日期(时间)
- user_id (int)
- 下载量(整数)
|----------------------+---------+-----------|
| date | user_id | downloads |
|----------------------+---------+-----------|
| 2020-08-24 00:00:00 | 1 | 6 |
| 2020-08-22 00:00:00 | 2 | 2 |
| 2020-08-24 00:00:00 | 3 | 7 |
...
按日期查找付费和非付费用户的下载总数。只包括非付费用户比付费用户下载更多的记录。输出应首先按最早日期排序,并包含 3 列日期,非付费下载,付费下载。
就像在问题 1 中一样,我们看到我们需要采用某种形式的分组和分组计数。具体来说,我们将不得不首先按日期对下载进行分类,并根据付费和非付费下载对每个日期进行计数。
然而,与问题 1 不同,我们的过滤必须在分组完成后进行。我们是怎么知道的?我们看到,该任务要求我们根据两个变量之间的关系进行过滤,这两个变量包含每天付费和非付费下载次数的信息。更一般地说,过滤是基于不存在于原始表中但存在于某个中间表中的值来完成的。因此,简单地检查我们应该过滤哪个变量为我们提供了一个很好的框架,让我们思考应用于数据集的适当步骤序列。
反过来,我们知道,为了按日期分组并统计付费和非付费用户下载的次数,原始未分组数据必须包含每个下载事务的一行数据,其中包含以下信息:( I)下载日期;( ii)下载的用户是付费还是非付费用户;( iii)下载次数。这段代码完成了这个任务:
user_acc = pd.merge(ms_user_dimension, ms_acc_dimension, how = 'left',left_on = ['acc_id'], right_on=['acc_id'])data = pd.merge(user_acc, ms_download_facts, how = 'left',left_on = ['user_id'], right_on=['user_id'])
首先,我们将用户-帐户对(ms_user_dimension)与关于它是否付费的帐户细节(ms_acc_dimension)合并,以获得 df。请注意,我们希望使用一个左连接,因为我们主要关心的是保留所有现有的用户 id,,因为我们将使用那个键来匹配关于下载的信息。第二,我们将用户信息(现在附加有关于支付的信息(df))与下载信息(ms_download_facts)合并,以获得 df1。同样,我们使用了一个左连接,因为我们想要保留所有的下载行,对于下载不止一次的用户,我们想要复制他们关于支付的信息。
这里有一个问题:在这种情况下,合并的顺序真的重要吗?这将测试您对连接实际工作方式的理解。
接下来,与前面直接使用 groupby 的例子不同,让我们探索一种不太常用的方法,用于 pandas 数据帧,称为 pivot_table 。对于那些熟悉 Excel 中数据透视表的人来说,这个函数的工作方式基本相同。
注意 pandas.pivot_table 的以下参数:
- index :我们希望对结果数据透视表中每一行的数据进行分组的字段(一行一个日期)
- ****列:我们希望对结果数据透视表中每一列的数据进行分组的字段(一列用于支付,一列用于不支付)
- ****值:我们希望根据上述类别(下载次数)执行聚合功能的字段
- aggfunc :聚合函数(在本例中是对下载次数求和)
综上所述,下一步应该是这样的:
pivot_data = data.pivot_table(index=['date'],columns=['paying_customer'],values=['downloads'],aggfunc='sum')
为了将日期的索引移出到第一个字段,我们可以使用方法 to_records ()。然而,因为这会将我们的表转换成 numpy 数组,所以我们必须以下面的方式将其转换回 pandas dataframe:
payment_count_by_date = pd.DataFrame(pivot_data.to_records())
我们还通过用空字符串替换所有括号、撇号、逗号和单词“download”来清理这个结果数据帧中的列,以便我们的列将只是 date,no,yes。这一行可以达到目的:
payment_count_by_date.columns = payment_count_by_date.columns.str.replace("[()]","").str.replace("[' ']","").str.replace("[,]","").str.replace("downloads","")**# replace null values with 0** payment_count_by_date = payment_count_by_date.fillna(0)
现在,请注意,这正是我们想要执行筛选的表,因此我们知道我们的分组和聚合现在已经完成。
这个问题中需要的剩余过滤器是只包括非付费客户比付费客户下载更多的记录。实现这一点最直接的方法是首先创建一个名为 diff 的新列,该列获取非付费下载的数量减去付费下载的数量。然后我们过滤那些 diff = no-yes > 0,的行,最后按日期对结果排序,得到我们的最终结果!
**# constructing diff variable** payment_count_by_date['diff'] = payment_count_by_date['no']-payment_count_by_date['yes'] **# filtering** payment_count_by_date = payment_count_by_date[payment_count_by_date["diff"] > 0]result = payment_count_by_date[["date","no","yes"]].sort_values("date")
FacePDFViewer—一种 PDF 查看器,可通过使用面部标志检测的头部运动来控制
原文:https://towardsdatascience.com/facepdfviewer-a-pdf-viewer-controllable-by-head-movements-using-facial-landmark-detection-ed7a91073bd1?source=collection_archive---------23-----------------------
计算机视觉
一个使用 face-api.js 的实时 web 工具,通过头部运动在浏览器中滚动 PDF 文档。

照片由 Cookie 在 Unsplash 上的 Pom 拍摄
人脸标志检测在计算机视觉中有许多有用的应用,例如人脸交换、眨眼检测、人脸对齐、情感识别和头部姿态估计。
在本文中,我将向您展示我的项目“FacePDFViewer”。我开发了一个简单的工具,可以让你在屏幕上浏览 PDF 文档,而不需要使用鼠标,只需要移动头部。
你可以在我的 GitHub 库中找到带有运行指令的源代码,在 GitHub.io 的这里有一个现场演示
软件用 Javascript 编写,使用 face-api.js 进行人脸地标检测,使用PDF js进行 PDF 文档渲染。

FacePDFViewer 在运行。作者图片
项目结构
该软件由 3 个实时操作组成:
- 人脸标志检测
- 头部姿态估计
- PDF 渲染
PDF 渲染是使用开源的 pdfjs 库完成的,该库使用 html canvas 元素作为文档的容器。在本文中,我将讨论前两个操作,同时我会参考 pdfjs 示例进行 PDF 渲染,因为我在这个项目中使用这个库是非常基本的。
人脸标志检测
检测面部标志意味着使用形状预测器沿着面部图像寻找重要的面部结构。
注 :物体检测中有一个基本的经验法则:由大到小。这意味着,在寻找图像中的小细节之前,我们必须找到放置它们的“容器”,这既是出于性能原因,也是为了减少假阳性。在这种情况下,在检测面部标志之前,我们必须首先定位面部。
最准确的人脸检测器之一被认为是 SSD (单镜头多框检测器),这是一种基于 MobileNetV1 的 CNN,它计算图像中每个人脸的位置,并返回每个人脸的边界框和概率;face-api.js 提供了一个预训练的模型(ssd_mobilenetv1_model)。
用 face-api.js 人脸检测非常容易。首先,我们必须加载模型,然后我们可以检测人脸和地标。注意,face-api.js 方法是异步的,所以它们返回一个 javascript promise 对象。
方法 detectSingleFace 仅在输入图像中寻找一张脸,并接受画布元素(但也包括视频和图像)作为输入;然后使用withfanelandmarks返回一个带有面部边界框和一组 68 个标志点的对象。
为了实时获取网络摄像头图像,软件使用了 Web API 的 mediaDevice 模块的 getUserMedia 方法。
由 face-api.js 检测的面部标志存在于沿着面部的 68 个关键点中,使用仅具有 350kb 大小的模型*face _ landmark _ 68 _ model-shard 1。*从 face-api.js 文档中,作者说该模型已经在大约 35k 张人脸图像的数据集上进行了训练,这些图像标有所有 68 个人脸标志点。由于这些原因,这种检测器非常适合实时应用,因为它非常轻便、快速而又精确。**

68 面地标图。作者图片
这个模型覆盖了最重要的面部区域:嘴、眉毛、眼睛、鼻子、下巴。我们将分析它们来进行头部姿态估计。
头部姿态估计

软件提供的可能操作。图片作者。
该软件实现 6 种可能的动作,实时识别用户头部的 3 种运动。

移动检测基于三个观察值:
- 当用户向上/向下看时,鼻子长度相对于中间位置变化。**

当用户向上/向下看时,鼻子长度改变。作者图片
当用户向下看时它变大,当用户向上看时它变小。因此,这个想法是实时测量点 31 和 34 之间的距离(见地标图像),将其放入缓冲区,并在用户向上或向下看的场景超过 n (固定为 n )时进行观察。
注 :考虑到点 1 和 17 (颧骨)使 的值与网络摄像头 的距离无关,对距离值进行归一化。
2.当用户向左/右看时,左/右颌变得非常靠近嘴,直到它消失;与此同时,从另一边看,相应的点会移开。****
注 :地标探测器始终返回 68 个点的位置,即使它们不可见,因为能够预测它们的位置。

当用户向左/向右看时,嘴巴和下巴发生变化。作者图片
所以这里的想法是观察左侧的点 4 和 49 与右侧的点 14 和 55 之间的距离。特别是,系统仅检查 x 轴的距离,以使其适合任何不同的头部倾角。这次没有缓冲,动作求值一次;然而,当该事件被触发时,有一个 1 秒钟的计时器,在此期间不能更改页面。
3.当用户向左/右倾斜头部时,y 方向上的距离变大。****

当头部倾斜时,y 方向上两眼之间的距离增加
这个想法是观察眼睛的 y 坐标:通常,眼睛之间在 y 方向上的距离(点 42 和 47 被考虑)几乎为零。当头部倾斜时,两眼之间的 y 距离增加。当距离大于某个阈值时,我们可以简单地看哪只眼睛的 y 坐标更高,以了解头部倾斜的方向。
同样,我们必须将距离标准化,使其独立于到网络摄像头的距离。
重要的是要注意,软件使用许多阈值来理解用户何时真正进行头部运动。这些值是根据经验得出的,您可以尝试调整它们来改变系统的灵敏度或延迟。
最重要的是 FaceDetector:例如,减少 buffer_len 将加快系统对用户的反应,但也有风险要考虑非常小的移动,使文档垂直移动太多;增加太多 buffer_thresh 滚动事件将需要更大的移动,但是如果你面对许多误报,这可能是一个好的解决方案。然而,我邀请你尝试不同的值,因为这些值是凭经验得出的。
用小模型做实验
在 face-api.js 预训练模型中有标准和微小模型权重。正如你可能猜到的,微型的比标准的小,但是没有标准的精确。在这个项目中,我使用默认的标准模式,但这样一来,软件消耗了大量的 CPU 能力和内存,在一些功能不太强大的电脑可能会有延迟,错误或闪烁的影响。
为了解决这个问题,我试图减少场景分析的次数(现在系统每秒钟寻找 3 次运动变化)。但是仍然有大量的 CPU 消耗。所以我决定试试小模型。
现在加载的模型权重为 4:
- ssd_mobilenetv1_model (人脸检测标准)
- tiny _ face _ detector _ model(tiny 用于人脸检测)
- face_landmark_68_model (地标检测标准)
- face _ landmark _ 68 _ tiny _ model(tiny 用于地标检测)
从我的尝试来看,在我看来,准确性的降低是显著的,因为使用标准检测器,假阳性率几乎为零,而使用微型检测器,检测不那么稳定和鲁棒。

用微型探测器做实验。作者图片
我决定保留两种可能性,让用户根据自己的硬件做出选择;然而,默认模型是标准模型。
在幕后
这是一个非常小的项目,但我希望它能向你展示计算机视觉的力量。所有的计算都是实时进行的,但在幕后,face-api.js 使用 Tensorflow.js 核心来运行 CNN,所获得的权重对超过 35K 的标记图像进行训练,用于地标检测,几乎 400K 用于人脸检测。
你可以在 github.io 中找到这里的演示。
警告:出于安全原因,github.io 禁止上传文件,因此您不能上传自己的文档,只能从 github 存储库中加载文件(提供原始 URL )或使用演示 PDF 文件尝试该工具。
如果你想使用自己的 PDF,你必须下载资源库并在你的电脑上本地运行该工具。
参考
1 Howard,Andrew G .等人,“移动网络:用于移动视觉应用的高效卷积神经网络” arXiv 预印本 arXiv:1704.04861 (2017)。
[2]v .卡泽米和 j .沙利文(2014 年)。用回归树集合进行一毫秒人脸对齐。IEEE 计算机视觉和模式识别会议论文集(第 1867-1874 页)。
面部关键点检测:图像和关键点增强
原文:https://towardsdatascience.com/facial-keypoints-detection-image-and-keypoints-augmentation-6c2ea824a59?source=collection_archive---------10-----------------------
使用 Imgaug 库进行增强

日落!图片作者作者
由于深度神经网络(DNN)具有大量的可学习参数,因此需要大量的标记数据,以便这些参数能够很好地推广到我们手头的任务。数据扩充是一种非常常见的技术,用于增加标记训练数据的大小和多样性。在深度学习专业化课程中,吴恩达提到,与其他领域不同,在计算机视觉中,拥有更多数据几乎总是更好。图像增强也已经成为一种常见的隐式正则化技术,以解决 DNNs 中的过拟合问题。通常在图像增强中,我们使用翻转、旋转、缩放等的组合。但是在关键点检测任务中,我们还需要随着图像增加关键点。所以这篇文章是关于如何在关键点检测任务中增加训练数据的简单描述。链接到完整的笔记本包括培训 DNN 在文章的结尾。让我们开始吧—
面部关键点数据:
我们将使用来自 Kaggle 的数据集,并引用那里的话——
每个预测的关键点由像素索引空间中的(x,y)实值对指定。共有 15 个关键点,代表面部的以下元素:
左眼中心、右眼中心、左眼内角、左眼外角、右眼内角、右眼外角、左眼外角、左眉内端、左眉外端、右眉内端、右眉外端、右眉外端、鼻尖、嘴左角、嘴右角、嘴中上唇、嘴中下唇
让我们加载数据—
train_read = pd.read_csv(data_path + '/training.csv', sep=',')
print ('training data shape; ', train_read.shape)>>> training data shape; (7049, 31)
在训练数据中有 7049 个图像,但是问题是在数据集中有许多空值。准确地说,在 31 列中,除了‘nose _ tip _ x’、‘nose _ tip _ y’和‘Image’列之外,所有列都有空值。让我们检查一下关键点的分布—

图一。关键点的分布(图片由作者提供)
关键点遵循正态分布,因此,数据插补的最简单策略之一是用分布平均值替换 NaN 条目。对于数据填补,从最大似然的角度来看,有必要先将数据拆分成训练测试,然后进行转换,否则,我们很容易导致数据泄漏。然而,用 Colab 中的 7049 幅图像+增强图像来训练一个网络是非常耗时的,所以这里我决定只使用干净的数据。
train_clean = train_read.dropna(axis=0, how=’any’, inplace=False)train_clean = train_clean.reset_index(drop=True)print (‘data-frame shape with no null values: ‘, train_clean.shape)>>> data-frame shape with no null values: (2140, 31)
正如我们看到的,仅使用干净的数据大大减少了训练规模(从 7049 幅图像到 2140 幅),在这里,增强将非常方便。但是在增强之前,我们需要做更多的处理。image 列包含字符串形式的像素值,字符串之间有一个空格。
clean_imgs = []for i in range(0, len(train_clean)):x_c = train_clean[‘Image’][i].split(‘ ‘) # split the pixel values based on the spacex_c = [y for y in x_c] # create the listed pixelsclean_imgs.append(x_c)clean_imgs_arr = np.array(clean_imgs, dtype=’float’)
从 Kaggle 中的数据描述来看,图像有维度(96,96),所以多几步—
clean_imgs_arr = np.reshape(clean_imgs_arr, (train_clean.shape[0], 96, 96, 1))train_ims_clean = clean_imgs_arr/255\. # scale the images
让我们从数据框中获取关键点—
clean_keypoints_df = train_clean.drop(‘Image’, axis=1)clean_keypoints_arr = clean_keypoints_df.to_numpy()
让我们定义一个函数来帮助我们一起可视化图像和关键点—
def vis_im_keypoint_notstandard(img, points, axs): axs.imshow(img.reshape(96, 96)) xcoords = (points[0::2] + 0.) ycoords = (points[1::2] + 0.) axs.scatter(xcoords, ycoords, color=’red’, marker=’o’)
太好了!现在我们都准备进入扩增。
线性对比度和高斯模糊:
首先,我们在关键点不受影响的地方做了一些增强。使用 Imgaug 库,我们将在图像上添加高斯模糊和线性对比度。正如您所理解的,我们不需要单独考虑关键点,因为它们不会受到影响。让我们看看下面的代码—
添加增强:高斯模糊和噪声
LinearContrast修改图像的对比度,GaussianBlur增强器用于使用高斯内核模糊图像,sigma是高斯内核的标准偏差。通过Sometimes,模糊仅适用于所有图像中随机 80%的图像。我们可以将生成的原始和增强图像可视化如下—

图二。通过添加来自 Imgaug 库的高斯模糊和线性对比度进行增强。(图片由作者提供)
缩放和旋转图像和关键点:
为了在我们的数据集中包含旋转和缩放的图像,我们还需要相应地改变关键点。这是主要原因之一,不建议在这个问题中使用Keras imagedata generator类。在这里,Imgaug 库非常方便。与其描述如何正确地完成它的步骤,我不如分享下面的代码块—
同时旋转和缩放图像和关键点。
我们使用Sequential定义由旋转和缩放图像组成的增强序列。旋转角度设置为 15 度,缩放范围设置为原始图像的 80%到 120%。图像和相应的关键点将以这些范围之间的任意数量随机增加。让我们想象一下结果—

图三。通过添加旋转和缩放来增强图像和关键点。(图片由作者提供)
水平翻转:图像和关键点:
与之前类似,我们可以使用 Imgaug 库进行水平翻转,但是显式编写代码也相对容易。对于图像的水平翻转,我使用了[numpy fliplr](https://numpy.org/doc/stable/reference/generated/numpy.fliplr.html);对于关键点,在水平翻转中,y 坐标不会改变,但 x 坐标会改变。由于图像的维数是(96,96),我们通过做(96-x)得到翻转的 x 点。下面是代码块—
包括一切,我们现在有完整的数据集。从 2140 张图片开始,现在我们有 17120 张图片。让我们想象一下其中的一些—

图 4。通过添加水平翻转来增强图像和关键点。(图片由作者提供)
我们在扩增结束时打乱数据—
from sklearn.utils import shuffleaug_ims_train_final, aug_points_train_final = shuffle(aug_ims_train_clean_g3, aug_points_train_clean_g3)
训练“类似盗梦空间”的深度神经网络:
扩充之后,我们现在开始建立 DNN 模型并准备培训。下面是我使用的神经网络结构,它是从最初的 InceptionV3 结构稍微简化的。

图五。盗梦空间就像‘深度神经网络’。(图片由作者提供)
我们也添加一些‘Callbacks’ 如下—
class customCallbacks(tf.keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs=None):
self.epoch = epoch + 1
if self.epoch % 50 == 0:
print ('epoch num {}, train acc: {}, validation acc: {}'.format(epoch, logs['mae'], logs['val_mae']))reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_mae', factor=0.8,
patience=25, min_lr=1e-5, verbose=1)
下一步是编译和训练模型—
face_key_model2_aug.compile(loss='mse',
optimizer=Adam(learning_rate=3e-3),
metrics=['mae'])face_key_model2_aug_train_clean = face_key_model2_aug.fit(aug_ims_train_final, aug_points_train_final,
validation_split= 0.05,
batch_size=64, epochs=300,
callbacks=[customCallbacks(), reduce_lr],
verbose=0)
训练和验证曲线绘制如下,作为时期的函数—

图 6:左图:超过 300 个时期的训练和验证损失。右图:与左图相同,但平均绝对误差不同。(图片由作者提供)
下一步是预测测试图像的关键点—
predict_points_aug2_clean = face_key_model2_aug.predict(test_ims)
print ('check shape of predicted points: ', predict_points_aug2_clean.shape)>>> check shape of predicted points: (1783, 30)
所以有 1783 张测试图像,我们可以看到下面的一些预测—
fig = plt.figure(figsize=(10, 8))
npics= 12
count = 1
for i in range(npics):
# ipic = i
ipic = np.random.choice(test_ims.shape[0])
ax = fig.add_subplot(npics/3 , 4, count, xticks=[],yticks=[])
vis_im_keypoint_notstandard(test_ims[ipic], predict_points_aug2_clean[ipic], ax)
count = count + 1
plt.tight_layout()
plt.savefig(data_path+'/prediction_keypoints.png', dpi=200, bbox_inches = 'tight', pad_inches = 0)
plt.show()

图 7。测试图像上的预测关键点。(图片由作者提供)
结论:
最后,我想强调的是,这篇文章更专注于数据增强部分,而不是明确地专注于在 Kaggle 中取得更好的成绩。一篇精彩的博文可以在这里找到,它讨论了如何在 Kaggle 中获得高分的一步一步的方法。由于我们专注于预测图像中的所有关键点,因此有一些帖子介绍了如何针对人脸上的单个关键点训练网络,并将它们组合起来以获得更好的结果。我希望这篇文章能帮助你开始解决这类问题,然后根据资源和时间,你可以尝试各种东西。
保持坚强,干杯!!
1 GitHub 链接获取完整笔记本。
混合数据的因子分析
原文:https://towardsdatascience.com/factor-analysis-of-mixed-data-5ad5ce98663c?source=collection_archive---------10-----------------------
对具有连续和分类特征的数据使用 FAMD

图片来自 Unsplash
简介
大型数据集对于数据分析师或数据科学家来说可能是一场噩梦,因为其中存在许多线性相关的要素。不必要的数据特征会降低 ML 模型的性能并增加训练预算。有一些技术可以用来减少能够充分解释大部分方差的重要特征的数量。在本文中,我将介绍针对数据集的因子分析技术,我们可能有兴趣减少关键要素的数量,从而使用最有效的数据成分进行建模。
Scikit Learn 的 PCA
大型数据集必须使用简单的技术如主成分分析(PCA)来降低维数。这是以准确性为代价的,但增加模型训练的灵活性和数据可视化的简单性是核心目标。PCA 不是分析数据集重要特征的“特征选择”。我在下面的文章中使用 Shapash 和 Scikit-Learn 描述了特性选择。
https://medium.com/geekculture/feature-selection-in-large-datasets-fc27a7e8e388
PCA 来自一个完全不同的世界,但目标是一样的:减少维数。PCA 通过标准化这些值、构建协方差矩阵并由此获得特征值和特征向量来获得分量。PCA 得出解释数据集中大多数可变性的最终成分。因此,这些新组件可以被认为是原始值的线性组合或复合。第一个成分解释了最高的可变性,第二个成分具有第二高的解释比率。我们可能非常熟悉 iris 数据集,它有四个数字列和一个花卉种类的目标列。PCA 适用于数据集的数字列,并且总是建议对连续变量执行 PCA。当进行主成分分析时,它仅在两个维度上产生物种间的明显差异。

虹膜数据集上的主成分分析
Scikit Learn 的因子分析
Sci-kit-Learn 还提供了另一种与 PCA 非常相似的降维方法。如前所述,主成分分析侧重于初始数据的线性组合,新的成分非常不相关。另一方面,因子分析指导对数据潜在特征的分析,并推导出成分。假设潜在因子保留在低维空间中,并且通过添加具有潜在因子的高斯噪声来获得新的观测值。

虹膜数据集的因子分析
结果与 PCA 输出非常相似,只是 x 轴和 y 轴刻度不同。
Scikit Learn 的独立成分分析
Scikit-Learn 提供了分析数据的选项,其中观察数据的潜在因素不是高斯型的。对于这种情况,我们可以使用 FastICA 模块。

虹膜数据集上的独立分量分析
这里,沿着独立组件 2 也可以清楚地区分物种。
如果特征是绝对的呢?
PCA 对连续数据非常有效,但是真实世界的数据是连续数据和分类数据的混合。有时,分类数据使用一键编码方法进行编码,但不推荐使用这种方法。PCA 背后的核心思想是以一定的准确性为代价来确定解释大部分可变性的成分。当我们通过编码获得二进制数据时,可变性的概念就崩溃了。
PCA 可能对编码数据起作用,但本质上这并不能使它成为一个好的分析。例如,住房数据有几个分类特征,这些特征首先被编码、标准化并输入 PCA 分析。

分类数据的主成分分析
最初,这似乎是一个很好的分析,显示了两个最主要的组成部分,但这种编码不建议在 categorial 上使用。相反,应该利用混合数据的因子分析(FAMD)等技术。FAMD 旨在处理混合类型的数据。
FAMD 利用王子
王子包有一些内置的因素分析方法,包括 FAMD。我们可以继续用同样的住房数据。
当选择“HouseStyle”作为拆分因子时,我们得到下图。

FAMD 住房数据集
显然,在数据点上有一些重叠,导致组分 1 仅解释了 8%的可变性,而组分 2 解释了约 3%的可变性。FAMD 结合主成分分析和多成分分析技术进行分析。MCA 代表多重对应分析,特别适用于多种分类因素。如果数据集按具有连续值和分类值的不同要素进行分组,则可以部署另一种称为 MFA(多因素分析)的技术。
让我们选择葡萄酒数据集,它由两个分类变量和几个数字变量组成,描述列出的葡萄酒的质量。一旦将 FAMD 应用于该数据集并在“土壤”类型上进行分割,数据点就可区分。

FAMD 数据集

土壤上的 FAMD 分析分裂
显然,按土壤划分时,有不同的群体。第一个因素解释了 26%,第二个因素解释了 22%的可变性。
结论
在本文中,我讨论了使用 FAMD 技术对大型数据集进行降维。可以通过几种特征选择方法来分析数据集以提取最重要的特征,或者可以利用成分/因素分析技术。不建议对分类数据应用常规 PCA。在处理混合数据时,FAMD 是处理不必要因素和降低数据维数的推荐方法。
Github
基于 R 和 Python 的“女子田径记录”数据因子分析
原文:https://towardsdatascience.com/factor-analysis-on-women-track-records-data-with-r-and-python-6731a73cd2e0?source=collection_archive---------6-----------------------
实践教程
不仅仅是降维,而是发现潜在变量

由尼古拉斯·霍伊泽在 Unsplash 上拍摄的照片
因子分析(FA) 和主成分分析(PCA) 都是降维技术。因子分析的主要目的不是降低数据的维度。因子分析是发现潜在变量的有用方法,这些潜在变量不是直接在单个变量中测量的,而是从数据集中的其他变量中推断出来的。这些潜在变量被称为 因素 。因此,因子分析是一种测量潜在变量的模型。例如,如果我们在我们的模型中发现两个潜在变量,则称之为一个 双因素模型 。FA 的主要假设是在我们的数据中存在这样的潜在变量。
今天,我们使用与主成分分析非常相似的主成分方法进行因子分析。我们在这里使用的数据是代表 55 个国家的七个不同项目的国家女子记录。你可以点击下载数据集。数据集如下所示。

前 5 行女性跟踪记录数据
变量的描述如下。

先决条件
本文假设你有主成分分析的知识。如果你没有,请阅读我以前的文章:
- 用 R 和 Python 对乳腺癌数据进行主成分分析
- 使用 Scikit-learn 进行主成分分析(PCA)
- PCA 背后的统计和数学概念
使用 R 对女性跟踪记录数据执行 FA
为数据集选择最佳数量的因子
这里,我们使用 principal() 函数对数据集应用 FA。 principal() 函数使用主成分方法执行因子分析。
下面的代码块对我们的数据执行 FA。
目前旋转设置为 无 。除此之外,我们要求我们的数据有 4 个因素。稍后,我们可以请求小于 4 的最佳因子数。covar 参数非常重要。通过设置 covar = FALSE ,FA 过程应该使用相关矩阵而不是协方差矩阵。从相关矩阵中导出的因子与从标准化(缩放)变量的协方差矩阵中导出的因子相同。因此,通过设置 covar = FALSE,数据将在分析前居中并缩放,即使数据集特征之间的比例存在显著差异,我们也不需要对数据进行显式特征缩放。
让我们通过运行 fa$values 得到特征值。

将这些值四舍五入到小数点后 3 位将得到:

因此,
- 单独用第一个因素解释的方差为:(5.068/7) x 100% = 72.4%
- 前两个因素共同解释的方差为:((5.068+0.602)/7)x 100% =81%
- 前三个因素加在一起解释的方差为:((5.068+0.602+0.444)/7)x 100% =87.34%
诸如此类…
让我们创建 scree 图,它是特征值的可视化表示。

弯曲明显发生在第二特征值处。根据 凯泽 法则,建议保留特征值大于 1.0 的分量。我们得到了特征值,只有第一个大于 1.0。保持对应于特征值 5.068 的第一因子仅解释了数据中约 72.4%的方差。所以,我们也保留第二个。然后,前两个因素一起解释了数据中 81%的方差。
因此,我们为我们的数据保留两个因素。
找出数据是否需要因子旋转
我们用 nfactors = 2 再表演一次 FA(之前, nfactors = 4 )。这是因为我们决定只保留我们数据中的两个因素。然后,我们得到以下因素载荷。

通过查看输出,我们可以确定原始因子加载没有产生可解释的因子。这是因为每个因素上的负荷分布不均匀。因此,我们尝试因子旋转,它是原始因子的正交变换。因子轮换是为了解释的目的。通过在 principal() 函数中设置 rotate = varimax ,我们用 varimax 因子旋转再次执行 FA。然后,我们再次得到因子载荷。

计算所选模型的因子载荷、公度和特定方差
运行 fa2 模型后,会得到如下输出。

上述输出在一个表中包含旋转因子加载、公因子和特定方差。 RC1 和 RC2 列表示我们选择的两个因素的载荷。 h2 列代表每个变量的公因子。u2 列代表每个变量的具体方差。运行 fa2\(loadings** 和 **fa2\)communality 也将分别给出旋转因子加载和 communality。


解释共性值和特定差异
【社群主义】
- X1: 关于X1 的 86% 的变异性由我们选取的两个因子来解释。
- X2: 关于X2 87%的变异性是由我们选取的两个因子来解释的。
- X3: 关于X3 78%的变异性是由我们选取的两个因子来解释的。
- X4: 关于X4 的 85% 的变异性由我们选取的两个因子来解释。
- X5: 关于X5 的 80% 的变异性由我们选取的两个因子来解释。
- X6: 关于X6 的 74% 的变异性由我们选取的两个因素来解释。
- X7: 关于X7 的 78% 的变异性是由我们选取的两个因素来解释的。
显然,我们选择的两个因素解释了所有变量的高方差。
【具体差异】
- 具体因素对 X1 的影响约为 14% 。
- 特定因素对 X2 的影响约为 13% 。
- 具体因素对 X3 的影响约为的 22% 。
- 具体因素对 X4 的影响约为 15% 。
- 特定因素对 X5 的影响约为 20% 。
- 具体因素对 X6 的影响约为 26% 。
- 具体因素对 X7 的影响约为 22% 。
确定模型中的因素
让我们再次看看旋转因子加载。

很明显,变量 X1、X2、X3 定义了因子 1 (因子 1 的高负荷,因子 2 的相对小负荷),而变量 X4、X5、X6 和 X7 定义了因子 2 (因子 2 的高负荷,因子 1 的相对小负荷)。但是变量 X4 具有由两个因子表示的属性方面(两个因子上近似相等的负载)。
为了给这两个因素命名,让我们把重点放在该领域的领域知识上。在给定的问题中,变量 X1,X2,…,X7 具有以下含义。

给定的数据集代表了代表 55 个国家的妇女在 7 个不同事件中的国家轨迹记录。一般在短距离跑(如 100 米、200 米、400 米)中,运动员应主要关注 速度 。在长跑中(如 1500 米、3000 米、马拉松),运动员应主要关注 耐力 或 耐力 。在我们的分析中,因子 1 代表短途轨迹记录(因为 X1、X2 和 X3 定义了因子 1),而因子 2 代表长途轨迹记录(因为 X4、X5、X6 和 X7 定义了因子 2)。因此,我们可以给这两个因素起相应的名字如下。
- 因子 1 → 速度因子
- 因素 2 → 公差或耐久性因素
但也要注意,参加 800 米项目的运动员(以 X4 为代表)应该在速度和耐力之间保持良好的平衡。这是因为 X4 在这两个因素上的负载大致相等。

作者图片
这些是可操作的见解,可以通过对“妇女跟踪记录”数据进行因子分析(FA)来获得。
用 Python 对女子田径记录数据进行 FA
在 Python 中,可以使用 factor_analyzer 库,在 matplotlib、pandas、numpy、scikit-learn 等其他库的帮助下进行 FA。这里,我们获得了完全相同的结果,但采用了不同的方法。我们不使用相关矩阵,而是使用协方差矩阵,并在运行 FA 之前手动执行特征缩放。然后,我们向从 FactorAnalyzer() 类创建的对象中提供标准化(缩放)的数据。
为了执行 FA,我们首先通过指定超参数的相关值,从 FactorAnalyzer() 类创建一个对象(称为 fa )。然后,我们调用它的 fit() 方法来执行 FA。我们向 fit() 方法提供缩放数据。然后我们调用 fa 对象的各种方法和属性来获取我们需要的所有信息。输出是 numpy 数组的形式。我们可以使用几个 print() 函数来很好地格式化输出。在这里,我们还创建了碎石图。最后,我们调用 fa 对象的 transform() 方法来获得因子得分。然后,我们将它们存储在一个 CSV 文件和一个 excel 文件中,以备将来使用。新的(缩减的)数据集的维度是 55 x 2。只有两列。这是因为我们决定只保留两个因素,这两个因素共同解释了原始数据中约 81%的可变性。
以下 Python 代码块对我们的数据集执行 FA。

下面的代码块创建了 scree 图。

运行下面的代码块后,因子得分被存储在一个 CSV 文件(track _ records _ 81 _ var . CSV)和一个 excel 文件(track _ records _ 81 _ var . xlsx)中,这两个文件将被保存在当前工作目录中。新的(缩减的)数据的维度是 55 x 2。这是因为我们决定只保留两个因素,这两个因素共同解释了原始数据中约 81%的可变性。
下图显示了新数据集的前 5 次观测。 RF1 代表旋转因子 1 和 RF2 代表旋转因子 2 。数据集的形状是 55 x 2。

精简数据集的前 5 个观察值
我们可以使用新的(精简的)数据集进行进一步分析。
摘要
因子分析不仅仅是为了降维,而是寻找潜在变量。这里,我们使用了两种不同的编程语言来执行 FA。尽管两种语言都提供了高级的内置函数,但 r 的输出格式良好。我们已经获得了完全相同的结果,但是采用了两种不同的方法。首先,我们通过在 principal() 函数中设置 covar = FALSE 来使用相关矩阵。作为我们的第二种方法,我们通过在 FactorAnalyzer() 函数中设置 is_corr_matrix = False ,将协方差矩阵用于缩放数据。
选择最佳数量的因素是主观的。这取决于数据及其领域。有时,我们不能仅通过查看 scree 图来决定最佳的因素数量。 凯泽的 法则也不是硬性规定。灵活性总是有的。总的来说,我们应该经常在因素数量和由所选因素共同解释的可变性数量之间保持良好的平衡(权衡)。
感谢阅读!
本教程由Rukshan Pramoditha,数据科学 365 博客作者设计创作。
在https://rukshanpramoditha.medium.com阅读我的其他文章
本教程中使用的技术
- Python & R (高级编程语言)
- 熊猫 (Python 数据分析与操纵库)
- matplotlib (Python 数据可视化库)
- Scikit-learn (Python 机器学习库)
- Jupyter 笔记本& RStudio (集成开发环境)
本教程中使用的机器学习
- 主成分分析
- 因子分析
本教程中使用的统计概念
- 相关矩阵
- 方差-协方差矩阵
本教程中使用的数学概念
- 特征值
2021–02–05
公平且可解释的机器学习
原文:https://towardsdatascience.com/fair-and-explainable-machine-learning-25b802b00bec?source=collection_archive---------24-----------------------
模型可解释性
关于如何防止机器学习模型中的偏差并理解其决策的指南。

由尼古拉斯·乔西在 Unsplash 上拍摄的照片
“虽然可以说神经网络可以编写自己的程序,但它们是朝着人类设定的目标,使用为人类目的收集的数据来这样做的。如果数据有偏差,即使是偶然的,计算机也会放大不公正。”
——卫报1
介绍
由于围绕使用算法作为自动决策工具的伦理问题,机器学习在医学、金融和教育等领域的应用目前仍然非常复杂。
这种不信任的两个主要根源是:偏见和低可解释性。在本文中,我们将探讨这两个概念,并介绍可以应用的不同技术,以使我们的模型更加公平和可解释。
偏见
在机器学习中,偏差这个术语通常与我们试图预测的正确值和我们的模型做出的预测之间的差异相关联。然而,在认知科学中,偏见一词可以有许多其他含义。认知偏差的一些例子有[2]:
- 报告偏差:与我们数据中考虑的其他结果/特性相比,结果/特性在我们数据中记录的频率并不能真正代表它在现实世界中出现的频率。
- 选择偏倚:从不能真正代表关键群体特征的群体中选择样本(非随机样本)。
- 过度概括:过度简化流程可能会导致错误的结论。
- 相关性谬误:将虚假的相关性误解为因果驱动的过程。
- 确认偏差:只专注于寻找证实我们先前信念的来源而不是公正地评估关于一个主题的不同观点/选项。
如果偏见嵌入到我们的数据和/或机器学习模型中,这可能会导致算法偏见(有偏见的自动决策工具)。为了试图理解机器学习模型是否有偏见,最直观的方法之一是分解对我们模型的评估。
例如,假设我们训练了一个模型来分类某人是否受到疾病的影响,我们达到了大约 80%的分类准确率。在这一点上,为了确保我们的模型是真正公平的,我们仔细看看如果我们首先只考虑女性,然后只考虑男性,准确性会如何变化。仅考虑女性,我们得到大约 95%的准确率,而仅考虑男性,我们得到大约 55%的准确率!为了试图理解为什么在性能上有如此大的差异,我们可以试着检查我们的训练数据。经过仔细分析后,我们注意到大约 75%的训练数据只包含女性患者的样本,因此我们的模型对这些样本给予了总体上更大的权重,以便最终尽可能降低我们的错误分类分数。因此,进行这种类型的分析有助于我们理解,尽管我们的模型能够整体表现良好,但它不应该潜在地用于医疗环境中,以便对男性患者进行诊断。考虑到每个亚组的准确性差异,将该模型用于男性和女性诊断实际上是“不公平的”。为了进一步分析,应考虑由此产生的混淆矩阵和精确度/召回率指标,以评估假阳性/假阴性率的影响。
当试图理解数据驱动的系统是否可能有偏差时,另一个应该考虑的因素是首先对训练数据是如何产生的进行一些背景研究(例如,参与者是否自愿参与,他们是否来自不同的国家,他们是否有不同的背景,研究是否由任何组织赞助等等)。
因此,在数据科学工作流程中,可以在三个可能的阶段应用偏差缓解算法:预处理(在训练数据上)、处理中(在训练模型时)、后处理(在预测标签上)。这些阶段中最常见的偏置抑制算法有3:
- 预处理:重新加权,不同影响去除器,学习公平表示。
- 处理中:对抗性去偏置,偏见去除器,元公平分类器。
- 后处理:拒绝选项分类和校准均衡赔率。
所有这些不同的方法都可以用 Python 实现,利用 AI Fairness 360 库。
可解释的人工智能
现代机器学习模型中的一个关键权衡是性能与复杂性。更复杂的模型,如基于深度学习的架构,事实上往往优于更传统的模型,如回归技术和线性分类器。
复杂的模型通常被称为黑盒(例如集合模型、神经网络),传统上,当试图理解为什么他们会做出选择而不是另一个时,它们很难解释。相比之下,诸如决策树和线性回归之类的模型反而被认为是白盒(它们更容易理解预测是如何产生的)。
可解释的人工智能是人工智能的一个新分支,旨在通过向最终用户提供不仅是预测,还有支持证据,来试图“揭开”机器学习模型预测的神秘面纱。
创建可解释的模型会有好处:
- 用户/客户:为什么我的贷款被拒绝了?为什么有人建议我接受这种治疗?
- 监管者/政府:提供模型公平的证据。
- 数据科学家/开发人员:我们的模型实际表现如何?如何改进?
目前,人们使用不同的方法来解释人工智能,一些主要的例子是:
- SHAP (Shapley 附加解释):旨在通过测量每个特征对预测的贡献大小来解释模型预测(Shapley 值的绝对值越大,特征被认为越重要)。
- LIME(局部可解释的模型不可知解释):主要用于处理高度非线性的模型。LIME 旨在通过将原始特征空间划分成不同的线性子部分来线性化非线性空间,从而使得该模型可由局部基础上的线性模型来解释。
- 基于树的特征重要性:计算每个输入特征导致的一棵树(或一片树林)的平均杂质减少量。根据这一原则,在创建模型预测时,将结点分割为更靠近三个结点顶部的要素将具有更大的权重。
- 部分相关图(PDP): 总结一组输入变量和我们的模型预测之间的关系。这样,可以直观地理解模型预测如何依赖于不同的输入变量。
- 个体条件期望(ICE)图:通过模拟输入数据稍加修改会发生什么,帮助我们理解个体观察预测。
为了执行可解释的人工智能任务,一些最常用的 Python 库是:人工智能可解释性 360 (AIX360) 和 Captum 。
联系人
如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
- 领英
- 个人网站
- 中等轮廓
- GitHub
- 卡格尔
文献学
1卫报关于机器学习的观点:人必须决定。社论。访问:https://www . the guardian . com/commentis free/2016/oct/23/the-guardian-view-on-machine-learning-people-must-decision
[2]斯坦福 CS224N:具有深度学习的| Winter 2019 |第 19 讲—AI 中的偏见。斯坦福在线。访问:https://www.youtube.com/watch?v=XR8YSRcuVLE&ab _ channel =斯坦福在线
3公平且可解释的 AI。玛格瑞特·格罗宁迪克。访问地址:https://marg riet-groenendijk . git book . io/trusted-ai-workshop/
芝加哥警察局的公平警务分析
原文:https://towardsdatascience.com/fair-policing-analysis-of-the-chicago-police-department-d251333a870c?source=collection_archive---------42-----------------------
变更数据
研究这些数据能在警务中带来更大的平等和公平吗?

由多米尼克·罗宾逊在 Unsplash 上拍摄
美国各地的警察部门因为各种错误的原因出现在新闻中,尤其是最近因为过度使用武力的事件。乔治·弗洛伊德(George Floyd)和布里奥纳·泰勒(Breonna Taylor)等人的死亡引发了一场关于种族/民族在警察对待个人方面所起作用的辩论。作为加州大学圣地亚哥分校数据科学学生协会项目委员会的一部分,我们的团队(阿里亚曼·辛哈、库莎·贾德巴贝和夏嫣·加格)试图收集、分析和可视化来自芝加哥警察局(CPD)的数据,以进行公平的警务分析。本分析的目的不是对芝加哥市的警务工作做出任何结论,而是简单地分析一个大型数据集,以促进进一步的数据驱动的警务研究。
我们的团队使用了来自芝加哥新闻公司无形研究所的数据集,可以在这里找到。这是美国警察部门最大的公开数据集之一,数据跨度从 1940 年到 2017 年。在我们的分析中使用的最相关的特征是芝加哥市的人口统计数据、军官级别/晋升、军官服役年限、军官工资和对军官的投诉。使用这个数据集被证明是具有挑战性的,因为一些文件命名不当;命名惯例和记录数据的方法会随着时间的推移而改变。
人口统计分析
我们分析的第一步是分析 CPD 的种族统计数据。在比较芝加哥市和芝加哥警察局的种族划分时,我们发现了惊人的差异。

作者图片
如图 1A 所示,白人官员约占芝加哥人口的 32%,而截至 2017 年,约 62%的芝加哥居民为白人,如图 1B 所示。黑人、西班牙人和其他种族/民族的人口统计群体在他们城市的警察队伍中明显不足。为了检验这一说法的有效性,我们进行了卡方拟合优度检验,得出了显著的 p 值< 0.0001 suggesting that these 2 sets of racial demographic counts come from differing distributions. However, the sample sizes here are essentially the entire population so the assumption of independence and randomization needed to conduct a chi-squared test is not met.
Following this analysis of demographics, our team compared the ranks of officers currently serving in Chicago by race and once again found notable differences.

Image by Author
As shown by the purple bars in Figure 2A, the proportion of White officers that served as low-rank officers is lower than all other races/ethnicities, which means that White officers were more likely to get promoted. Also, the data suggest that White officers hold higher ranks (Medium and High-Rank Positions) as the orange and teal bars appear higher for White officers in comparison to Black, Hispanic, and officers of other races.。出于对这一结果的好奇,我们检查了是否存在类似的性别差异,如图 2B 所示:

作者图片
在这里,我们可以看到,男性军官的紫条较低,再次表明男性军官往往更容易获得晋升。这表明,与女军官相比,男军官更有可能担任中级和高级职务,因为男军官的橙色和蓝绿色条纹更多。
任职年数分析
作为我们项目的一部分,我们的团队有兴趣了解不同种族/民族和性别的官员在任职期间是否有显著差异。使用我们的官员档案数据集,我们通过减去他们的辞职日期(或数据集最后更新的日期,意味着该官员没有辞职日期,但仍在服务)和他们的第一个任命日期来计算官员的服务年限,并在我们的数据集中创建了一个名为“years_served”的列。为了首先分析种族/民族,我们将数据分为三个主要的种族/民族群体:白人、黑人和西班牙人/其他人,并使用这一点,我们使用 Python 中的 Seaborn 包创建了 33,324 名警官服役年限的叠加直方图:

作者图片
如图 3A 所示,我们整个数据集的平均值为 20.94 年,中位数为 23.07 年。通过我们对数据的解读,我们发现了三个有趣的点。首先,许多官员,特别是西班牙裔/其他官员,工作时间不超过一年。出于这种好奇,我们查看了我们的数据,发现大部分被标记为西班牙裔/其他种族/族裔的官员都有指定的日期和合理/准确的辞职日期。我们推测,我们关于西班牙裔/其他种族/民族的数据可能会有偏差和局限性。接下来,我们看到在芝加哥警察局工作了 20 年后退休的所有种族/族裔的警官人数突然增加。我们做了进一步的研究(可在芝加哥市的网站上公开获得),我们发现芝加哥警察局的一名警察在余生中获得其最新年薪 50%的养老金计划的最低门槛是在服务 20 年后。我们还注意到,很大一部分军官在服役 30 年后退休;我们的团队假设,这种趋势是由于在服务 29 年后养老金计划达到最大值(在他们的余生中,最高可达一名官员最新年薪的 75%)。我们对按性别划分的服务年限进行了类似的分析,并在图 3B 中发现了相关结果:

作者图片
在这个柱状图中,男性官员的平均寿命为 21.9 岁,而女性官员的平均寿命为 16.2 岁。正如前面对种族/族裔的分析一样,我们注意到大量军官,特别是女军官,任职时间只有一年或不到一年。这让我们特别感兴趣,因为我们无法从提供给我们的数据中找到这个峰值的任何因果关系。此外,我们看到 20 年后辞职的女性和男性官员人数突然增加,大部分官员,特别是男性官员,在大约 30 年后辞职。这一趋势也符合我们之前提出的假设,即官员在 20-30 年范围内退休,以最大限度地提高他们的养老金福利。
薪资分析
在对任职年限进行卡普兰-迈耶分析后,我们的团队调查了官员的种族/民族是否会影响其薪酬。使用所有警察级别的数据,我们发现每个种族/民族的警官的平均工资大致相同。然而,当军官按级别分层时,我们发现了潜在的工资差异。从数据来看,西班牙裔或其他种族/族裔的警长平均工资比白人和黑人警长低 3.8%。这就转化为大约 2500 美元的收入差异,如图 4A 所示:

作者图片
此外,白人指挥官的年薪高于黑人、西班牙裔和其他种族/族裔的指挥官,平均高出 35.2%,收入差距约为 39,000 美元,如图 4B 所示:

作者图片
为了检验这些发现的统计显著性(图 4A 和 4B),我们进行了四个独立的方差分析假设检验,按照警察队伍中的级别(技术员、警官、警官和指挥官)对警察的种族/民族工资进行了分层。针对不同军官种族/民族的方差分析测试。在进行 ANOVA 测试时,我们验证了满足以下假设:(1)每个种族/族群中的数据接近正态,(2)每个种族/族群中的分布大致相等,以及(3)每个种族/族群内和每个等级中的数据是独立的。具体来说,中士和指挥官的工资在种族/民族群体之间并不平等,因为这两项测试都产生了小于 0.001 的显著 p 值。我们的研究结果表明,在警察部队的较高级别中存在种族/族裔方面的收入不平等,但对于较低级别的职位,如技术员和警官,在收入方面没有统计上的显著差异。数据集中存在一些限制,会对我们得出的结论产生影响;例如,指挥官等高级官员的数据明显较少,因此,工资可能无法准确反映芝加哥警察局所有指挥官的工资。
出于对这些发现的好奇,我们做了进一步的分析,以观察性别是否也会对警官的工资产生影响,如图 4C 所示:

作者图片
我们进行了假设检验来比较男女警官之间的平均工资,发现在任何警察级别上,男女之间的工资都没有统计上的显著差异。
过度受力分析
在 2000 年至 2016 年间针对芝加哥警察局警官的 244,000 份投诉记录中,有 32,500 份,或 17.2% 这些投诉是因为过度使用武力。此外, 98.2%的过度使用武力投诉没有导致对警官的惩戒。为了进一步分析,我们想按警官的种族/民族和性别来分析被指控过度使用武力的警官。使用我们的投诉数据集,我们过滤了过度使用武力的投诉,并删除了不止一次投诉过度使用武力的警官(这意味着我们的分析分析了不同种族/民族和性别的警官对他们过度使用武力的投诉比例)。首先,我们按种族/民族分析了投诉过度使用武力的警察比例,如图 5A 所示:

作者图片
我们的调查结果显示,黑人警官对过度使用武力的投诉最多,约为 34%。白人警官的比例为 30.2%,西班牙裔和其他种族/族裔的警官分别为 27.7%和 22.1%。我们还对性别进行了类似的分析,如图 5B 所示:

作者图片
通过这个横条图,我们发现 31.9%的男警官抱怨过对他们过度使用武力,24.3%的女警官紧随其后。从这个角度来看,几乎三分之一的男性警官抱怨对他们过度使用武力。
重要性
在我们的整个项目中,我们的团队强调了芝加哥警察局在种族/民族和性别方面的诸多差异,如下所列:
- 当比较芝加哥警察局和芝加哥市时,在种族/民族人口统计方面存在显著差异。
- 军官晋升的可能性存在种族和性别偏见:白人军官和男性军官更有可能获得晋升。
- 当谈到一名官员在防止歧视委员会任职的年数时,存在种族/民族和性别差异。
- 种族/族裔群体之间在警察部队的高级别可能存在收入不平等。我们发现不同性别的官员之间的工资没有显著差异。
- 大约三分之一的男警官和 24.3%的女警官被投诉对他们过度使用武力, 98.2%的过度使用武力投诉没有导致警官受到申斥。
作为一个团队,我们集思广益,为这些主要差异和对被指控过度使用武力的官员采取的最低限度的纪律处分提出了一些可能的解决方案。首先,为了促进芝加哥警察局内部的多样性,我们认为实施强制性的多样性培训,并雇用代表芝加哥人口的警官将是有益的。此外,通过使用人体摄像机和公民审查委员会来加强对过度使用武力的问责至关重要。纳入这些措施将促进芝加哥市警务的平等和公平,为美国的公平警务开创先例。
关于项目/作者
该项目是作为加州大学圣地亚哥分校数据科学学生协会项目委员会的一部分创建的。你可以在我们的 GitHub 点击查看我们的完整分析、视频演示和代码。欢迎在下面留下您的想法,如果您有任何问题,请通过 LinkedIn 联系我们!团队成员包括:
Aryaman Sinha (团队负责人,加州大学圣地亚哥分校二年级数据科学专业和认知科学辅修专业)
Koosha Jadbabaei(数据科学实习生、本科研究学者、加州大学圣地亚哥分校数据科学专业二年级学生)
夏嫣·加格(加州大学圣地亚哥分校数据科学专业一年级学生)
用 Faker 伪造(几乎)一切
原文:https://towardsdatascience.com/fake-almost-everything-with-faker-a88429c500f1?source=collection_archive---------20-----------------------

dasha shchukova 在 Unsplash 上拍摄的照片
最近,我的任务是创建一些随机的客户数据,包括姓名、电话号码、地址和其他常见的东西。起初,我想我只是生成随机的字符串和数字(一些胡言乱语),然后就到此为止。但是我记得我的同事们用了一个包。我知道,总有一个包裹可以装下所有的东西,嗯,几乎所有的东西。
不管怎样,我想我要试一试。这些天我开始写一些严肃的 Python 代码,并认为探索 Python 可用的各种包是个好主意。我执行了 pip 命令,下载了这个包,并开始在我的 CSV 文件中随机生成一些人。很有趣。所以我想我会记录这个过程,因为鉴于我的历史,我肯定会忘记 Faker。
安装 Faker
安装 Faker 与使用 pip 安装任何其他 Python 包没有什么不同。您可以使用以下任何一个命令来安装 Faker。
pip install faker
pip3 install faker
python -m pip install faker
python3 -m pip install faker
根据您安装的 Python 版本,使用适当的命令来安装 Faker 包。不会超过几分钟。
将 Faker 导入您的代码并初始化它
将 Faker 包导入到您的代码中也没有什么不同。只需在 Python 文件的开头添加下面的 import 语句,就可以开始了。
from faker import Faker
一旦导入了包,就需要创建 Faker 类的对象。您可以使用以下命令来实现这一点。不过,locale 参数是可选的。你可以跳过它,你会完全没事的。
faker = Faker(locale='en_US')
让我们先看看它能做什么
在深入研究代码之前,让我们先看看它能为我们做什么。
My name is Mx. Linda Dunn III , I'm a gender neutral person. You can call me at 001-099-311-6470, or email me at caroljohnson@hotmail.com, or visit my home at 2703 Fitzpatrick Squares Suite 785 New Crystal, MN 18112 My name is Dr. John Harris MD , I'm a male. You can call me at (276)611-1727, or email me at combstiffany@brown-rivers.org, or visit my home at 7409 Peterson Locks Apt. 270 South Kimfurt, IL 79246 My name is Dr. Ann Huynh DVM , I'm a female. You can call me at 543.024.8936, or email me at timothy30@shea-poole.com, or visit my home at 5144 Rubio Island South Kenneth, WI 22855
这是我编写的一个简单 Python 脚本的输出,用来生成假的客户数据或假的人。看着这个,它看起来是如此的逼真。我用来得到这个输出的代码如下:
from faker import Fakerfaker = Faker(locale='en_US')print("My name is %s %s %s , I'm a gender neutral person. You can call me at %s, or email me at %s, or visit my home at %s" %(faker.prefix_nonbinary(), faker.name_nonbinary(), faker.suffix_nonbinary(), faker.phone_number(), faker.ascii_free_email(), faker.address()))print("My name is %s %s %s , I'm a male. You can call me at %s, or email me at %s, or visit my home at %s" %(faker.prefix_male(), faker.name_male(), faker.suffix_male(), faker.phone_number(), faker.ascii_company_email(), faker.address()))print("My name is %s %s %s , I'm a female. You can call me at %s, or email me at %s, or visit my home at %s" %(faker.prefix_female(), faker.name_female(), faker.suffix_female(), faker.phone_number(), faker.company_email(), faker.address()))
你现在可以看到产生大量的虚假客户是多么容易,当然是为了测试。乐趣并没有在这里结束。从那来的还有很多。您可以生成整个公司,例如:
The company I just created!David PLCProviders of Horizontal value-added knowledge userPhone: 001-891-255-4642x93803Email: ksanchez@cochran.com234 Torres PortsWest Rhonda, AL 96210
从上面的输出可以看出,我们为用户提供了一些很好的横向增值知识。那应该是公司的口头禅。
我不骗你,有一种方法叫做 bs() 。我不知道你什么时候用过,但是你可以随时调用 Faker 的 bs()。看到我做了什么吗?
这有什么帮助?
我以为你已经明白这一点了。无论如何,当你需要数据来测试,并且你需要这些数据尽可能真实(或者尽可能真实)的时候,你可以使用 Faker 来轻松快速的生成测试数据。
其实我对我最后一句话里的“迅速”部分不太确定。生成数据肯定很容易。而是生成一百万条包含名字、姓氏、电子邮件、电话等的客户记录。,在 2019 年的 16 英寸基本型号 MacBook Pro 上花费了近 350 秒。所以你想怎么做就怎么做。
摘要
尽管如此,这绝对是一个非常方便和有趣的软件包。你可以很容易地产生任意数量的客户或朋友(无论你如何摇摆),每个人都有完整的离线和在线资料。你可以生成家庭电话和电子邮件,工作电话和电子邮件,家庭住址,工作地址,兴趣,个人资料,信用卡,车牌号码,等等。所以一定要去看看包的 Github repo ,四处看看,然后带着它转一圈。源代码也很容易理解。
如果你喜欢你在这里看到的,或者在我的个人博客和 Dev。要写博客,并希望在未来看到更多这样有用的技术帖子,请考虑在 Github 上关注我。
原载于 2021 年 9 月 30 日 https://blog.contactsunny.com**的 。
假鸟和机器学习
原文:https://towardsdatascience.com/fake-birds-machine-learning-acc553312a8f?source=collection_archive---------27-----------------------
使用流行的鸟类品种数据来演示最近邻分类
介绍
本文使用虚构的数据来说明 k-最近邻分类算法,这些数据是使用以前的文章中的代码生成的。读者可以将本文作为使用 k-最近邻算法执行分类算法的指南。
本文附有一个 Jupyter 笔记本和一个 YouTube 教学视频。我将这些附加资源的链接放在了文章的末尾。
该算法
k-最近邻算法是可用于分类的最简单的监督机器学习算法之一。通常,k-最近邻算法的性能与许多其他更复杂的机器学习算法选项一样好。
这种算法的基本原理是将观察值放在多维空间中。然后使用距离测量,以便识别多维空间中“附近”的其他观察。邻近性作为相似性的度量。
该算法假设任何数据点都可能与附近的其他数据点属于同一类。同名的 k 是一个指定邻居数量的参数。
这里显示的前一篇文章说明了距离度量如何测量相似性。
图 1 展示了我们熟悉的毕达哥拉斯定理是如何测量距离和相似性的。

图 1:作者的插图,粗略地说明了勾股定理如何测量距离,以及相似性
数据
这些数据(BirdVarietyData_March2021.csv)包括两千次观测。我在 GitHub 主持这个数据。这些数据是虚构的,与一种有两个变种的鸟有关。有西方品种和东方品种。有如下六个变量:
lat : The latitude at which the bird was observed.
long : The longitude at which the bird was observed.
weight : The weight of the bird (pounds).
wing : The wing span of the bird (inches).
color : The color of the bird (yellow, black, or white).
variety : The variety of the bird.
图 2 显示了数据的摘录。

图 2:摘自 BirdVarietyData_March2021.csv 作者插图。
图 3 显示了数据的概况。

图 3:作者对 BirdVarietyData_March2021.csv 的图解。
任务
本文以及这里显示的任务旨在训练一个机器学习模型,该模型可以根据鸟类的重量、长度、位置和颜色来预测物种多样性。
获取&审核数据。熊猫可以很容易地从互联网上下载数据,而不必先在浏览器或查找器中下载本地副本。
# Shown in multiple lines to conserve line width.
web_root = 'https://raw.githubusercontent.com/'
location = 'adamrossnelson/BirdsDataCreation/main/'
filename = 'BirdVarietyData_March2021.csv'df = pd.read_csv(web_root + location + filename)
有关数据的回顾,请参见上面的图 2。
对分类数据进行编码。为了处理鸟的颜色,我们需要对类别进行编码。至少有两种选择可以在一个热编码方案中对这些数据进行编码。第一种选择是使用列表理解:
*# One Hot Encoding with list comprehension.*
df['isBlack'] = [1 **if** x == 'Black' **else** 0 **for** x **in** df['color']]
df['isWhite'] = [1 **if** x == 'White' **else** 0 **for** x **in** df['color']]
df['isYellow'] = [1 **if** x == 'Yellow' **else** 0 **for** x **in** df['color']]
另一个选项,在 Jupyter 笔记本和下面链接的视频中显示,是使用pd.get_dummies()。任一选项都会产生相同的结果,如图 4 所示(参见最右边的三列中的一个热编码变量)。

图 4:鸟类颜色的一次热编码后的数据回顾。吹气框中最右边的三列显示了一种热编码鸟的颜色。作者插图。
标准化&刻度数据。缩放数据是重要的一步。该步骤确保每个预测变量在预测中的权重相等。就像执行一个热编码一样,有多种方法可以缩放数据。本文将展示如何通过sklearn.preprocessing用 z 分数来缩放数据。随附的 Jupyter 笔记本演示了另一种方法。
**from** **sklearn.preprocessing** **import** StandardScaler
scaler = StandardScaler()
scaler.fit(df.drop(['variety','color'], axis=1))
scaled_features = scaler.transform(df.drop(['variety',
'color'], axis=1))
训练,测试,&拆分数据。训练,测试,&拆分技术是一种留出一部分训练数据用于测试的技术。在搁置测试数据上测试和评估模型提供了机器学习模型在其他看不见的数据上表现如何的真实视图。这种技术可以防止过度配合。
有多种方法可以有效地训练、测试和分割数据。这篇文章展示了如何在sklearn.model_selection的帮助下完成任务。这种技术还使用了 Python 中一种流行的技术,称为“多重赋值”,如下所示。
**from** **sklearn.model_selection** **import** train_test_splitX_train, X_test, y_train, y_test = train_test_split(scaled_features,
df['variety'],
test_size=0.2,
random_state=1234)
在这个代码之后,我们有四个新的数据帧。我们将使用X_train和y_train来训练机器学习模型。然后我们将使用X_test和y_test来测试和评估模型。上面的代码留出 20%的观察值用于测试,80%的观察值用于训练。
训练模型。这个过程的最后一步是训练模型。该步骤包括使用sklarn.neighbors.KNeighborsClassifier的辅助。
**from** **sklearn.neighbors** **import** KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)
knn = KNeighborsClassifier(n_neighbors=1)
做出预测&评估结果。用X_train和y_train训练模型后,我们用X_test进行新的预测。将新的预测存储在pred中后,我们可以将这些结果与y_test进行比较,以评估模型的性能。
pred = knn.predict(X_test)
pd.crosstab(pred, y_test)
上面的代码生成一个交叉列表,让我们将预测的变化(pred)与实际的变化(y_test)进行比较,并生成以下输出

图 5:作者举例说明了比较预测数据和测试数据的交叉列表。
还有更复杂的工具和技术可以改进模型评估,我在附带的 Jupyter 笔记本和视频中展示了这些工具和技术。
寻优 K. 同名的 k 是邻居的数量。该算法的性能将随着邻居数量的变化而变化。如图 6 所示,当查看第一个最近邻时,模型显示的错误率在 0.15 到 0.16 之间。当查看两个最近的邻居时,错误率增加到 0.18 到 0.19 之间。之后,当查看 29 个最近的邻居时,错误率稳定地下降到大约 0.12。

图 6:作者对模型错误率的说明。
随附的 Jupyter 笔记本和视频展示了如何生成类似于图 6 中的视觉效果。使用笔记本和视频中讨论的技术进一步检查模型性能。
https://adamrossnelson.medium.com/membership
额外资源
Jupyter 笔记本支持并进一步展示了本文中介绍的代码和技术。对于那些对更具互动性和视听效果的指南感兴趣的人,我还准备了一个 YouTube 教学视频:
感谢阅读
感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。Twitter:@ adamrossnelsonLinkedIn:Adam Ross Nelson 在 Twitter 和脸书: Adam Ross Nelson 在脸书。
假工作预测器
原文:https://towardsdatascience.com/fake-job-predictor-a168a315d866?source=collection_archive---------9-----------------------
使用文本和数字数据识别欺诈性职位发布的分类器。
在封锁期间,我花了一些时间研究一个项目——“假工作预测器”我找新工作的时候,做这样的预测器似乎是个好主意。Github 链接提供了相关代码—https://Github . com/anshupriya 2694/Fake-Job-Posting-Prediction。
定义
介绍
就业诈骗呈上升趋势。根据美国消费者新闻与商业频道的数据,2018 年的就业诈骗数量比 2017 年翻了一番。当前的市场形势导致了高失业率。对于许多人来说,经济压力和冠状病毒的影响已经大大减少了就业机会和失业。像这样的案件给骗子提供了一个合适的机会。许多人成为这些骗子的牺牲品,他们利用前所未有的事件引起的绝望。大多数骗子这样做是为了从他们诈骗的人那里获得个人信息。个人信息可能包含地址、银行账户详情、社会保险号等。我是一名大学生,收到过几封这样的诈骗邮件。骗子为用户提供一个非常有利可图的工作机会,然后要求金钱作为回报。或者他们要求求职者投资并承诺给他们一份工作。这是一个危险的问题,可以通过机器学习技术和自然语言处理(NLP)来解决。
本项目使用 Kaggle 提供的数据。此数据包含定义职务公告的功能。这些招聘信息被分为真假两类。虚假招聘信息只是这个数据集中的一小部分。这是意料之中的事。我们不希望看到很多虚假的招聘信息。这个项目分五个阶段。本项目采用的五个步骤是
- 问题定义(项目概述、项目陈述和指标)
- 数据收集
- 数据清理、探索和预处理
- 建模
- 评价

发展阶段
问题陈述
这个项目旨在创建一个分类器,将有能力识别假和真的工作。基于两种不同的模型来评估最终结果。由于所提供的数据具有数字和文本特征,一个模型将用于文本数据,另一个用于数字数据。最终的输出将是两者的结合。最终模型将接受任何相关的职位发布数据,并产生最终结果来确定该职位是否真实。
韵律学
基于两个指标对模型进行评估:
- 准确度:这个公式定义了这个度量-

如公式所示,该指标产生所有正确分类的数据点与所有数据点的比率。这是非常有用的,因为我们试图识别真实和虚假的工作,而不是只有一个类别是必不可少的。然而,这种方法有一个缺点。机器学习算法往往倾向于优势类。由于我们的类别是高度不平衡的,高精度将代表我们的模型对负面类别(真实工作)的分类有多好。
- F1-Score : F1 分数是模型在数据集上的准确性的度量。这一指标的公式是–

使用 F1 分数是因为,在这种情况下,假阴性和假阳性都是至关重要的。该模型需要识别具有最高可能分数的两个类别,因为这两个类别都具有高成本。
分析
数据探索
本项目数据可在 ka ggle—https://www . ka ggle . com/shivamb/true-or-fake-fake-job posting-prediction获取。数据集由 17,880 个观测值和 18 个要素组成。
数据是整数、二进制和文本数据类型的组合。变量的简要定义如下:

表:功能描述
因为大多数数据类型不是布尔就是文本,所以这里不需要汇总统计。唯一的整数是 job_id,它与此分析无关。进一步研究数据集以识别空值。

空值计数
department 和 salary_range 等变量有很多值缺失。这些列不再进一步分析。
在对数据集进行初步评估后,可以看出,由于这些招聘信息是从几个国家提取的,所以招聘信息使用了不同的语言。该项目使用了来自美国的数据,这些数据占了数据集的近 60%。来自美国各地的数据确保所有数据都是英文的,以便于理解。此外,该位置被分为州和城市,以便进一步分析。最终数据集有 10593 个观测值和 20 个要素。
该数据集非常不平衡,9868 个(93%的工作)是真实的,只有 725 个或 7%的欺诈性工作。相同的计数图可以非常清楚地显示差异。

探索性分析
本项目中可视化数据集的第一步是创建一个相关矩阵来研究数值数据关系。相关矩阵在数字数据之间没有表现出任何强的正相关或负相关。

相关矩阵
然而,布尔变量远程办公有一个有趣的趋势。当这两个变量的值都等于零时,这份工作有 92%的可能是欺诈性的。在数字特征之后,探索该数据集的文本特征。我们从这个位置开始探索。

各州的工作计数
上图显示了哪些州创造了最多的工作岗位。加州、纽约和德克萨斯州的职位发布数量最多。为了进一步研究这一点,创建了另一个条形图。这个条形图显示了前 10 个州的假工作和真工作的分布情况。

假工作和真工作按州统计
上图显示,德克萨斯州和加利福尼亚州比其他州出现假工作的可能性更高。为了更深入地挖掘并包括各州,创建了一个比率。这是一个基于州和城市的虚假的真实的工作比率。以下公式用于计算每个真实职务有多少个假职务:

只有大于或等于 1 的比值绘制如下。

假工作与真工作的比率
在加州,贝克斯菲尔德的假工作与真工作之比为 15:1,德克萨斯州达拉斯的假工作与真工作之比为 12:1。来自这些地方的任何招聘信息都很有可能是虚假的。进一步探究其他基于文本的变量,以可视化任何关键关系的存在。

上面的图表显示,大多数欺诈性工作属于全职类别,通常是要求学士学位或高中教育的入门级职位。
基于文本的类别被合并到一个称为文本的字段中,以进一步扩展对文本相关字段的分析。组合字段包括——职位、位置、公司简介、描述、要求、福利、必需经验、必需教育、行业和职能。一个描述字符数的直方图被用来可视化真实工作和虚假工作之间的差异。可以看到的是,即使真实工作和虚假工作的字符数相对相似,但真实工作的频率更高。

真实和虚假招聘信息中出现字符的频率
算法和技术
根据初步分析,很明显,文本和数字数据都将用于最终建模。在数据建模之前,确定最终数据集。该项目将使用具有以下特征的数据集进行最终分析:
- 远程办公
- 不诚实的
- 比率:基于位置的虚假与真实工作比率
- 文本:头衔、地点、公司简介、描述、要求、福利、必需的经验、必需的教育、行业和职能的组合
- character_count:文本数据字数统计直方图中的字数
在将文本数据用于任何数据建模之前,需要进一步的预处理。
项目中使用的算法和技术有:
- 自然语言处理
- 朴素贝叶斯算法
- SGD 分类器
比较朴素贝叶斯和 SGD 分类器的准确性和 F1 分数,并选择最终模型。朴素贝叶斯是基线模型。使用它是因为它可以根据每个事件发生的概率计算两个事件的条件概率;将这些概率编码是非常有用的。一个比较模型,SGD 分类器,实现了一个简单的随机梯度下降学习例程,支持不同的损失函数和分类惩罚。当分类不正确时,这个分类器将需要很高的惩罚。这些模型分别用于文本和数字数据,最终结果被合并。
基准
这个项目的基准模型是朴素贝叶斯。该模型的总体精度为 0.971,F1-得分为 0.744。使用这种模型的原因已经在上面阐述过了。任何其他模型的能力将与朴素贝叶斯的结果进行比较。
方法学
数据预处理
对文本处理采取以下步骤:

- 标记化:文本数据被分割成更小的单元。在这种情况下,数据被拆分成单词。
- 降低:拆分的单词被转换成小写
- 停用词去除:停用词是对句子没有增加多少意义的词。例如,the,a,an,he,have 等。这些词被删除。
- 词形变化:词形变化组合在一起使用的过程。
履行
以下是该项目的实施示意图。数据集分为文本、数字和 y 变量。文本数据集被转换成术语-频率矩阵用于进一步分析。然后使用 sci-kit learn,将数据集分为测试数据集和训练数据集。基准模型 nave Bayes 和另一个模型 SGD 使用训练集进行训练,训练集占数据集的 70%。基于两个测试集(数字和文本)的模型的最终结果被组合,使得如果两个模型都说特定数据点不仅仅是欺诈性的,那么工作发布就是欺诈性的。这样做是为了减少机器学习算法对多数类的偏向。在测试集上使用经过训练的模型来评估模型性能。我们比较了两个模型(朴素贝叶斯和 SGD)的准确性和 F1 值,并选择了最终模型进行分析。

结果
模型评估和验证
用于此分析的最终模型是— SGD。这是基于与基线模型相比较的度量结果。基准模型和 SGD 的结果如下表所示:

根据这些指标,SGD 的性能比基准模型稍好。这就是最终模型被选为 SGD 的方式。
正当理由;辩解
如上所述,最终模型的性能优于基线模型的既定基准。该模型将能够以非常高的准确度识别真实的工作。然而,它对虚假职位的识别能力仍有待提高。
自由形式可视化
混淆矩阵可以用来评估项目的质量。该项目旨在识别真假工作。

上面的混淆矩阵显示了以下值——分类标签、标签下分类的数据点数量,以及每个类别中表示的数据百分比。测试集共有 3265 个真实作业和 231 个虚假作业。根据混淆矩阵,很明显,该模型在 99.01%的情况下识别出真正的工作。然而,欺诈性的工作只有 73.5%的机会被识别出来。只有 2%的情况下模型没有正确识别类别。这个缺点已经在前面讨论过,机器学习算法倾向于选择占优势的类别。
反射
虚假招聘是现实世界中一个至关重要的挑战,需要积极的解决方案。这个项目旨在为这个问题提供一个潜在的解决方案。对文本数据进行预处理以生成最佳结果,并选择相关的数值字段。多个模型的输出被组合以产生最好的可能结果。这样做是为了减少机器学习模型对主导类的偏见。
这个项目最有趣的部分是特定的地点是欺诈性工作的缩影。例如,加州贝克斯菲尔德的假工作与真工作之比为 15:1。像这样的地方需要一些额外的监控。另一个有趣的部分是,大多数入门级的工作似乎是欺诈性的。看起来,骗子们倾向于瞄准那些拥有学士学位或高中文凭、正在寻找全职工作的年轻人。最具挑战性的部分是文本数据预处理。数据的格式非常复杂。清理它需要很大的努力。
改进
这个项目中使用的数据集非常不平衡。大部分工作是真实的,很少有欺诈的。因此,真正的工作很容易找到。某些技术,如 SMOTE,可用于生成合成少数类样本。一个平衡的数据集应该能够产生更好的结果。
假新闻分类器,以解决新冠肺炎造谣-二
原文:https://towardsdatascience.com/fake-news-classifier-to-tackle-covid-19-disinformation-ii-116ed2eb44e4?source=collection_archive---------35-----------------------
努力解决当今世界面临的最紧迫的问题之一,假新闻。

(图片由作者提供)
介绍
冠状病毒(新冠肺炎)是一种导致持续疫情的传染病。中国武汉首次发现该病,2019 年 12 月发现首例。截至 2021 年 5 月 21 日,180 个国家和地区报告了超过 1.6 亿例病例。这个疫情的庞大规模给当代人带来了无数的问题。我遇到的一个严重问题是虚假新闻文章的传播,在当今世界,虚假新闻文章会引起恐慌和大规模歇斯底里。我意识到这个问题的严重性,并决定将我的下一个机器学习项目建立在解决这个问题的基础上。
这是我关于假新闻分类器项目的第二篇文章,将详细阐述使用 Flask 框架在 Heroku 上将经过训练的 SVM 分类器部署为 web 应用程序所需的步骤。那些没有读过我的第一篇文章的人可以通过下面的链接 来阅读。
问题陈述
开发一个 web 应用程序,可以准确地将一篇关于新冠肺炎的新闻文章分类为真实新闻或虚假新闻。
工作流程
在我的第一篇文章《假新闻分类器解决新冠肺炎虚假信息-I》中,我在 Google Colab 中进行了数据预处理、数据工程、模型训练、模型测试和模型评估。我的结论是,SVM 机器学习算法在对新闻文章进行真伪分类时提供了最佳结果。
由于最初的分析已经在第一篇文章中完成,下面描述的步骤将集中在使用 Flask 框架在 Heroku 上将经过训练的 SVM 模型部署为 web 应用程序。
步骤 1 :训练 SVM 机器学习模型,并将训练好的模型和 Tf-Idf 向量分别保存为 pickle 文件。
首先,我创建了一个名为 model.py 的 Python 脚本文件,并使用训练数据执行了训练 SVM 模型所需的所有必要步骤。详细的分步方法可以在本文的 第一部分 中找到。后来,训练的 SVM 分类器和 Tf-Idf 向量被保存为 pickle 文件。
第二步:创建一个 web 应用。
现在,我们已经保存了训练好的 SVM 分类器以及 Tfi-Idf 向量,是时候将我们的注意力转移到创建 web 应用程序上了。为此,我创建了一个名为 app.py 的 Python 脚本文件。App.py 充当我们的 web 应用程序的后端,负责处理用户输入的数据,并使用经过训练的 SVM 分类器对新闻文章进行分类。
另一个最重要的文件是 Index.html 文件。Index.html 是包含我们的 web 应用程序前端代码的文件,它与 app.py(我们的后端)通信,向用户显示结果。
步骤 3 :创建在 Heroku 上托管 web 应用程序所需的配置文件。
在我们将 web 应用程序托管到 Heroku 之前,需要一些重要的配置文件。第一个文件称为“proc file”。Procfile 用于指定用户访问 web 应用程序时需要首先执行的文件。procfile 包含以下配置语句。
web:gunicorn app:app
第一个参数“app”是需要首先执行的文件(app.py),而第二个参数“app”是 Flask(name)。第二个需要的配置文件是“requiremnts . txt”文件。这个文件是必需的,因为它使 Heroku 环境能够下载我们的 web 应用程序平稳运行所需的所有库。
步骤 4 :将整个代码提交给 GitHub 库。
下一步是在 GitHub 上创建一个新的资源库。这个库将连接到 Heroku 平台,Heroku 将从库本身访问所有需要的代码文件。我上传到 GitHub 资源库的文件有 app.py、model.pkl、tfidf.pkl、nltk.txt、requirements.txt、procfile 和 templates 文件夹。上传完成后,转到步骤 5。
步骤 5 :在 Heroku 上创建一个新账户,并将新建的 GitHub 库链接到你的 Heroku 账户。
1 -如果您还没有 Heroku 帐户,请创建一个并登录您的 Heroku 帐户。导航到 Heroku 仪表盘的右上角,点击“新建”,然后选择“创建新应用”。

(图片由作者提供)
2 -现在,输入您想要的应用程序名称,将地区设为美国,最后点击“创建应用程序”。

(图片由作者提供)
3-应用程序配置完成后,您将被重定向到新创建的 Heroku 应用程序的仪表板。在这里,我们将把这个应用程序连接到我们的 GitHub 存储库。向下滚动到部署方法并点击“连接到 GitHub”。

(图片由作者提供)
4-这将在“连接到 GitHub”选项下打开一个提示,要求您输入您的 web 应用程序的存储库名称。输入您的 GitHub 库的名称,点击“搜索”按钮,然后点击位于您想要的库旁边的“连接”选项。等待几秒钟,您的 web 应用程序存储库将连接到 Heroku 应用程序。

(图片由作者提供)
步骤 5 :使用 Heroku 部署 web 应用。
Heroku 提供了两个选项来完成 web 应用程序部署。这两个选项分别是自动部署和手动部署。对于这个项目,我决定选择手动部署。要执行手动部署,请向下滚动并导航到手动部署。确保存储库分支正确,然后点击“部署分支”。部署应该会自动开始,您可以在显示在构建部分下的日志文件中看到所有的安装。

(图片由作者提供)
如果安装成功,您将在构建日志的末尾收到一个 URL。此外,在 Deploy to Heroku 部分之后,您会收到一条消息,说明“应用程序已成功部署”。此 URL 可用于从世界任何地方访问 web 应用程序,您可以在您的同行之间共享它,并让他们测试部署的 web 应用程序。

(图片由作者提供)
结论
以下项目是我完成的第一个端到端机器学习项目,其中我成功地在 Heroku 云平台上部署了我的 SVM 分类器。端到端的机器学习项目打开了技术堆栈的几个方面,并帮助您参与应用程序的前端和后端。
在我看来,开发高度精确的机器学习模型如果停留在你的 Jupyter 或 Colab 笔记本上,是没有任何用处的。端到端项目提供了一个向广大最终用户展示您的模型的准确性的机会,从外行到有经验的 IT 专业人员。这种思想促使我强调,从今以后,我将专注于部署我承担的每一个机器学习项目。
整个项目可以在我的 Github 页面找到。我希望你喜欢看我的博客。
无处不在的假新闻:如何用 SOTA NLP 检测
原文:https://towardsdatascience.com/fake-news-everywhere-how-to-detect-it-with-sota-nlp-f2dc1e07247c?source=collection_archive---------29-----------------------
一个初学者友好的教程,有真实的世界,网络搜集的文章数据。探索一系列 SOTA 自然语言处理技术,以准确标记新闻文本是假的还是真的。

凯拉·贝拉斯克斯在 Unsplash 上的照片。com
目录
- 介绍
- 原始输入数据
- 探索性数据分析
- 特征工程
- 型号选择
- 结论
- 参考
介绍
作为互联网时代的副产品,社交媒体的崛起代表了 21 世纪最深远的趋势。当然,自从广泛采用以来,相当一部分社会生活转移到了网络空间,而几个世纪以前的人类交流涉及身体接触。其倡导者经常使用的一句话:“你的网络就是你的净值”,恰恰体现了这一点,也说明了我们赋予这一新的现代化方面多大的力量。从让用户建立社交网络、知识和新闻共享池、有组织的社区和亚文化,到释放利润丰厚的商业潜力。它永远改变了我们的生活。以至于现代劳动力别无选择,只能获得数字技能来挖掘其潜力。
然而,这还不是故事的全部。社交媒体带来的不仅仅是荣耀和魅力。今天存在的所有许多渠道,如 Instagram、脸书、Twitter、抖音和 scores more,不仅仅是连通性的绿洲。它们不只是作为虚拟贸易中心为我们的世界服务,完全有益。正如新奇事物的一贯情况一样,它也有消极的一面;而本教程旨在探讨其中之一:即假新闻传播。事实上,由技术驱动的假新闻泛滥对整个社会都有毁灭性的后果。因此,企业和政府有必要进一步发展现有的检测机制。
但是在开始研究这些数据之前,让我们先确定假新闻的定义。它经常被不恰当地用来排除不喜欢的新闻内容,事实上它并不那么容易定义。因此,我问自己为什么要堆积学术论文:是否有一个普遍接受的定义?如果是,如何处理报道的事实或数字中的非故意失误?它们也算假新闻吗?
最终,我意识到没有一个被广泛接受的定义。但是发表在数据科学的文本分类分支上的研究对此提供了一些明智的观点。例如,Rubin 等人描述了三种假新闻:
- 没有发表在任何主流新闻媒体上的严重捏造——由于其罕见性,使用网络搜集更难收集。
- 因其病毒性质而出现在多个平台上的独特创意恶作剧。
- 为娱乐目的而制造的幽默假新闻,就像讽刺作品一样。作者认为,包括这种类型会使算法更难用传统的分类方法检测前两种类型。
因此,我们可以得出结论,假新闻完全是捏造的,其传播者将党派内容作为事实呈现——主要在网络上传播,如博客、新闻网站和社交媒体平台。一般来说,有两个标准区分假新闻和真新闻:(1)首先,捏造从未以报道形式发生的事件,以及(2)第二,旨在赞扬或诋毁目标个人、组织和/或实体的党派因素。两者在传播虚假信息方面是一致的。
原始输入数据
数据科学竞赛网站 Kaggle 提供了数百个丰富的数据集,研究人员可以利用这些数据集达到任何可以想到的目的。你只需要注册一个账户,然后就可以开始搜索有趣的数据集了。我选择了下面链接的这个特定数据集,它收集了川普和希拉里争夺美国最高职位期间发表的新闻文章:(【https://www.kaggle.com/saratchendra/fake-news/metadata】T2
我敢肯定,有一个更老的提交了更多的细节,包括确切的时间范围内刮文章覆盖。回想起来,我们知道假新闻现象在特朗普 2016 年竞选总统后获得了最多的关注。他的竞选陷入了俄罗斯参与的指控,显然是以社交媒体机器人军队的形式帮助他获胜。出于这个原因,可以有把握地假设我们的数据集实际上与两篇学术论文中使用的更突出的数据集相同:(https://www . ka ggle . com/clmentbisaillon/fake-and-real-news-dataset?select=True.csv
运用熊猫的头部和信息方法给我们留下了良好的第一印象:

数据集的摘要信息。作者照片。

表 1:新闻文章数据集。作者照片。
探索性数据分析
这方面的第一步应该是仔细检查我们是否受到一个叫做阶级平衡的问题的影响。同样,检查数据是否在缺失值中表现出某种模式也是值得的。我们可以在下面的图中看到,新闻文章数据集是完全平衡的,因为一半的行是假新闻,另一半是真实的,这表明在其创建过程中有人干预。考虑到类别不平衡对预测算法的应用提出了巨大的挑战,这样的发现总是令人欣慰的。

图 1:阶级平衡的垂直条形图。作者照片。
现在转到我们旨在寻找丢失值的模式的努力,让我们使用下面的代码片段将丢失的行涂成黄色,将完美的行涂成蓝色。这种方法在处理时间序列数据时特别有用,因为用肉眼可以立即发现重复出现的模式。

图 2:缺失值热图。作者照片。
“作者”一栏中明显有大量的黄色,表明有很多值缺失。这是上一节已经提到的观察结果。作为一个问题更有意思的是:假新闻文章比真新闻文章更容易缺少对应的作者姓名吗?让我们对此进行研究,并进一步加深我们对数据集的理解。

看看作者一栏中出现频率最高的名字的水平条形图,我们确实可以确认假新闻文章中出现频率最高的名字是‘未知’。然而,需要注意的是,在运行可视化功能之前,我已经将丢失的值编码为“未知”。
这个特殊的可视化绘图函数是 Yellowbricks 的便捷文本可视化类的调整版本,称为令牌频率分布(https://www.scikit-yb.org/en/latest/api/text/freqdist.html)。
无论如何,对于真正的新闻文章来说,图片看起来怎么样?

前 50 名最常见的真实新闻文章作者中没有一个是“未知”的。这已经证实了许多学术研究中提出的一个观点:匿名往往是假新闻传播者的一个激励因素。因为他们在匿名的互联网世界中茁壮成长,在那里个人可以使用隐藏的身份,发布有问题的内容而不受影响。相当直观。但是我们能从这个探索性的分析中学到什么呢?简而言之,每当你看到一个由匿名人士运营的账户时,要知道假新闻传播的可能性很高。
特征工程
现在转到这个项目的特征工程部分,我们将从自然语言处理(NLP)的最新进展中探索一系列方法。作为机器学习和人工智能的一个分支,它见证了最近令人难以置信的进展。事实上,大约每年数据科学界都会为一种新技术的新闻感到高兴,这种新技术为 NLP 用例提供了最先进的(SOTA)工具包。
然而,这并不意味着所有可能出现的问题都是外包的。仍然需要做出艰难的选择,并且提取文本特征仍然很困难。从精致的正则表达式到处理复杂的概念,比如单词向量,这确实需要一些细致的工作。
文本分类面临的最大挑战之一是高维问题。与数字数据集类似,人们也可以从文本数据集中提取非常多的特征:在涉及新闻文章、社会互动和产品评论分析的任务中。有大量的习语、单词和短语需要处理,这通常会导致计算量很大的操作。对于这个项目,我遵循了 Ahmed 等人 2017 年的分类流程:

图 5:假新闻分类流程。作者照片。
支撑我的特征工程方法的主要公理很简单:真实的新闻文章不同于虚假的文章,因为作者遵循新闻的语言实践。这些记者中有许多是职业作家,他们非常注意如何表达自己。因此,我认为首先提取语言特征是明智的。以下是我首先提取的基本特征列表:
- 一篇文章由多少个字符组成
**word_count**:整体字数**nr_unique_words**:一篇文章中有多少独特的词语?- 这篇文章是由一个不知名的作者写的吗?
**common_nouns**:一篇文章中有多少常用名词?**proper_nouns**:一篇文章中有多少专有名词**proper_common_ratio**:专有名词与普通名词之比**num_exclamation_marks**:文章中感叹号的数量**nr_question_marks**:文章中问号的数量**readability score**:弗莱施·金凯阅读难易程度评分
之前的探索性数据分析表明,由未知作者撰写的内容是假新闻存在的强烈迹象。因此,任何由匿名人士撰写的文章都可能是在传播谎言。阅读文献还发现,假新闻文章往往在标题中包装很多,通常是个人、机构或政党的名称。因此,作为一种诱饵,专有名词比普通名词出现得更频繁。既然如此,我得出结论,检查一篇文章中普通名词和专有名词之间的比例可能是有用的。
此外,我在社交媒体领域经常遇到的是在正文中使用感叹号,这可能是暗示重要性的一种伎俩。这就是为什么我还决定收集作者使用感叹号的频率和问号的次数。除了这些简单的符号特征之外,我认为创建像 Flesch-Kincaid 阅读容易度评分这样的语言特征是很有意义的。写作无疑是一个风格问题,鉴于假新闻传播者会努力掩饰他们的谎言,他们的写作风格和语言用法可能会有所不同。初始特征数据帧由以下函数生成:
时不时地,特性工程确实会变成一种高强度低产出的活动。看一看“标签”列(其中 1 表示文章是假新闻,0 表示不是假新闻)和工程特征之间的皮尔逊相关系数,我们可以看到,只有未知作者和独特字数列似乎具有任何有意义的影响。其余的比随机噪声稍微好一点。然而有趣的是,我们仍然可以看到一些值得注意的结论。总结一下:
- 文章中感叹号用的越多,越有可能是假新闻。
- 专有名词(人名、地名、宗教名、建筑名)越多,文章越有可能是假新闻。这意味着文献中的 clickbait 假设适用于我们的数据集。
- 作者使用的独特词汇越多,他/她发布假新闻的可能性就越小。

图 6:皮尔逊特征重要性图。作者照片。
但是相关性并不像古老的统计学格言所说的那样是因果关系。我们必须超越单纯的相关分析,使用复杂的算法来建立关系模型。有鉴于此,在进入模型选择部分之前,我决定加入一个强大的 NLP 技术来弥补手工设计的特性的解释能力。这个技巧叫做单词向量,SpaCy 库中的一个内置功能。那么,什么是词向量呢?
在介绍这些神奇的单词向量之前,让我们先了解一下语义相似性的思想。琢磨一分钟,有些词的意思不是比其他词更接近对方吗?例如,如果你邀请一个朋友去吃一顿美味的汉堡餐(向你们当中的素食主义者和纯素食主义者道歉),你能不能不说:“嘿,我们去快餐店好吗?”理论上这包括汉堡餐吗?看看下面的图片,这个想法会变得更加清晰。

图 7:词向量的线图。作者照片。
单词向量是由统计模型计算的文本数据的数字表示,该统计模型倾向于使用相似性度量(例如余弦相似性)来表示某些单词在意义上彼此有多接近。在上图中,我们可以看到男孩和女孩这两个词很相似,公主和王子这两个词也很相似。在 SpaCy 的 NLP 工具包中,相似性值的范围在 0(无相似性)到 1(完全相似性)之间。见下图功能:
使用 SpaCy 中名为“en_core_web_md”的海量词汇中的词向量,通过 访问。矢量 属性,我已经为我们的假新闻预测任务多生成了 300 个特征。随后,我将 word vector dataframe 与我们手工设计的功能结合起来。现在让我们看看我们的混合结构在将新闻文章分类为假新闻或真新闻的实际任务中表现如何。
型号选择
在学术文献中,文本分类专家采用了许多建模技术。从逻辑回归(LR)到朴素贝叶斯(NB)和神经网络(NN)模型,一切都已经在假新闻数据集上进行了尝试和测试,无论是人工构建的还是从真实世界中搜集的数据集。但在最终开始预测 Kaggle 的假新闻文章并选择一系列模型之前,我们必须将数据分为训练集和测试集。我做了一个艰难的选择,随机选择 20k 行中的 30%作为测试数据集。
与我读过的许多学术研究相比,这是一个相当大的数据集。没有任何不尊重,但只阅读方法部分,你会注意到数据集越小,准确性和结果越好。下面是我用来运行 4 种算法的代码片段:逻辑回归分类器、决策树分类器、随机森林分类器和 XGB 分类器。最后一个模型——其参数未经调整——在 30%划分的测试数据集上表现最佳,准确率为 76% 。
我艰难地认识到,在野外发现假新闻实际上是一件非常困难的事情。当然,我的准确性只反映了我手动提取的语言特征和来自 SpaCy 的单词向量。通过部署 n-gram 模型、TF-IDF 矢量化技术、更先进的神经网络以及对我的算法进行详尽的超参数调整,我可能会超过 80% 的准确度标志。但那是以后的事了,因为我的文章已经超过了推荐的 5 分钟阅读时间。

表 2:建模输出。作者照片。

图 XGB 分类器模型的混淆矩阵。作者照片。
结论
检查我们的获胜算法 XGB 分类器的混淆矩阵,揭示了所有测试模型的症状问题。因此,我们可以得出结论,检测真实新闻比检测假新闻容易得多。因为 86%的真实新闻被正确预测为真实新闻。相比之下,只有 14%被标记为假阳性。作为一个题外话,重要的是要注意,我们希望在混淆矩阵图中看到更多的黄色和暗紫色。
但这一画面与我们希望看到的假新闻大相径庭。不幸的是,我们的算法只正确地发现了 66%的假新闻,而它将 34%的真实新闻标记为假新闻,这说明了最大的挑战所在。许多假新闻伪装得如此之好,以至于需要更有效的模型来划清界限。
然而,另一个我不知道的可能性是,讽刺文章与 20,000 篇文章混合在一起,使大多数选定的算法偏离了更准确的检测路径。然而,不管是哪种情况,通过学术论文和真实世界的数据,我在这次旅程中学到了很多关于假新闻的知识。
事实上,随着互联网和社交媒体的使用成为全球各个角落的现实,假新闻的传播也将成为一个更具破坏性和普遍性的问题。数亿人已经通过社交媒体平台上的庞大网络联系在一起。此外,尽管这些平台对发布的内容有调节机制,但仍然有足够的空间传播有害内容:这刺激了虚假新闻的传播,以获取政治或经济利益。
在本文中,您了解了一些用于检测此类内容的技术。在随后的后续文章中,我将探索更多的方法,并将它们与这里使用的方法进行对比,可能作为更高级的扩展。希望将来我也能写更多初学者友好的帖子。
通过在媒体上关注我或者订阅我的博客,让我知道你的想法。我的推特账号是 @warsame_words ,我欢迎反馈和建设性的批评——对于后者来说, LinkedIn 是一个受欢迎的渠道。谢谢你陪我走完这段旅程。
参考
- 利用机器学习检测假新闻:2020 年系统性文献综述 Ahmed 等人康奈尔大学计算机与社会。arXiv:2102.04458 [cs。CY]
- Ahmed,h .,Traore,I .,和 Saad,S. (2017 年)。使用 n-gram 分析和机器学习技术检测在线假新闻。分布式和云环境中的智能、安全和可靠系统国际会议论文集,127–138,加拿大温哥华施普林格,2017。https://doi.org/10.1007/978-3-319-69155-8_9↩
- 社会媒体上的假新闻检测:数据挖掘视角,舒,斯利瓦,王等,2017 年。https://dl.acm.org/newsletter/sigkdd
- J.C. S. Reis,A. Correia,F. Murai,a .贝洛索和 F. Benevenuto,“假新闻检测的监督学习”,载于 IEEE 智能系统,第 34 卷,第 2 期,第 76-81 页,2019 年 3 月-4 月,doi:10.1109/mis . 2019 . 289928993
- Rahul Agarwal 使用并行处理使您的熊猫应用更快https://towards data science . com/Make-Your-own-super-Pandas-using-multi proc-1c 04 f 41944 a 1
- 周,扎法拉尼,舒,2019。ASONAM ' 19:2019 年 IEEE/ACM 社交网络分析和挖掘进展国际会议论文集 2019 年 8 月 436–439 页 https://doi . org/10.1145/33342436
Faker:一个惊人且非常有用的 Python 库
原文:https://towardsdatascience.com/faker-an-amazing-and-insanely-useful-python-library-b313a60bdabf?source=collection_archive---------29-----------------------
编程;编排
快速为项目创建虚拟数据,与世界自由共享,等等

格尔德·奥特曼在 Pixabay
创建数据集!分析一下!并与世界分享!
寻找数据集总是数据分析项目的第一步,除非提供了数据集。
满足我们需求的数据可能很昂贵,很难收集,或者根本就不可用。
其次,即使你收集了数据,在与他人分享数据时也会受到⚠️版权的限制。
因此,在接下来的 3 分钟里,我们将通过创建我们自己的虚拟美味数据集来解决这个问题!!💯
我们所需要的是 python 包随机,熊猫和 Faker!!
假装直到你成功。
——史蒂芬·泰勒
骗子
Faker 是一个 Python 包,它为你生成假的或虚假的数据。
它的用法也很简单。
仅供第一次使用,安装软件包 Faker 如下所示。
pip install Faker
一旦安装成功,是时候试用了!!🚀
首先使用faker.Faker()创建并初始化一个生成器,它将通过访问各种属性来生成数据。

Python 包 Faker |作者图片
fake是上图中的生成器,它访问姓名、地址、工作、电话号码等各种属性,并生成虚拟数据。
先行一步,Faker 还支持不同的https://faker.readthedocs.io/en/master/locales.html🌎然后也可以生成区域特定的属性。一切都是假的!!
你需要做的就是为新的区域创建一个新的生成器。下图比文字更能说明问题。

使用 Faker |作者图片创建虚假数据
此外,这个包不仅限于字符串和数字数据类型,还可以生成日期时间数据!⏰
**fake.date()
>>> '1998-04-02'**
此外,还可以生成给定两个日期之间的日期。

使用 Python Faker | Image by Author 生成日期时间数据
将 Faker 包与 Pythonrandom和 Python 熊猫结合,可以创建一个完全虚拟的数据集!🎁
这里是如何→

如何使用 Python | Image by Author 制作假数据集
如果你想自己尝试,这里有代码→
官方文献记载这里是https://faker.readthedocs.io/en/master/index.html。
总结一下,
我希望,你们都准备好用 Faker 创建自己的数据集,并与世界分享,而不用担心版权问题!🏆
现在,你可以通过 在这里报名 成为媒介会员,阅读我和其他作家发表的所有故事。如果你这样做,我会得到一小部分你的费用,没有额外的费用给你。欢迎加入我的电子邮件列表来了解我写作的最新进展。
我这里有更多有用的文章→
**1\. [5 Most Powerful One-Liners You Should Know in Python Programming](/5-most-powerful-one-liners-you-should-know-in-python-programming-c9d49a89b7f3?source=your_stories_page----------------------------------------)
2\. [4 Awesome Pandas Methods To Quickly Analyze Any Dataset](/4-awesome-pandas-methods-to-quickly-analyze-any-dataset-65d2252af6e8?source=your_stories_page----------------------------------------)
3\. [3 (and Half) Powerful Tricks To Effectively Read CSV Data In Python](/3-and-half-powerful-tricks-to-effectively-read-csv-data-in-python-c4699a4f9d07?source=your_stories_page----------------------------------------)
4\. [3 Time-Saving Python Tricks](/3-time-saving-python-tricks-a017f4215aeb?source=your_stories_page----------------------------------------)**
感谢您的阅读和投入时间!
再次爱上 SQL
原文:https://towardsdatascience.com/falling-in-love-with-sql-again-169d7fa56e47?source=collection_archive---------9-----------------------
通过利用公共表表达式的能力

乔恩·泰森在 Unsplash 上的照片
当我在 15 年前第一次学习 SQL 时,我惊讶于它是如此的有用和强大。我恋爱了。但是多年来,随着我开始编写更复杂的查询,我的代码开始变成一堆难以维护的连接和嵌套子查询。如果我需要编辑几周前写的一个查询,我的眼睛会变得呆滞,我会放弃理解它,我会从头开始写一个新版本。SQL 正在失去它的魅力。
然后,几年前令人惊奇的事情发生了。我在一个随机的博客上偶然发现了示例 SQL 代码,它使用了这个叫做公共表表达式的特性。SQL 又长又复杂,但易于阅读和理解。我立刻就被吸引住了。从那以后,我完全改变了思考和编写 SQL 的方式。
有哪些常见的表格表达式?
通用表表达式,或 CTE s,基本上是一种在查询顶部列出一个或多个选择表达式并在以后通过名称引用它们的方法,就像你处理一个表一样。它们在概念上类似于创建临时表,但是更容易使用并且开销更低。
WITH
cte1 as (SELECT …),
cte2 as (SELECT …)
SELECT * FROM cte1 JOIN cte2
在这样一个虚构的例子中,很难完全理解 cte,所以让我们来看一个更真实的用例。
学生考试成绩示例
比方说,我想找到加利福尼亚州学生的平均最高数学考试分数。思考这个问题,我基本上需要做以下几个步骤:
- 获取学生子集(加利福尼亚)
- 获取测试分数的子集(数学)
- 加入他们在一起,以获得所有加州学生的数学考试成绩
- 获得每个学生的最高分
- 取整体平均值
为了让这个变得更有趣一点,我将假设我们不直接存储学生的地理位置,而是必须加入他们的学校才能算出来。
我将回顾我解决这个问题的老方法(在我了解 cte 之前)和我的新的改进方法。
旧方法(没有 cte)
我将首先让加州所有的学生。就像我上面提到的,我们需要将学生加入他们的学校,以便确定他们的位置:
SELECT students.id
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
然后,我可以加入数学考试的结果:
SELECT students.id**, test_results.score**
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
**JOIN test_results ON (
students.id = test_results.student_id
AND test_results.subject = 'math'
)**
请注意,我们可以反过来做——从数学测试结果开始,加入加州学生。两者同样有效。至此,我已经掌握了所有加州学生的数学成绩。为了只选择每个学生的最高分,我可以添加一个最高分,并按以下方式分组:
SELECT students.id, **MAX(**test_results.score**) as score**
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
JOIN test_results ON (
students.id = test_results.student_id
AND test_results.subject = 'math'
)
**GROUP BY students.id**
现在,每个学生都有一行他们最好的数学成绩。最后,我将把它放在一个外部选择中,以获得总体平均值:
**SELECT AVG(score) FROM (**
SELECT students.id, MAX(test_results.score) as score
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
JOIN test_results ON (
students.id = test_results.student_id
AND test_results.subject = 'math'
)
GROUP BY students.id
**) tmp**
我们完事了。最终结果还不算太糟,但是如果没有上下文,阅读和理解起来还是有点吓人。添加更多的嵌套级别、连接和条件只会让事情变得更糟。
值得注意的一件有趣的事情是,每一步都倾向于在 SQL 的开头和结尾添加代码——换句话说,逻辑是由内向外分层流动的。
新方法(使用 cte)
我会以同样的方式开始,选择加州的所有学生。为了使用公共表表达式,我给这个 SELECT 语句起了个名字,并把它添加到查询顶部的带有子句的中:
**WITH
student_subset as (**
SELECT students.id
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
) **),**
现在我可以对考试成绩做同样的事情。与我以前的方法不同,我现在不担心连接。我只是为所有学生选择一个独立的数学考试成绩数据集。
WITH
student_subset as (
SELECT students.id
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
), **score_subset as (
SELECT student_id, score
FROM test_results
WHERE subject = 'math'
),**
获得这两个独立的数据集后,我可以使用另一个 CTE 将它们连接在一起:
WITH
student_subset as (
SELECT students.id
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
),
score_subset as (
SELECT student_id, score
FROM test_results
WHERE subject = 'math'
), **student_scores as (
SELECT student_subset.id, score_subset.score
FROM student_subset
JOIN score_subset ON (
student_subset.id = score_subset.student_id
)
),**
现在,我需要将每个学生的最高分限制在一行,我也可以通过添加额外的 CTE 来做到这一点:
WITH
student_subset as (
SELECT students.id
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
),
score_subset as (
SELECT student_id, score
FROM test_results
WHERE subject = 'math'
),
student_scores as (
SELECT student_subset.id, score_subset.score
FROM student_subset
JOIN score_subset ON (
student_subset.id = score_subset.student_id
)
), **top_score_per_student as (
SELECT id, MAX(score) as score
FROM student_scores
GROUP BY id
)**
最后,我取总平均值。因为这是最后一步,所以它是作为主查询在 WITH 子句之外完成的:
WITH
student_subset as (
SELECT students.id
FROM students
JOIN schools ON (
students.school_id = schools.id AND schools.state = 'CA'
)
),
score_subset as (
SELECT student_id, score
FROM test_results
WHERE subject = 'math'
),
student_scores as (
SELECT student_subset.id, score_subset.score
FROM student_subset
JOIN score_subset ON (
student_subset.id = score_subset.student_id
)
),
top_score_per_student as (
SELECT id, MAX(score) as score
FROM student_scores
GROUP BY id
) **SELECT AVG(score)
FROM top_score_per_student**
我们完事了。最终结果要冗长得多(27 行对 12 行),但在许多方面弥补了这一点。
cte 的好处
逻辑流—使用 cte,逻辑从上到下流动,而不是从内向外流动。这是一种更自然的思考和编写 SQL 的方式,尽管它需要一些时间来适应。
可读性 —逻辑流程意味着您可以从顶部开始阅读使用 CTEs 的查询,并立即开始理解它。对于更传统的从内向外的流程,仅仅是找出从哪里开始阅读就要花费令人沮丧的时间。一旦你开始阅读,你将不得不不停地跳来跳去弄清楚发生了什么。随着查询变得越来越复杂,这变得越来越明显。
文档 —将查询分成线性步骤的另一个副作用是,它为您提供了一个添加文档的自然位置。除了 CTE 名称(例如“top_score_per_student”),在步骤之间添加行内 SQL 注释也很容易。没有 cte 提供的强制线性结构,这样做既困难又麻烦。
轻松调试 —当结果看起来不正确时,您可以轻松地从上到下完成这些步骤,沿途验证数据,以确定问题发生的确切位置。如果没有 cte,您将经常发现自己在调查 bug 时对 SQL 进行了如此糟糕的处理,以至于当您最终找到 bug 时,需要花费同样长的时间来展开所有工作并返回到最初的查询。最糟糕的是,有时您的修复工作在剥离下来的被屠杀的 SQL 上,但由于某种原因而不是在您的原始查询上,您又回到了起点。
可重用性—多个查询通常共享一些通用的业务逻辑。例如,使用相同的“加州学生”定义,但查看出勤记录而不是考试成绩。使用 cte,这些定义中的每一个都是一个自包含的 SELECT 表达式,可以很容易地在查询之间复制/粘贴,而无需修改。
性能 —您可能认为使用 cte 会导致查询变慢,但实际上大部分时间都不是这样。SQL 查询引擎非常聪明,通常会将这两种方法编译成相同的优化执行路径。以一种迂回的方式,cte 实际上可以提高性能,因为它们帮助您将优化工作的优先级集中在最广泛使用的表达式上。例如,如果您经常使用连接学生和学校的表达式来获取地理位置,那么创建一个新的物化视图来消除连接可能是值得的。
离别笔记
我找不到几年前第一次向我介绍常用表表达式的博客文章,但我永远感激它帮助我重新点燃了对 SQL 的热爱。希望这篇文章能为其他人做同样的事情。
FAMD:如何将主成分分析推广到分类和数值数据
原文:https://towardsdatascience.com/famd-how-to-generalize-pca-to-categorical-and-numerical-data-2ddbeb2b9210?source=collection_archive---------7-----------------------
让我们深入研究 FAMD 的理论和实现,这是一种非常有效(但却被误解)的技术,可以将 PCA 应用于所有类型的变量。

Mathilda Khoo 在 Unsplash 上的照片
本文介绍了混合数据的因子分析(FAMD) ,它将主成分分析(PCA)算法推广到包含数值和分类变量的数据集。为了让讲师准确理解这种技术,本文将深入 FAMD 背后的数学规则,并展示一些演示来证明重要的结果。如果你没有兴趣了解它为什么会起作用,可以直接上 III). 3。以了解如何在您的程序中轻松实现它。最后,你可以在这篇文章中看到这一理论的一个应用示例,其中 FAMD 在一个聚类用例中帮助删除数据集中的偏差。享受阅读吧!
I]导言
主成分分析(PCA)是统计分析领域中广泛使用的技术。考虑通过 P 变量描述的 N 个数据点的初始数据集,其目标是通过寻找K(1≤K≤P主成分来减少表示每个数据点所需的维数。通过每次寻找尽可能解释惯性的轴,这些主成分被迭代计算,同时与之前的主成分正交(图 1)。最后,我们可以通过 K 主成分直接描述数据点。尽管它们更难解释,但它们使我们能够从原始数据集中保留尽可能多的信息,同时大大减少了变量的数量。

图 1:在图上,我们表示一个人群的体重和身高(居中并缩放后)。PCA 获得的第一个分量(可以解释为“跨度”或“体积”)是在一个维度上表示这个特定数据集的最佳轴,因为当我们在这个轴上投影点时,我们丢失的信息量最少。(图片由作者提供)
在实践中,PCA 通常用于减少存储信息所需的内存,或者有效地可视化数据,通常在平面图(二维)中。如果你对 PCA 不熟悉,并且这个快速演示感到模糊,我建议你在继续之前阅读 这篇文章 。
虽然这种算法对数值数据非常有效,但它不能直接考虑分类数据。在本文中,我们将介绍 FAMD,这是一种考虑到数值变量和分类变量的主成分分析的推广,同时在最终成分的产生方面给予每个变量相似的重要性。
II]理论概念
首先也是最重要的:在本文的其余部分,我们将讨论一个在ℝ取值的数值变量(例如,年龄、工资、身高等)。),以及当分类变量在一系列 m 个可能模态中取值时的分类变量,其中 m ≥2(例如,眼睛颜色、婚姻状况、国家、性别等。).
1.相关系数和相关比率
两个数值变量 X 和 Y 之间的线性相关系数(又名皮尔逊相关) r(X,Y) 取[-1,1]中的值。接近 1 的值(分别为。-1)意味着这两个变量是正的(分别为。负)相关,接近 0 的值意味着变量之间不是线性相关的(图 2)。

(图片作者)

图 2:不同变量的相关系数。(图片由作者提供)
为了得到两个变量之间相关性的直接指示,我们使用平方相关系数 r (X,Y) 。它取[0,1]中的值,并且随着 X 和 Y 更明显地相关,它将变得越来越高。
另一方面,分类变量 A 和数值变量 Y 之间的平方相关比𝜂 (A,Y) 也取 0 到 1 之间的值。解释该度量的直观方式如下:𝜂 (A,Y) 越接近 1,对 A 所采用的模态的了解越多,这就有力地表明了 Y 所采用的值。例如,我们可以想象,在一个由身高 H 、性别 S 和发色 C 、𝜂 (C,H) 描述的大型人口数据集上,这些变量应该接近于 0(因为这些变量没有任何关联),而𝜂 (S,H) 可能不可忽略(因为男性通常比女性高)。

(图片作者)
因此, r (X,Y) 和𝜂 (A,Y) 都表示与 Y 的相关性,取[0,1]中的值,并随着变量( X 或 A )与 Y 显著相关而变高。因此,我们可以在对数字变量或分类变量给予同等重视的同时对它们进行比较。
2.惯性和投影惯性
现在让我们从变量的角度来看我们的数据集。我们考虑用 N 维表示的 P 变量(或列) (vⱼ) (通过我们初始表的 N 数据点)。我们假设 P 变量为
- centered (mean( vⱼ )=0):必要的,因为我们正在处理线性相关性
- scaled (std( vⱼ )=1):技术上不是强制性的,但是我们希望以类似的方式缩放所有变量,在计算惯性时给它们相同的初始权重。
☀ 并且稍微提醒一下:对于标量积 ⟨.,.⟩ ,我们将关联的范数和距离定义如下:

(图片作者)
我们将变量 (vⱼ) 的惯性定义为:

(图片作者)
其中 pⱼ 是变量的相对权重。在大多数情况下,我们可以对所有变量取 pⱼ = 1,给它们相同的惯性影响。我们还可以定义 (vⱼ) 在由矢量 w 支撑的轴上的投影惯性:

(图片作者)
3.主成分分析算法的机理
PCA 算法的工作步骤如下:
- 我们选择了多个组件K(1≤K≤P)
- 对于从 1 到 K 的 k :
- 我们将主成分 Cₖ 定义为以ℝⁿ为中心并且与前面的主成分 正交的单位向量,其最大化变量【vⱼ】(其中 pⱼ = 1 用于所有变量)。一个快速的演示(在论文 (A) 的末尾给出)表明,这个定义相当于说,在每一步, Cₖ 最大化与数据集变量的平方相关:

(图片作者)
FAMD:理论与应用
1.FAMD 背后的想法
在本文的开始,你们中的一些人可能会想“为什么不在应用 PCA 算法之前简单地对分类变量进行一次性编码呢?”
☀ 一键编码将数据集中的分类数据转换为数字数据,为每个分类变量的每个模态提供一列,如果变量采用模态,则为 1,否则为 0(图 3):

图 3:两个分类变量的一键编码。(图片由作者提供)
正如我们所看到的,PCA 最大化了所有柱在给定部件上的投影惯性(公平分布)。如果我们直接在包含一个热编码模态的表格上应用 PCA,给予分类变量的惯性将固有地取决于变量可用的模态的数量,以及这些模态的概率。因此,不可能给所有初始变量一个类似的权重超过计算出的分量。
相反,当搜索 K 个主成分时,FAMD 算法希望给予所有变量完全相同的权重,无论是数字变量还是分类变量。为此,当 vⱼ是数值时,它将迭代地寻找使 r (vⱼ,Cₖ)最大化的分量 Cₖ,当 vⱼ是分类的时,它将迭代地寻找𝜂。
事实上,正如我们之前看到的,这两个量是可比较的,并且以相同的方式翻译相似的信息。利用由 P 描述的初始数据集,数值变量 (vⱼ)(1≤j≤P) 和 Q 分类变量 (vⱼ)(P+1≤j≤P+Q) 、、Cₖ 将被定义如下:

(图片作者)
只剩下一个问题:我们如何获得这些部件?
2.定理
我们来考虑一个 P 数值变量 (vⱼ) (1≤j≤P) 的表格;和 Q 分类变量 (vⱼ) (P+1≤j≤P+Q) ,它们各自呈现 Mⱼ 模态。有了 M =∑ Mⱼ ,我们称之为 T 大小为n**(p+m*的表格,包含 P 数值变量,紧挨着 M 单热编码模态。我们对 T 应用以下步骤:
对于 P 数值栏:
- 将它们缩放至 1
- 将它们居中
对于 M 分类模态:
- 我们用模态的概率 μₘ 来划分每一列(一列中的数除以 N )
- 将它们居中
最后,为了计算投影惯性,我们给数字列加权【pₙᵤₘ】= 1,给分类变量的模态【pcₐₜ=【μₘ**(模态的概率)。
这些在 T 上的编码和权重的选择产生了 FAMD 的基本方程:对于在ℝⁿ:的 w 中心向量

(图片作者)
虽然第一条线是直接获得的(与 PCA 中的推理相同),但第二条线不是。对于比较好奇的讲师,全文演示 (B) 在本文最后呈现。
用这种方法,我们能够把一个数值(相应地。分类)变量在轴上与其平方相关系数(分别为。平方相关比)与轴的支持向量!因此,每个变量——无论是分类变量还是数字变量——都会带来一个在 0 和 1 之间变化的投影惯性,这取决于它与向量的相关性。
3.在实践中
FAMD 算法由两部分组成:
- 以适当的方式编码数据
- 迭代地寻找 K 个主成分
主成分的研究以与 PCA 完全相同的方式进行。因此,我们所要做的就是应用一个经典的 PCA 算法(scikit-learn 等。)在编码数据上。不幸的是,PCA 的大多数程序不允许增加对每根柱子的惯性的考虑。为了克服这个困难,我们使用下面的结果:用 sqrt(μₘ(而不是μₘ)来划分模态的独热编码列将具有与应用适当加权相同的效果。这一论断的证明也可以在本文末尾【C】找到。
总的来说,FAMD 算法是这样的:
a)标准标度数值变量(=获得 z 值)
b)对于分类变量:
-获取一次性编码列
-将每列除以其概率的平方根【sqrt(μₘ)
-将列居中
c)对获得的表格应用 PCA 算法!
备注:
- PCA 算法通常自动使列居中,因此这部分在步骤 a)或 b)中是可选的
- 将模态除以其概率的平方根可以这样解释:我们给稀有模态更多的惯性,因为当模态非常稀有时,“取此模态”的信息更丰富。
- 通过构造,分类变量 vⱼ 和 Mⱼ 模态可以生成最大的 Mⱼ -1 维度(因为所有列的总和总是给出一个 1 的向量)。因此,根据前面的符号,主成分的最大数量 K 是 P+M-Q.
四]结论
你有它!一种实现 FAMD 的简单方法,它将 PCA 推广到任何类型的数据集,同时对其所有变量给予相同的重视。
而且,因为我们对预编码数据应用了 PCA 算法,所以您可以使用 PCA 的所有分析工具(组件的惯性比、相关圆等)。)就像使用 PCA 一样。
我希望这有所帮助!如果您有任何问题,请告诉我:)
V]示威

(图片作者)

(图片作者)
VI]参考资料
1 G .萨波塔,(1990),意大利统计学会。意大利帕多瓦科学联盟。第 62-72 页。Ffhal-02513970f。
[2] J. Pagès,分析混合配料的因素 (2004 年),《应用统计杂志》,第 52 卷,第 4 期(2004 年),第 93-111 页
3 F .休松和 j .帕格斯,分析对应的倍数,应用数学实验室,西部农业大学。
在野外寻找家庭
原文:https://towardsdatascience.com/families-in-the-wild-track-iii-b5651999385e?source=collection_archive---------41-----------------------
思想与理论,计算机视觉教程
大规模搜寻和找回失踪的家庭成员

T-3 样本结果(排名 10)。对于每个查询(行),探针的一个或多个面返回图库的相应样本作为前 10 名。这里,x(红色)表示错误的预测,而正确的预测显示关系类型(绿色)😛 表示父关系;c 为儿童;代表兄弟姐妹。作者创造了这个形象3。
目录
目录
简介
背景信息
∘ 问题陈述
∘ 数据
∘ 源代码
结果及分析
∘ 定量结果
∘ 2020 年结果(最后一次 RFIW)
∘ 排名靠前的家族:
∘ 排名靠后的家族:
其他用于识别 FIW 的用例
总结
相关博客
∘ 2021 RFIW 数据挑战赛
∘ 野外数据集中的家族
引用
介绍
贩卖人口是我们在国际范围内面临的一个不幸的问题。每一个父母最可怕的噩梦往往是这种受利益驱使的残暴绑架行为所造成的影响。甚至讨论这个话题都令人作呕;然而,像大多数问题一样,坐视不理只会让事情变得更糟。因此,国土安全部(DHS)采取激励措施来解决这个问题,比如他们的蓝色运动。关于人口贩卖的更多含义,请查阅 DHS 的文章什么是人口贩卖?
那么,这与识别家庭有什么关系呢?如你所想,当局经常在网上发现被剥削的儿童,但却无法识别视频中的儿童。一种方法是通过排除法,由调查员人工筛选失踪儿童。考虑到仅在美国在任何给定时间都有近 50 万失踪儿童(globalmissingkids . org/awareness/missing-children-statistics/),人工检查的解决方案是不切实际的。按片段中孩子的某些属性查询怎么样?第一个问题是相关信息被剪辑和列表中的所有孩子过滤的基本假设。第二,我们现在正在使这个过程依赖于一个严重的偏见过程,这个过程不可能让所有的记录保管者和调查者在同一页上(即,除非手册被制作、分发和阅读,那么我们永远不可能为所有的儿童组织这样一个一致的数据库)。最终,我们需要一个更好的解决方案。
为了找到这样的解决方案,让我们首先问自己传统人脸识别方法的问题,这种方法通常可以帮助我们识别视频中的人:缺乏记录数据进行匹配。换句话说,为了简单起见,让我以自己为例:我失踪了,一个亲人向当局求助,我的名字被加入了数据库;此后,我的脸出现在一个可疑的视频剪辑中,一名调查员使用人脸识别来确定我的身份。嗯,那里有我的照片:RMV,东北大学,我现在的工作,以及(大的)各种社交媒体平台。尽管如此,儿童,尤其是在幼年时拍摄的儿童,不太可能有这样的记录(即,来自各种来源的丰富图像)。因此,归结起来,问题不在于人脸识别技术(也就是说,我们有人脸识别技术),而在于缺乏数据来查询令人不安的被拐卖儿童视频。因此,我建议,虽然数据缺少一个孩子,我们可以很可能确定一个家庭成员。这将大大减少搜索空间到一个单一的家谱,因此,这是一个客观的,资源有限的解决一个世纪之久的问题。
背景资料
Families In the Wild (FIW)是一个大规模标记图像集,用于支持视觉亲属识别问题[1,5,9]。通过它,识别 FIW (RFIW)是一项每年举行的数据挑战[3,7],当前(第五版)现已推出(参见相关博客中的第一项)。上述跨越数年的工作,构成了我论文的一部分。感兴趣的读者应该看到博客末尾提供的链接和参考资料。
问题陈述
给定一个探针,搜索人脸的图库以确定成对得分,然后作为排序列表返回。换句话说,输入是感兴趣的主体的面部(即,探头)。输出是一个排序列表,通过配对的可能性(即探针与图库中的受试者,在信息论(IT)中称为血亲)来指示图库的索引。具体来说,探测器是由一个或多个人脸表示的感兴趣的单个对象,而图库是人脸数据库,每个人脸都有已知的身份和家庭关系。现在,如果我们能在图库中找到探针的亲属,那么我们实际上就能确定被查询对象的身份。这种范式遵循了搜索和检索任务的经典观点。那些可能不太熟悉机器学习(ML)和 IT 相关术语的人:将问题空间想象成一种推荐系统:对于给定的探针(例如,用户),获得图库中的顶级项目(例如,推荐的电影或产品,分别是网飞或亚马逊)。
数据
我们有一个探针列表和一个图库中的面孔列表。
探针列表如下:
$ head -n 5 track-III/test/probe_list.csv
,ref,fid,feat_path
0,F0754/MID3,F0754,F0754/MID3/avg_encoding.npy
1,F0753/MID2,F0753,F0753/MID2/avg_encoding.npy
2,F0557/MID3,F0557,F0557/MID3/avg_encoding.npy
3,F0762/MID1,F0762,F0762/MID1/avg_encoding.npy
图库人脸列表然后,
head -n 5 track-III/test/gallery_list.csv
,fid,mid,labels,ref
0,F0775,F0775/MID3,1,F0775/MID3/P08183_face2.jpg
1,F0146,F0146/MID8,1,F0146/MID8/P01574_face0.jpg
2,F0593,F0593/MID2,1,F0593/MID2/P11000_face0.jpg
3,F0699,F0699/MID8,1,F0699/MID8/P07292_face0.jpg
两者的区别:探针带有一个或多个面,其中每个图库元素都是一个面。因此,每个探头表面的所有特征通过在特征尺寸上平均然后 L2 归一化来融合。
该函数用于归一化特征(即早期融合)。
也可以进行后期融合(即,为探针获得所有面的分数,然后取每个图库面的平均分数)。

具体计数,如 2020 RFIW 白皮书3中所列:
源代码
我的 GitHub 上有所有代码:
https://github.com/visionjo/pykinship
我们所指的脚本是:
https://github.com/visionjo/pykinship/blob/master/rfiw-tools/track-III-demo.py
结果和分析
首先,报告定量结果,然后进行更定性的分析。
定量结果
对于搜索&检索的问题,这里使用了几个度量标准。我们将把它们作为 RFIW 数据挑战系列的一部分3。
先说职级和 CMC。等级是返回真正值(即,真相对值)的绝对位置。例如,rank@1 表示在相应排序列表的第一个位置具有相对的探测器的平均数量。同样,排名@10 是探测器在画廊的前十名中找到至少一个亲戚的平均数量。我们的基线表现如下:
- 排名@1: 0.326(或 32.6%)
- 排名@5: 0.500
- 排名@10: 0.537
- 排名@20: 0.589
- 排名@50: 0.716
现在,让我们看看累积匹配曲线(CMC)。顾名思义,曲线代表所有等级的匹配分数(如上所列)!出于视觉目的,我们以对数标度绘图。

track-III 结果的 CMC 曲线。作者使用科学绘图生成的绘图。
最后,平均精度(mAP)是用于比较的基本指标。从数学上来说,每个 N 探针的分数计算如下:

其中平均精度(AP)是 f 族的函数,总 PF 真阳性率(TPR)。
然后,我们对所有 AP 分数进行平均,以确定总体 mAP 分数,如下所示:

我们的新基线获得了一个 mAP = 0.105。
2020 年结果(上一次 RFIW)

来源:https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=
排名靠前的家族:
现在,根据 AP 分数对探针进行分类,我们显示了排名前两位的家族(顶部是探针,底部是图库中的真正亲戚。

探针。


两个家庭成员的脸出现在得分最高的家庭的画廊里。

探针。





画廊中第二至最高得分家庭的五位家庭成员的面孔。
排名最低的家族:
同样,根据排序后的 AP 得分,我们检查得分最低的家族。

探针。




两个家庭成员的脸出现在最低分家庭的画廊里。
识别 FIW 的其他用例

面部识别越来越擅长在图像中找出相似之处,从而将人们识别为家庭成员。信用:psychologicalscience.org。
正如研究界的许多人所认识到的那样,视觉亲属识别有大量的用例:从推动本博客的应用程序,到使用不同难民营的低成本相机重新连接家庭,边境控制(即,识别司机和孩子是否是亲属的原始点非常频繁),基于祖先的研究,ancestry.com 等服务,基于社交媒体的应用程序,甚至是制作数字家庭相册的商业软件。除了基于辨别的任务,一些生成模型可以直接应用,如 DNA-Net [4]中的识别,它转换(父母的)一对脸来预测他们孩子的脸。Robinson 等人在最近一项关于视觉亲属识别的调查中涵盖了这一点,甚至更多。另外, ACM 新闻的桑德琳·瑟斯特蒙特在 2020 年末()报道了这个话题 FB clid = iwar 0 AK 4 u 3 azkd-zxh 3 ou qfvlrknd 8 neuz 2 vlj 9 kaj 32 LBO 3 endhspeov 6948。
概括起来
我们讨论了当代社会面临的人口贩运问题,重点是失踪儿童。我们想到了一个场景,当一名调查人员偶然发现一名在网上被剥削的儿童时:看到了孩子的脸,但缺乏使用常规面部识别来推断其身份所需的数据。可访问的面部数据可能包含未知儿童的血亲。因此,使我们能够找到亲属的解决方案可以大大减少搜索空间,从而为确定有需要的儿童铺平道路。
问题陈述是一个逐年流行的研究问题。具体来说,我们会问,我们如何利用面部图像来识别亲属关系?这个问题是我论文的一大部分,为了支持这个问题,我们建立了最大的用于亲属关系识别的标记图像集。也就是说,数据是野生动物的家庭,这是年度数据挑战识别 FIW (RFIW)的中心。
去年,作为第四届 RFIW 的一部分,新的任务 Search & Retrieval 作为一项附加任务被引入:它是一个开放形式的问题,本质上,它是所有 FIW 基准中最困难的。使用最先进的人脸编码器,对探针进行早期特征融合(即将所有特征平均为一),并使用余弦相似度将探针特征与图库主题的特征进行比较。
给定一个大小为 D 的图库,带有 M 个探针,得到的矩阵(即提交给竞赛的矩阵)大小为 M x D ,其中 M 行代表每个探针,而 D 列是相应图库主题的索引。上述基线导致 mAP 得分为 0.105,这意味着我们能够确定一些探针的亲缘关系,然而,还有很大的改进空间!
查看排名最高的探针(即我们最有信心的探针):排名第一和倒数第二的探针都是儿童(相对于家庭数据),并且都是女性,但父母出现在数据中(即排名第一的只有父母,倒数第二的有父母(不同种族)和兄弟姐妹在图库)。前两名之间的另一个差异是代表探测器的面部图像的相对年龄(即,第一名是中年人,第二名是青少年时期)。
然后,分析评分最差的探针(即,具有排序列表中最靠下的家庭成员的探针),在家庭数据中属于类型祖父和父亲。此外,探针的实例是一名老年受试者。这与之前的研究一致,这些研究表明祖父母(即年龄)是使用人脸自动识别亲属关系的挑战之一。
接下来,列出了几个相关的博客,以及相关的视频教程。最后,我们列出了全文中使用的参考文献。
相关博客
2021 RFIW 数据挑战
了解与 2021 年 IEEE FG 会议同时举行的当前 RFIW 的更多信息。
https://medium.com/to-recognize-families-in-the-wild-a-machine-vision/rfiw2021-7ceb357a39a6
野生数据集中的家族
参考文献部分包含几篇关于 FIW 的论文。
这里是以前的媒体博客,涵盖了概述、路线 I 和路线 II(即,此博客在路线 III 上)。
https://medium.com/that-publication/the-4th-recognizing-families-in-the-wild-rfiw-bec59285fb4
我录制的论文,其中包括一个关于 FIW 基准和数据集的部分。
最后,是我在之前的 CVPR 和 FG 会议上给出的指导。
参考
- 约瑟夫·罗宾逊、扎伊德·汗、余音、邵明和傅云。野生多媒体中的家庭(FIW-MM):用于识别亲属关系的多模态数据库。2021 年 IEEE 多媒体汇刊。( PDF )
- 约瑟夫·罗宾逊、邵明和傅云。视觉亲属识别:酝酿中的十年。 IEEE 模式分析与机器智能汇刊,2021。( PDF )
- 约瑟夫·鲁滨逊、、扎伊德·汗、、夏、迈克尔·斯托帕、萨姆森·蒂莫纳、马修·特克、拉马·切拉普和傅云。野外识别家庭(RFIW):第 4 版。 IEEE 自动人脸&手势识别国际会议( PDF )
- 高、、夏、罗宾逊、、、、、、、傅云。 DNA-Net:具有年龄和性别意识的亲属人脸合成器。 2021 年 IEEE 多媒体与博览会国际会议(ICME)。( PDF )
- 约瑟夫·罗宾逊,邵明,吴越,刘洪福,蒂莫西·吉利斯,傅云。野外家庭的视觉亲属识别。IEEE 模式分析与机器智能汇刊(TPAMI),2018。( PDF | 附录 | 补充 PP | 视频演示 | 网页)
- 、丁、、、傅云
通过潜在自适应子空间进行亲属分类
IEEE 自动人脸和手势识别,2018 ( PDF ) - 约瑟夫·罗宾逊,,赵,,蒂莫西·吉利斯,傅云。识别野外家庭(RFIW):与 ACM MM 2017 联合举办的数据挑战研讨会。野外识别家庭研讨会论文集,2017 ( PDF | 竞赛页面)
- 王淑洋,约瑟夫·罗宾逊,傅云。利用边缘化去噪度量学习对野外家庭进行亲属关系验证。2017 年第 12 届 IEEE 自动人脸和手势识别大会。( PDF | 海报)
- 约瑟夫·罗宾逊,邵明,吴越,傅云。野外家庭(FIW):大规模亲属图像数据库和基准。2016 年美国计算机学会多媒体会议。( PDF | 海报 | 数据集)
- 罗宾逊,J. P. (2020)。自动人脸理解:识别照片中的家庭(东北大学博士论文)。( YouTube )
接触
我希望这篇文章对你有用。
要进行连接,请执行以下操作:
- 跟我上 中
- 在 LinkedIn 上连接并联系我
- 查看关于学者的出版物
干杯!
约瑟夫·罗宾逊博士
人工智能工程师,替代外科
著名的意大利纸牌模拟和统计使用 Python
原文:https://towardsdatascience.com/famous-italian-solitaire-simulation-and-statistics-using-python-b10d60b34fc6?source=collection_archive---------17-----------------------

阿曼达·琼斯在 Unsplash 上的照片
用几行(Python)代码实现一个纸牌模拟器并从中提取获胜概率
注:你可以在这里 (GitHub 资源库)找到这部微小作品的完整脚本。你也可以找到我最近在做的其他项目。❤
首先你需要知道的事情:
意大利男人喜欢打牌
因为我是意大利人,所以我也不例外。你需要知道的第二件事更苦。这种社会距离迫使人们保持安全,在某些情况下…孤独。当没人在你身边时,一个消磨时间的好方法是玩单人纸牌游戏。单人纸牌游戏是那种除了你自己之外不需要任何其他玩家的纸牌游戏。关于单人游戏有趣的事情是,有时赢得游戏是难以置信的困难。
一个著名的单人纸牌游戏叫做“Uno,Due e Tre”。
这个游戏极其简单。你开始数“Uno(一)”、“Due(二)”和“Tre(三)”,当你这样做的时候,你选择一张卡片。如果这张卡是:
- 当你说“一”的时候,一张(那是“一”的对应卡片)
- 当你说“二”的时候
- ****三一面说“三”
你输了这场比赛。
现在我们来设置环境。一副意大利牌由 40 张卡片组成。更确切地说,你有 10 个数字和 4 种颜色。这意味着你有 4 张 Asso,4 张 2 和 4 张 3:12 张危险的牌和 28 张无害的牌。
但是要小心,这张牌中的一张足以让你输掉你的游戏,如果它是在你宣布与牌中相同的数字时被拿走的话。
真希望我把游戏解释的通俗易懂,但如果不是这样,你可以看看这个视频,会更清楚。
如你所见,这个游戏完全是随机的。但是赢得比赛真的很难吗?在这张牌的最后一张牌揭晓之前,你有多少机会真正“活下来”?
让我们找出答案。
1.游戏模拟器
让我们调用(导入)一些恶魔(库)。
这个游戏可以用下面几行代码来模拟,我已经用这些代码模拟了 10 万个游戏
2.获胜的机会(概率)
这个“计数器”变量实际上是在计算你松动的次数。这个数字很高吗?嗯,是的,真他妈的高:****
你有 99.15%的概率输掉比赛。
嗯,我知道,这不是有前途的,这就是为什么这个游戏可以让你玩几个小时,没有好结果。
那我们下一步做什么?我们是数据科学家,所以我们不会放弃。
比如让游戏简单一点。让我们承认,犯一两个错误,你仍然可以赢得比赛。它会一直变得容易吗?
这就是你承认一个错误的后果:
这就是你承认两个错误的后果。
如你所见,即使你承认错误,全球情况也不乐观:
3.比赛的统计数据
让我们暂时忘记输赢的事情。在一场比赛中,关键事件会发生多少次?我们能从中获得统计信息吗?
我们当然可以。
正如你所看到的,你通常会在一场比赛中出现 4 次错误,而零错误事件位于铃的左尾。
说到…我们能谈谈“钟形”分布吗?
好吧,如果我们进行正态测试,结果绝对不乐观:分布不是高斯分布。
事实上,获得一张与您之前呼叫的相同的卡可以被建模为“极端事件”。这可能是为什么 中心极限定理 在这种情况下可能没有用的一个原因。我们可以使用威布尔分布、弗雷歇分布或冈贝尔分布,并参考费希尔·蒂皮特·格内登科定理来获得更多关于该分布的信息。无论如何,一旦它被声明为极端事件,就没有必要确定需要使用哪种分布。
4.这都是为了生存
假设你选了 10 张牌,没有什么重要的事情阻止你继续游戏。赢得比赛的概率有多大?
一个众所周知的定理可以帮助我们,它叫做贝叶斯定理。

定理的陈述
如果我们认为 A 是“赢得游戏”事件,B 是“赢得游戏的前 n 张牌”事件,则我们得到:
知道我们“幸存”了前 n 张牌,赢得游戏的概率等于知道我们赢了游戏的前 n 张牌“幸存”的概率乘以赢得游戏的概率,再除以“幸存”前 n 张牌的概率
但是“在知道游戏已经赢了的情况下赢得前 n 张牌的概率”是 1。所以基本上是 P(A)/P(B)。这使得事情真的更容易计算。
让我们计算一下赢得游戏的概率,知道我们幸存了 10 张牌。
现在,如果我们对 10,20,30,…,39 张牌使用相同的技巧,我们有这个概率:
而且存活 39 张牌后赢得游戏的概率并没有你想象的那么高(85.6%)
5.终场哨声
这种纸牌游戏的性质允许很容易地实现一个有效且快速的代码,并给我机会在几分钟内得到一个统计数据。
令人兴奋的是,有时候很难意识到一个游戏有多难完成。我敢打赌,你们中没有人会说,你有 0.0085 的概率赢得这样一场比赛。
因此,如果你真的想知道你成功的机会,拥有一个统计心态是非常重要的,尤其是当游戏如此随机的时候。
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于现有最新技术的文章。
再见:)
用于视频会议的花式和定制神经风格传输滤波器
原文:https://towardsdatascience.com/fancy-and-custom-neural-style-transfer-filters-for-video-conferencing-7eba2be1b6d5?source=collection_archive---------39-----------------------
实践教程
您是否一直希望为您的视频通话配备别致独特的过滤器?他们来了!
我的开源 GitHub 脚本 提供了基于人工智能的过滤器,它将一种叫做艺术神经风格转移的新技术应用到你的物理网络摄像头设备的输入流中。与传统过滤器相比,这些基于人工智能的过滤器具有特征感知能力。根据视频中明显的特征类型,人工智能调整输出。此外,这些类型的过滤器可以从任何真实世界的图像中学习。由于提供的过滤器直接应用于网络摄像头视频流,它们可以用于所有类型的视频会议工具,如 Zoom,Skype,Discord,MS-Teams…

显示不同风格的变焦录像。(图片由作者提供)
具体来说,我的脚本设置了一个虚拟网络摄像头设备,它将艺术神经风格的传输应用到物理网络摄像头设备的输入流中。这种新的网络摄像头设备可以像任何视频会议应用程序中的任何物理网络摄像头一样使用。到目前为止,这个工具只适用于 Linux,但我正在开发 Windows 版本。为了使安装非常方便,您需要做的就是构建一个 Docker 容器。一个简单的分步安装指南详细解释了其工作原理。通过训练 PyTorch 快速神经风格转移示例模型 提取特定图像的风格,甚至可以轻松创建您自己的定制风格。

这些不是普通的过滤器。所使用的人工智能是特征感知的,并且适应出现的输入特征。(图片由作者提供)
什么是神经风格转移:

神经风格转移:将左图像的内容特征与中心图像的风格特征相结合,生成风格转移图像 by Theo Lagendijk , src, CC BY-NC-SA 2.0 。
这种深度学习方法,最初是由 Leon Gatys 等人开发的。2016 年,可以提取特定图像的样式,并将其应用于其他图像。这种方法的基本思想是生成一个图像,该图像一方面具有与内容图像相似的内容特征,另一方面试图具有与风格图像的特征之间存在的相同数量的特征相关性。这种特征相关性是图像风格的表示。
技术障碍以及为什么没有更早创建:
自从发明神经风格转移以来,由 莱昂·加蒂斯等人 。2016 年,迄今为止还没有任何工具将这种方法集成到可以直接使用的虚拟网络摄像头设备中。一个主要的障碍是高质量风格传输图像的处理时间仍然太慢,无法实时实现高帧速率。另一个障碍是很难访问和定制网络摄像头驱动程序代码来应用神经类型转移。幸运的是,两项相对较新的技术成就让我跨越了这些障碍:首先,Nvidia 的tensort库导致模型推理时间大幅加速。我改编了来自 PyTorch 示例 的快速神经风格转移模型,使其与 TensorRT 兼容,结果推理时间加速超过 75%。这使得在当前的 Nvidia 显卡上以每秒 25 帧以上的帧速率运行高清网络摄像头成为可能。其次,虚拟网络摄像头驱动程序 akvcam 为我在 Linux 上设置虚拟网络摄像头铺平了道路,它可以像任何物理网络摄像头一样使用。
现在,我是否激发了你的兴趣,让你尝试用奇特、古怪和独特的方式进行视频会议?请按照https://github . com/maximumtschler/Virtual-Neural-Style-Transfer-web cam-for-Linux上的安装指南立即开始。要求是 Linux 和相当新的 Nvidia GPU。
奇妙的功能以及在哪里可以找到它们
原文:https://towardsdatascience.com/fantastic-features-and-where-to-find-them-967e79634a18?source=collection_archive---------12-----------------------

图片作者。
几年前,我为一个在线双边市场开发了一个识别欺诈交易的模型。我最初的模型是基于交易及其环境的特征。这个模型很好,但我想把它做得更好。我已经在使用梯度增强树,更多的超参数调整并没有带来任何显著的性能提升。我再次转向特征。
该模型已经在使用买家和卖家之间的文本消息,包括消息的元数据(如交换的消息数量),但它没有考虑消息之间的时间间隔。具体来说,它没有考虑潜在买家提问和卖家回复之间的时差。这被证明是一个信息量很大的特征——欺诈性的卖家实际上没有东西可卖,所以他们不需要花时间为买家研究任何东西。他们还试图在买家怀疑之前迅速达成交易。由于这两个原因,他们通常比合法卖家回复得更快。
我设计了功能,重建了模型,然后看到了精确度的显著提高。它成为模型中最重要的特征之一。这是一个奇妙的功能。但是,奇妙的特征并不总是容易找到的。在这篇文章中,我想激发识别特征的重要性,并提供如何从数据中找到和提取它们的建议。
特征重要性
如果你花时间开发预测模型,你会发现特征重要性有一个共同的模式。典型地,有一些非常重要的特性和一长串相对不重要的特性。为了展示这一点,我为 scikit-learn 中包含的七个特征集训练了随机森林模型,并绘制了每个数据集的特征重要性。

图片作者。
从图中可以看出,在所有的数据集中,前 1-3 个特征相对于其他特征具有极大的重要性。在接下来的图中,我们可以更清楚地看到这一点。左侧是要素重要性的分布,右侧是每个数据集要素重要性的累积总和。特征重要性的分布显示大多数特征处于重要性等级的低端(例如小于 0.1)。右侧图中的累积特征重要性图显示,在大多数情况下,在几个特征之后,收益递减(取决于模型中有多少个特征)。这并不是说不太重要的特征应该被丢弃——总的来说它们非常重要——但是单个来说它们对模型的预测性能没有显著的贡献。
这里的要点是,一个模型的预测准确性的大部分通常可以归因于仅仅几个选择的特征。我发现这很有激励性,因为这意味着你有潜力从根本上改进一个模型,如果有一个高度可预测的特征,并且如果你能找到它。

图片作者。
发现奇妙的特征
- 思考问题,而不是数据*。在我在本文开头讨论的欺诈检测问题中,我从一开始就拥有所有可用的数据。问题是,我没有真正考虑欺诈用户在与买家打交道时可能会有什么行为。当我开始思考这个问题时——即使是在相当基础的层面上——很明显,通信速度可能很重要,但最初的模型无法检测到这一点。通过思考问题和微妙的行为细微差别可能会将一个群体与其他群体区分开来,与仅仅在笔记本上摆弄数据的不同组合和变换相比,你往往能想出更多有趣的想法。*
- 与人交谈:作为数据科学家,当我们在模型构建过程中工作时,很容易将自己孤立起来。这是一个很大的错误。认为我们可以简单地获取一个数据集并在我们可能不熟悉的环境中建立一个预测模型,这不仅有点自大,而且也失去了对什么才是重要的以及你的注意力应该集中在哪里的宝贵见解。当启动一个新项目时——尤其是在你不熟悉的环境中——你应该尝试与领域专家合作。这并不是说你不能从探索数据中发现新颖的、非常重要的特征。你当然可以,但是在你花大量时间开发它之前,领域专家通常可以看到新的特性想法是否通过了“嗅探测试”。
- 站在巨人的肩膀上:数据科学在过去几年里取得了长足的进步。有很多关于不同团队和公司如何处理各种预测建模任务的文章。我真的很喜欢阅读 Kaggle 竞赛获胜者采访,我最喜欢的一篇关于寻找 fanastic 特征的文章是这篇来自 Instacart 市场篮子分析获胜者的文章。关于他们如何通过思考时间相关性以及尝试识别客户没有采取的行动的模式来解决问题,有很多有趣的信息。这篇文章和其他文章非常值得你花时间去读。就像你正在处理的任何问题一样,你很可能不是第一个解决它的人。环顾四周,看看其他人都做了什么,你可能会发现一些很棒的想法,这些想法可以解决你的问题。
提取奇妙的特征
- 用树嵌入来绑定特征:宁滨分类和连续输入通常是去除噪声、强调信号和揭示特征和目标之间的非线性的好技巧。但是,以最佳的信息保存方式这样做可能很有挑战性。当然,您可以手动完成这项工作,例如,判定连续特征或将相似的分类特征合并到一个公共组中。在这两种情况下,都存在实际上从特性集中删除或减少有价值的信息的风险。有效绑定特征一个好方法是使用基于树的模型。我第一次了解到这种技术是在脸书的论文中。树形模型可用于以信息最大化的方式有效绑定连续变量和分类变量。虽然,只有当你想使用线性模型时,这才是真正合适的,但在很多情况下,这是非常可取的!在我关于鲜为人知的数据科学技术的帖子中,我讨论了这样做的一个关键实际优势。
- 用神经网络进行实体嵌入:使用神经网络将分类变量转换成实值向量已经改变了游戏规则。核心思想是使用神经网络在训练期间学习分类数据的数字表示。这类似于神经网络如何被用来学习单词向量。通过学习分类特征的实值表示,相似的值在嵌入空间中被更紧密地映射,从而揭示否则将会丢失的数据的潜在特征。这种方法的一个极好的、有据可查的例证是它在 Kaggle 上举办的罗斯曼商店销售竞赛中的应用。尽管只做了最少的功能工程,该团队还是在比赛中获得了第三名。这在很大程度上是因为从分类特征中学到了非常有价值的表征。
- 去除特征中的噪声:成为数据科学家的部分艺术是在噪声中寻找信号。这种普遍的思维模式应该应用到你放入模型的特性中。如果某个要素杂乱无章、缺少数据、具有随机极值或异常值或其他随机变化源,所有这些都会掩盖其真实值。我经常试图以这样的心态来处理预测建模问题:我想让我的模型尽可能容易地了解特征和目标之间的关系。虽然这是一个大话题,但有几种去噪算法可以应用于连续时间序列数据,这将有助于最小化噪声并强调信号,例如,卡尔曼滤波器和快速傅立叶变换。这本 Kaggle 笔记本讨论了其中一些去噪算法,并提供了 Python 实现。
摘要
寻找奇妙的特征既是一门艺术也是一门科学。这也是一个真正有创意的企业,我个人觉得很有趣,也很有收获。找到一个新功能是非常激动人心的,尤其是能够显著提高模型性能的功能。这也不容易,可能会让你陷入许多陷阱。我喜欢我之前提到的 Instacart 文章中的这句话:
“花了很多时间构建的特性到头来什么也没做,这很常见。但是如果我们什么都不做,就无法知道结果。所以最重要的是参与到你会得到一个更好的结果的错觉中去,如果你尝试的话!”
希望这篇文章能帮助你做得更好。
感谢阅读!
这个帖子的附页代码可以在这里找到。
奇妙的功能以及在哪里使用它们
原文:https://towardsdatascience.com/fantastic-functions-and-where-to-use-them-looped-and-grouped-f76ab7bb6b47?source=collection_archive---------13-----------------------
循环分组

由 Rhii 摄影在 Unsplash 上拍摄的照片
数据准备过程是每项分析的支柱,因为几乎没有数据准备就绪。借助 python 和 pandas 的魔力,可以简化这个过程,并避免重复代码。
在这篇文章中,我将带您了解一些最不喜欢但非常有效的 applymap、apply、groupby 函数,以及这些函数可以节省您的编码时间并提高代码可读性的潜在用例。
出于说明和适用性的目的,我将分析一家位于许多城市的 imaginery 连锁店的盈利能力和资本支出需求。然而,代码可以在各种领域和用例中实践。
内容
导入库
创建用于分析的数据框
- Applymap:遍历整个数据帧
- 嵌套应用:盗梦空间
- 分组按多个数据帧进行算术运算
- 分组依据与应用:自定义功能
- Groupby 对数据帧 进行切片
结论
导入库
我们导入以下库来创建带有随机字符串和数字类型数据字段的数据框。
import pandas as pd
import numpy as np
import stringpd.set_option('display.max_rows', 400)
pd.set_option('display.max_columns', 200)
pd.set_option('display.max_colwidth', 150)# ['A', 'B', ..., 'AA', 'AB', ..., 'ZY', 'ZZ']
ALPHABETICAL_LIST = (list(string.ascii_uppercase) +
[letter1+letter2 for letter1 in string.ascii_uppercase
for letter2 in string.ascii_uppercase])
创建用于分析的数据框
让我们创建一个 40 家商店的列表,这些商店在 1980 年至 2010 年间在 10 个不同的城市开设,有 20 年的收入和成本轨迹。
PERIOD_COLUMNS = [i for i in range(2021,2041)]CITIES = np.array(sorted([ALPHABETICAL_LIST[x] for x in np.random.choice(range(10), 40)]))STORES = np.array(sorted(['Street_' +
ALPHABETICAL_LIST[x] for x in np.random.choice(range(100), 40, replace=False)]))STORE_OPENING_YEARS = np.array(([x for x in np.random.choice(range(1980, 2010), 40)]))STORE_REVENUS = np.random.uniform(low=500, high=1000, size=(40, 20))STORE_COSTS = np.random.uniform(low=300, high=600, size=(40, 20))df_Stores = pd.DataFrame(np.column_stack((CITIES,
STORES,STORE_OPENING_YEARS,)),columns = (['City', 'Store', 'Opened'])).astype({'Opened': np.int64})df_Stores

商店的虚拟数据框,作者照片
df_Store_Revenues = df_Stores.drop(['Opened'], axis=1).reset_index(drop=True)df_Store_Revenues[PERIOD_COLUMNS] = pd.DataFrame(STORE_REVENUS, columns=PERIOD_COLUMNS)df_Store_Costs = df_Stores.drop(['Opened'], axis=1).reset_index(drop=True)df_Store_Costs[PERIOD_COLUMNS] = pd.DataFrame(STORE_COSTS, columns=PERIOD_COLUMNS)
1- Applymap:遍历整个数据帧
使用时,applymap 通过数据帧上的每个单元格在内部循环 lambda 函数。


onder rtel 和 Tine Ivani 在 Unsplash 上的照片
尽管要处理多个数据类型字段,这个“一行程序”还是非常方便的。
例如,我们有一个商店收入轨迹,如下图所示。
df_Store_Revenues.head(10)

商店收入,作者照片
如果我们不想看到很多小数点,我们可以简单地对所有非对象类型的单元格执行 applymap 循环。
df_Store_Costs = df_Store_Costs.applymap(lambda x: np.round(x,2) if type(x)!=str else x)df_Store_Revenues = df_Store_Revenues.applymap(lambda x: np.round(x,2) if type(x)!=str else x)df_Store_Revenues.head(10)

商店收入,四舍五入。作者照片
使用案例:当我们需要对每个单元格应用一个线性规则时(例如,用对数/乘数缩放、向上/向下舍入、用值替换等),此函数非常有用。)
2-嵌套应用:开始
我真的很喜欢这部电影,因为它让我想起了我最喜欢的电影之一:“盗梦空间”。通过在数据帧上双重应用 lambda 函数,我们可以获得该单元格的索引/列名信息。

由 Christophe Hautier 在 Unsplash 上拍摄
让我们考虑这样一个场景,我们的商场在运营后的第 25 年需要经历一个主要设备的更新阶段,并在随后的 3 年中加强房地产(油漆/地板)。
RENEWAL_YEAR_AFTER_OPENING = 25 #yearsENHANCEMENT_DURATION_AFTER_RENEWAL = 3 #yearsdf_Store_CAPEX = df_Stores.copy().reset_index(drop=True)df_Store_CAPEX[PERIOD_COLUMNS] = pd.DataFrame(np.array([[np.nan ]* 20] * 40), columns=PERIOD_COLUMNS)df_Store_CAPEX.head(5)

作者照片
我们可以计算出该特定年份是典型的维护、更新年还是升级年,如下所示:
df_Store_CAPEX[PERIOD_COLUMNS] = df_Store_CAPEX[PERIOD_COLUMNS].apply(lambda x: pd.DataFrame(x).apply(lambda y:
'Maintenance' if x.name < (RENEWAL_YEAR_AFTER_OPENING +df_Store_CAPEX.loc[y.name, 'Opened'])
else('Renewal' if (RENEWAL_YEAR_AFTER_OPENING +df_Store_CAPEX.loc[y.name, 'Opened']) == x.name
else('Enhancement' if x.name < (RENEWAL_YEAR_AFTER_OPENING +
df_Store_CAPEX.loc[y.name, 'Opened'] + 1 + ENHANCEMENT_DURATION_AFTER_RENEWAL
)
else 'Maintenance'))
, axis=1))
例如,Street_AR(建立于 1997 年)在 2022 年之前和 2025 年之后需要维护,2022 年需要更新(1997 + 25),2023 年至 2025 年需要改进。

年度资本支出类型,作者照片
用例:这种方法非常类似于 Excel 表格的使用,在表格值内的公式中使用标题单元格。
除此之外,当数据框的索引顺序和列名几乎相同时,这种嵌套的应用方法可能更适合在多个数据框之间进行复杂的计算。
3-用多个数据帧分组进行算术运算
当我们知道索引顺序(某些数据字段的顺序)在数据框中是相同的时,前面的方法是有效的。然而,如果不是我们首先创建了这些表,并且我们不知道这些几乎相似的表是否有任何添加或删除,那该怎么办呢?
如果是这样,如果我们需要做 4 个基本的算术运算(加、减、乘、除),那么有一个更安全、更好的方法。

照片由 Recha Oktaviani 在 Unsplash 上拍摄
关键是将对象类型字段设置为索引,这样数字列就可以彼此相加/相乘。
INDEX_KEYS = ['City', 'Store']# we can put as many dfs as we like inside this list
# buy multiplying with -1 and summing at the end,
# we're basically substraction negative table from the positive table.LIST_OF_DATAFRAMES_TO_SUM = [df_Store_Revenues.set_index(INDEX_KEYS),df_Store_Costs.set_index(INDEX_KEYS) * -1,
]df_Store_Profit = pd.concat(LIST_OF_DATAFRAMES_TO_SUM,
sort=False,
keys=range(len(LIST_OF_DATAFRAMES_TO_SUM))
).groupby(
level=[i+1 for i in range(len(INDEX_KEYS))]
).apply(lambda x : (x.sum())
).reset_index()df_Store_Profit
Street_A 的收入和成本视图如下:

Street_A 的收入,作者照片

Street_A 的成本,作者照片
Street_A 的毛利观如下:

商店的毛利,作者拍摄的照片
乘法(或除法)也与前面的方法(加法/减法)非常相似,只有几处不同。
INDEX_KEYS = ['City', 'Store']# we can put as many dfs as we like inside this list
# buy taking the power to the -1 and multiplying at the end,
# we're basically dividing first table by the second table.LIST_OF_DATAFRAMES_TO_MULTIPLY = [df_Store_Profit.set_index(INDEX_KEYS),df_Store_Revenues.set_index(INDEX_KEYS) ** -1,
]df_Store_ProfitMargin = pd.concat(LIST_OF_DATAFRAMES_TO_MULTIPLY,
sort=False,
keys=range(len(LIST_OF_DATAFRAMES_TO_MULTIPLY))
).groupby(
level=[i+1 for i in range(len(INDEX_KEYS))]
).apply(lambda x : (x.cumprod(skipna=False).iloc[-1])
).reset_index()df_Store_ProfitMargin
Street_A 的毛利率视图如下:

商店的毛利率,图片由作者提供
用例:当有许多数据帧要对多级数据进行(加/减)或(乘/除)时,这些算术运算是非常可取的。
请注意,sum / multiply 不能混合在同一个 dataframe 列表中,必须单独运行。
4-具有应用功能的分组依据:定制功能
我们可能需要只适合我们需求的定制功能。

萨尔瓦多·戈多伊在 Unsplash 上的照片
例如,如果我们想找到每年毛利率为负的商店,该怎么办?

商店的毛利率,图片由作者提供
关键点是创建一个内部有一个空字典的函数,并将其作为用字典键索引的 pandas series 返回。
def find_stores_that_loses_money(x):
d={}
for COLUMN in PERIOD_COLUMNS:
MONEY_LOSING_STORES = x[x[COLUMN] < 0]
if len(MONEY_LOSING_STORES) > 0:
d['Loss_{}'.format(COLUMN)] = ' , '.join(list(
MONEY_LOSING_STORES['Store'].values
)
)else:
d['Loss_{}'.format(COLUMN)] = np.nan
return pd.Series(d, index=d.keys())df_Stores_Losing_Money = df_Store_ProfitMargin.groupby(['City']).apply(find_stores_that_loses_money).reset_index()df_Stores_Losing_Money
对于每年的每个城市,我们都会发现毛利率为负的商店。

亏损商店,作者照片
用例:天空才是极限。只需创建一个最符合您业务需求的功能,并与 groupby magic 结合即可。
5- Groupby 对数据帧进行切片
我们有时需要以多层次的方式分割数据帧来处理子集。

照片由 Kabir Kotwal 在 Unsplash 上拍摄
对于每个城市,我们筛选 2021 年盈利能力最高的商店行。
idx_max = df_Store_ProfitMargin.groupby(['City'])[2021].transform(max)==df_Store_ProfitMargin[2021]df_Most_Profitable_Stores_2021 = df_Store_ProfitMargin[ (idx_max) ]df_Most_Profitable_Stores_2021
请查看索引号中的过滤效果。

每个城市 2021 年利润最高的商店,作者照片
用例:当我们需要保存数据库历史中每个类别的最新行时,这种方法非常有用。(即每个用户和产品的 Updated_At 列的最大值)
结论
在这篇文章中,我想带你了解一些最不喜欢但有效的循环和分组函数,它们可以增加代码的可读性,并让你赢得时间和确定性。但是,请注意,在处理数百万行的数据帧时,循环并不是首选,它们需要谨慎处理。
美妙的谷歌代码体验之夏以及我是如何发现它们的
原文:https://towardsdatascience.com/fantastic-google-summer-of-code-experiences-and-how-i-found-them-dd1c5b09a364?source=collection_archive---------36-----------------------

图 1:Unsplash上 Rhi 摄影 的照片
在过去的三个月里,我有机会加入了 2021 年版本的谷歌代码之夏,作为一名深度学习实习生在科学机器学习组织工作。我的项目是端到端(E2E)小组的一部分,处理的是将深度学习应用于 CERNCMS 实验的增强顶夸克质量回归。为了纪念这个充满学习的机会的结束,我决定写一篇文章,描述我作为代码学生的谷歌之夏的经历,所以系好安全带,让我们开始吧!
0.初步:什么是 GSoC?
Google Summer of Code 或 GSoC(在狂热的软件开发学生中通常被称为 GSoC)是一个由 Google 组织的十周计划,旨在让来自世界各地的学生与开源组织合作。每年夏天,学生们都会申请与不同背景的组织一起从事开源项目,从 web 开发、软件工程、机器学习到生命科学等等!
1.在流程的早期开始
GSoC 项目因竞争激烈而臭名昭著。每年,成千上万的学生申请该计划,并向开源组织提交他们的建议。为了使这一过程更容易,GSoC 组织者花了很大力气为整个项目设定时间表。
不要让每一步的充足时间欺骗了你!我注意到许多学生(包括我自己)每年都会犯的一个错误是把提交提案的步骤留到最后一刻。但由于许多组织在提交提案之前有不同的申请程序,许多学生将无法按时完成他们的提案,并进入申请流程的下一步。所以热拍是要早点开始的!给自己足够的时间研究这些组织,了解他们的选择过程,并最终写出那份强有力的提案!
2.等等,ML4SCI 到底是什么?
我在 GSoC 的主要目标是加入试图解决现实世界科学问题的机器学习项目。领导这些项目的组织之一是科学机器学习组织(ML4SCI) 。这是一个将机器学习应用于科学项目的伞式组织。它将来自大学和科学实验室的研究人员与积极的学生聚集在一起,为许多学科的前沿科学项目做出贡献。
ML4SCI 目前包括多个领域的项目。例如,一些项目探索了机器学习在欧洲粒子物理研究所高能物理中的粒子重建和分类、基于深度学习的天体物理学暗物质搜索、机器学习技术在行星科学任务返回数据中的应用、量子机器学习在科学中的应用等。
它的目标是通过解决重要的科学挑战和在各学科间转移机器学习的知识和工具来发展科学机器学习的开源社区。
3.ML4SCI 应用程序

图 Bram Naus 的图片(来源:Unsplash)
在 ML4SCI 组织提供的许多令人兴奋的项目中,我决定申请 ML4SCI 端到端小组提出的使用机器学习的大规模回归(接下来将详细介绍该项目)。
ML4SCI 申请程序的第一步是评估测试,帮助您展示您的机器学习和数据分析技能。我的评估测试包含三个深度学习练习,使用 Tensorflow 和 Pytorch 成功训练和调优了一个深度学习模型。在此期间,每次我都面临困难。我发现 Gitter 上的 ML4SCI 社区是一个寻求帮助并与其他申请人和导师就提案交换意见的好地方。
4.哦,求婚!准备有那么难吗?
完成测评任务后,你应该为自己感到骄傲!这不是一件容易的事!现在,是时候开始准备你的提案了。正如我之前所说的,慢慢来,花时间写提案。相信我,只要有足够的时间,你就能起草一份经过充分研究的强有力的提案。写建议书有很多方法,我发现GSoC 指南提供的 这里的 相当清晰全面。请务必详细阅读,如果有任何疑问,请不要犹豫,访问 Gitter 上的社区,并联系导师了解更多问题。一旦你对你提议有了信心,深呼吸,点击提交按钮。
5.我加入了!

图 3: GSOC 验收
恭喜你!你终于被 GSoC 项目录取了,除此之外,你还被邀请参加 ML4SCI 社区会议!根据你的项目,你应该期待每周与你的小组开会,在会上你将有机会分享你的进展,并从你的导师和同事那里得到有价值的反馈。与此同时,你可能需要联系你的导师,讨论你的下一步以及你的提案中需要做出的修改。
6.酷,但是你的项目怎么办?
是时候了,你可能想知道我的项目怎么样了!系好安全带,你将在机器学习和粒子物理的世界中享受一次甜蜜的旅程!
a.(物理)故事时间
探测顶夸克是欧洲核子研究中心 CMS 实验的一项重要任务。顶夸克预计将通过许多物理实验产生,并在 CERN 的 CMS 物理项目中进行深入研究。例如,量子力学允许希格斯玻色子粒子在非常短的时间内波动成一个顶夸克和一个顶反夸克,这两个夸克立即相互湮灭成一个光子对。因此,除了其他任务之外,对顶夸克及其测量的研究对于希格斯玻色子的研究是重要的。

图 4:CMS 象限示意图(来源: CERN 文件服务器 )
在进入关于机器学习方面的更多细节之前,让我们后退一步,研究一下我们的数据。什么是碰撞?如前所述,CMS 实验通过碰撞粒子和评估产生的结果,产生了大量关于基本粒子的数据。由于每次碰撞都会产生许多粒子,它们被自己的动量和角度推开,并撞击 CMS 的不同探测器层。然后这些“点击”被记录下来,并在欧洲粒子物理研究所的许多项目中使用。

图 5:粒子碰撞时喷射出的粒子‘射流’(来源:CERN)
如你所见,那些层层叠叠的安打,可以当作图片来研究。因此,使用了专门的深度学习模型,如卷积神经网络,它的优势在处理类似图像的结构时很方便。
b.机器学习方面
因此,我的项目开始了:利用从 CMS 实验的粒子碰撞中收集的数据,使用深度学习来回归顶夸克质量。我训练了一类卷积神经网络,称为 ResNets 或残差神经网络,来预测被增强的顶夸克的质量。
现在我们已经讨论了物理部分,我们可以转到有趣的部分了。残差神经网络(简称 ResNets)是一类卷积神经网络,多年来被用作许多计算机视觉网络的主干。Resnets 已经用于非常深的模型,并能够在 2015 年赢得 ImageNet 挑战赛。

图 6:带 Skip /快捷连接的 34 层 ResNet(上)、34 层素网(中)、19 层 VGG-19(下)(来源: 走向数据科学 )
你会问,为什么是 ResNets?在我们的实验设置中,我们使用的数据如前所述具有与图像相似的结构。由于 ResNets 能够在不降低性能的情况下增加深度,它们提供了强大的能力来学习复杂的模式,并在我们增加深度时灵活地提高模型的性能。
c.训练实验
为了训练模型,我们使用了通过皮媞亚模拟软件生成的数据。我们有超过 200 万个样本,以 90–5–5 的比例分布在训练集、验证集和测试集中。
对于预处理,我们将每个 jet 层的像素值重新调整为 0 到 1 之间的值。我们还将低于 1e-5 的所有值转换为零,将高异常值转换为 1。我们使用这些策略来加速训练并减少由训练中的异常值引入的噪声。
对于目标变量,我们执行了无偏过程,将质量分布转换为均匀分布,并将这些值除以最大质量值(500 GeV)。使用这种技术,我们降低了一个质量亚组的模型偏差的风险,并减轻了过度拟合。

图 7:单偏和重定标后的目标质量分布

图 8: MAE 公式
在对数据进行预处理之后,我们对 25 个时期训练一个 ResNet-15 模型。至于我们的损失函数,我们使用平均绝对误差(MAE) 进行训练和评估。

F 图 9: MRE 公式
我们还采用了平均相对误差(MRE) 作为评估指标,对模型性能进行基准测试,并与其他实验进行比较。
我们使用了 Adam 优化器和学习率调度器,每当验证损失不再减少时,该调度器就会降低学习率,以提高模型的收敛性。
d.结果
经过 25 个周期的训练后,在测试集上 MAE 达到 56.43 GeV。在测试集上,MRE 也达到了 24.04%的百分比。还不错,但是还有提升的空间!

图 10:训练和验证集在训练时期的比例损失图
该图证明了模型在合理误差内预测质量值的能力,而不会过度拟合训练集。


图 11:验证 MAE 和 MRE 在训练时期的演变图
为了改善结果,我们计划通过添加额外的两个图层 dZ 和 pt 来重复这个实验。在顶夸克分类中的早期工作已经表明,增加这两层提高了分类器的性能。
7.不要忘记记录你的进步!
现在你已经完成了你的项目并通过了你的最终评估,祝贺你!你已经正式完成了你的(奇妙的)GSoC 体验!最后一条建议是,确保你对项目的宝贵进展和有价值的贡献被保存下来,并为将来的补充做好记录。根据您的组织,您可能希望将注释良好的代码提交到 Github 或 Gitlab 上的存储库中,并向您组织的存储库提交一个 pull 请求。
最后的想法
至此,我结束了这次奇妙的 GSoC 体验!在过去的三个月里,我有机会了解了很多关于在加速器物理学中应用机器学习的知识,并遇到了一个充满才华横溢、支持我的同事和导师的激励社区。尽管 GSoC 结束了,但我很高兴能继续成为 ml 4 sci e2e 小组的一员,以改进顶级夸克质量回归项目,并促进其集成到 CMS 探测器管道中。最后,我要感谢谷歌代码之夏创造了如此宝贵的机会,还要感谢我的导师们 大卫·迪·克罗齐谢尔盖·格利泽达里亚·迪亚可娃 对我的持续支持。
您可以在这里 了解更多关于我的项目 。
原载于 2021 年 8 月 26 日 https://anisdismail.com**。
朱莉娅·REPL 的绝妙建议
原文:https://towardsdatascience.com/fantastic-tips-for-the-julia-repl-2865ba81bd61?source=collection_archive---------16-----------------------
用这些很酷的建议来提升你的 REPL 体验吧!

(图片由作者提供)
介绍
ulia 是一种非常酷的编程语言,在过去的几年里,它将许多非常有趣的概念带入了许多开发人员和科学家的脑海。诸如即时(JIT)编译和多重调度这样的概念现在已经成为数据科学和一般编程社区中的一个大话题,这是有充分理由的,因为 Julia 确实非常好地执行了这些概念。
对于朱莉娅语言的大部分独立部分,很容易看出他们在构思中投入了大量的思想和努力。从 Julia 出色的包管理器 Pkg,到它处理模块和文档的方式,Julia 已经做了一些非常酷的开箱即用的事情,使这种语言比许多其他可用于类似任务的编程语言更容易使用和流畅。该语言及其软件包的任何一个单独的部分都可以很快带领你进入一个有趣的编程概念和计算机科学的兔子洞。如果你想研究 Julia 语言中的其他一些很酷的东西,我有几篇文章可能会让你感兴趣,这些文章详细介绍了我喜欢软件包管理器(一个叫做 documented 的软件包)的什么,以及这两者如何协同工作来创建奇妙的“JuliaHub”我认为对朱莉娅感兴趣的人绝对值得一读:
REPL 基础知识
正如我在介绍中提到的,Julia 语言经常采用简单的想法,然后完全颠覆它们,产生令人难以置信的结果。首先要承认的是,朱莉娅 REPL 的伟大之处在于,它能够直接从朱莉娅 REPL 访问其他回复。朱莉娅的软件包管理器 Pkg 实际上有自己的 REPL,在朱莉娅 REPL 中按]就可以快速访问。这对于添加包非常方便,我个人总是选择这种方法,而不是传统的使用 Pkg 的方法:
using Pkg; Pkg.add("Lathe")
相反,我更喜欢:
julia> ]pkg> add Lathe
这也为使用 git 通过 URL 直接添加包打开了大门:
pkg> add [https://github.com/ChifiSource/Lathe.jl.git](https://github.com/ChifiSource/Lathe.jl.git)
对于下一个令人惊叹的特性,让我们考虑一个计算机程序员可能经常会遇到的场景。你 CD 进入一个目录,打开你的 REPL,只是意识到你要么在错误的目录,或者也许你实际上不知道你应该看什么文件。当然,使用像 pwd()、cd()和 readdir()这样的方法,在 Julia 本身中导航文件系统是可能的,但是我的 Unix 用户无法忍受为了导航我的文件而编写方法调用。此外,readdir()对我个人来说是一个非常奇怪的选择,因为我太习惯于写 ls 了。幸运的是,Julia 还有一个非常方便的方法可以直接从 REPL 访问 Bash!这意味着不再需要 ctrl+C/ctrl+D 来查看您的 REPLs 文件系统!我们可以输入这个 REPL 使用:。
julia> ;shell> cd ..shell> ls
directory
other-directory
shell> pwd
look/its/my/working/directory
更酷的东西
朱莉娅·REPL 实际上本身就是一个模块。它里面包含了很多有用的工具,而且你可以用这个模块本身包含的函数和类型做很多很酷的事情。例如,您可以创建一个文本编辑器,它已经用 Acorn.jl 包完成了。当然,在这方面有很多内容要介绍,但它肯定值得熟悉,因为我认为它肯定会在一些项目中派上用场!
你可以用朱莉娅 REPLs 做的另一件很酷的事情是创建你自己的回复!您可以使用 REPLMaker 包来实现这一点,我正好有一个教程,您可以在这里查看:
结论
Julia 语言提供了大量非常有趣的编程概念和便利,提高了大多数使用该语言的人的生活质量。我认为有很多很棒的软件包和功能可以补充这一点,但一个很好的例子是在 REPL。Julia 的 REPL 体验非常流畅,甚至可以让最困难的编程和开发任务变得非常简单。在这方面,我最喜欢的一件事是快速访问 Bash REPL,我经常用它来用 nano 编辑文本和浏览文件。感谢您阅读我的文章,我希望它提供了一些改进或知识,当谈到使用朱莉娅 REPL 的全部潜力!
梦幻英超 Python 的另一种积分系统
原文:https://towardsdatascience.com/fantasy-premier-league-an-alternative-points-system-with-python-b0773e2217ad?source=collection_archive---------40-----------------------
如果 FPL 的积分和 f1 一样,你会赢吗?

埃米利奥·加西亚在 Unsplash 上拍摄的照片
#动机
当你玩梦幻英超时,首先要做的决定是将你的联赛设为经典还是单挑。一些人认为经典联赛更公平,但面对面的比赛更有趣。我同意这一点。后一种形式会引发更多的竞争,并允许更深层的策略发挥作用。
这两种形式总的来说非常有趣。然而,如果你像我一样,两次都没有赢得联赛冠军,你可以想出第三个,当然是更好的一个。现在,我是输不起的人吗?这是可能的,但幸运的是这不是本文的重点。
相反,我们要研究的是,如果在每个比赛周,积分都以一级方程式中的相同方式分配,即比赛周的最高分为 25 分,第二名为 18 分,第三名为 15 分,以此类推,会对排名产生什么影响
#识别和获取相关数据
我已经写了一篇关于 FPL 的文章,解释了如何使用 FPL API 获取相关数据。如果您感兴趣,可以在这里找到:
我们将首先使用这个 URL 端点和一个简单的 GET 请求从我们的头对头联盟获取数据:
https://fantasy . premier league . com/API/league-h2h-matches/league/h2h _ league id/?page = 1&event =GW number
这一部分的代码如下所示:
请随意在你自己的 H2H 联赛中使用它!您需要更改的唯一参数是 leagueID(您可以在联盟页面的 URL 中找到),您要考虑的开始游戏周和结束游戏周,以建立替代排名。
这就是我们从 FPL 需要的一切,我们将从这里开始建造其余的!
#组织数据
为了更容易地处理数据,并最终创建一个包含我们正在寻找的备选排名的数据框架,我们首先将数据存储到一个字典中,该字典包含联盟中的所有球队,以及每个球队在每个比赛周的 FPL 分数。

按作者分类的图片——字典中组织的每个游戏周每个团队的分数
这就是我们如何得到这个结果的:
#建立备选排名
该过程的第二步是用每个队获得的 FPL 分数填充包含队(行)和比赛周(列)的数据框。
这就是我们最终得到的结果:

作者图片——32 个游戏周之后的经典排名,每个游戏周都有积分
这里没有什么令人惊讶的,因为所有这些都可以在 FPL 网站上以更好的格式获得。然而,这是实现最终目标的必要步骤。
最后,剩下要做的就是根据下面的评分系统计算每支球队在每个比赛周获得的新分数:

作者图片—一级方程式积分系统
为了做到这一点,我们对比赛周和各队进行循环,比较他们的分数,并根据比较结果给他们分配新系统的分数:
最后,我们在数据框中添加一个总计列,得到如下结果:

作者图片—32 个游戏周之后的备选排名,每个游戏周都有积分
#结论
这种替代积分系统产生的排名介于混乱的面对面联赛和平凡的经典联赛之间。这是一个高风险高回报的系统,有利于排名靠前的位置,因为位置之间的差距会随着比赛周排名的下降而减小。
将 32 个比赛周之后我的排名表与另一个排名表进行比较,显示出一些巨大的差异和一个较少依赖于运气的排名。

作者提供的图像—两种积分系统的比较
如果你想知道用这个积分系统你的头对头联盟会是什么样子,完整的代码可以在这里找到:
https://github.com/Guigs11/fpl_alternative_ranking
梦幻英超 x 数据分析:跻身前 2%
原文:https://towardsdatascience.com/fantasy-premier-league-x-data-analysis-being-among-the-top-2-98a714a1d170?source=collection_archive---------10-----------------------
对我构建的应用程序做一个简单的概述,在这个应用程序中,我使用了数据分析来帮助我的 FPL 团队提升图表
编辑:App 2022-23 赛季重新上线此处。
梦幻英超:一种现象
非足球迷或非体育迷会问,梦幻英超(FPL)到底是什么?好吧,让我们从什么是 FPL 开始,然后在深入数据分析代码之前,看看这个游戏是如何玩的。

由杰克·莫纳奇在 Unsplash 拍摄的照片
游戏概述
来自维基百科、、梦幻足球(以及一般的梦幻体育运动)是一种游戏,参与者组建一个由现实生活中的足球运动员(运动员)组成的虚拟团队,并根据这些球员的实际统计表现或他们在赛场上的感知贡献得分。通常,在一个特定的幻想游戏中,玩家是从特定国家的一个特定部门中挑选出来的。
所以,FPL 是英格兰超级联赛的梦幻足球联赛。1971 年 8 月 14 日星期六,伯尼·唐纳利在英格兰创立了现在的梦幻超级联赛的最初版本。作者已经玩了当前的网络版 FPL 游戏五年了,结果喜忧参半。然而,游戏本身的“结果”却是喜忧参半:从 2014/15 赛季的约 350 万玩家到现在的 800 万,游戏每年增加约 75 万玩家。
一个很容易理解的问题是:是什么让这个游戏如此吸引人和令人上瘾?嗯,首先,这是足球固有的魅力,英超联赛和管理自己球队的幻想,所有这些加在一起。除此之外,你可以和你的朋友一起玩迷你联赛,如果你能赢得这些迷你联赛或 FPL 本身(如果你能管理好的话),你就有吹牛的权利。最重要的是,有了围绕英格兰超级联赛的资金,他们决定邀请比赛的获胜者进行为期 3 天的英国之旅,并有幸观看他们最喜欢的球队的现场比赛。难怪每个疯狂的英国足球迷都玩它!
游戏规则
既然我们已经确定了游戏的意义,让我们来看看规则。当我们深入研究代码时,这些将会派上用场,因为数据分析已经完成,记住了游戏规则。
前提是:球员有 1 亿英镑的预算购买一支由 15 名球员组成的球队,其中包括 2 名守门员、5 名防守队员、5 名中场队员和 3 名前锋,此外还有一条规则,即从任何一支球队中最多只能选择 3 名球员。玩家的花费由游戏开发商根据玩家在上一个足球赛季的受欢迎程度和表现预先确定。
除此之外,在每一轮(称为游戏周)游戏后,用户可以选择将一名玩家从他的团队中转移出去,如果他愿意的话,可以免费将另一名玩家带到相同的位置。任何额外的转移都将受到 4 分的罚款。关于规则和游戏评分系统的更多细节,你可以访问页面。
游戏的目标:在预算和其他限制条件下,每周尽可能拥有得分最高的球员,这样从长远来看,你可以在其他球员中积累最多的分数。
数据
游戏的制作者维护一个为他们的网站提供动力的 API。该 API 可公开访问此处为。它包含了每个游戏周,每个团队的数据,以及联盟中每个玩家的统计数据。这就是为什么它是数据分析的金矿!
我们对这些数据进行了一些初步的 EDA,以检验一些假设。其中一个问题是,就 FPL 而言,一支球队在联赛中的位置是否会影响该队球员的表现。这意味着属于上半区球队的球员是否比属于下半区球队的球员有更多的 FPL 积分。如果是这种情况,我们将需要来自 API 的团队数据。然而,这个假设被证明是错误的。尽管在很高的层面上是正确的,但也有一些异常值得分过高,不容忽视。
所以,在确定了球队位置或多或少与 FPL 的球员表现没有关系后,我们决定提取整个球员名单的球员统计数据。这包括以下信息:
['id', 'code', 'first_name', 'second_name', 'web_name', 'team_code', 'player_type', 'status', 'form', 'total_points', 'points_per_game', 'minutes_played', 'goals_scored', 'assists', 'clean_sheets', 'goals_conceded', 'own_goals', 'penalties_saved', 'penalties_missed', 'yellow_cards','red_cards', 'saves', 'bonus', 'now_cost']
上面的大多数属性都是不言自明的。web_name是网站上使用的玩家名称。player_type是指球员是前锋、中场、后卫还是守门员。status指球员的受伤状态。form表示玩家形态的整数,越高越好。
这里有个旁注:因为在赛季开始的时候,没有那个赛季的球员表现数据(很明显!),我们已经使用前一个赛季的最后一周数据来决定球员,并为转会截止日之前发生的 Gameweeks 建立一支球队。一旦队伍确定下来,并且有了适当数量的当前赛季数据,我们就开始利用这些数据来决定谁该加入队伍。
FPL 团队制造商:应用
这里可以找到的应用有两种模式:组队模式和转会模式模式。团队建设模式允许用户建立一个 15 名球员的团队,每个团队的球员人数和重要性的定制形式和总积分留给用户。转移模式允许用户将指定数量的球员转移到他已经存在的球队中,这是他输入的。下面我们来深入了解一下细节。
熊猫和纸浆:分析后台
分析背后的直觉很简单:最好是拥有得分排行榜上的顶级球员(通常是昂贵的),但在特定时间拥有最佳状态的价值球员来抵消顶级得分球员的一些糟糕表现也是好的。
根据直觉,数据操作的两个最重要的属性变成了total_points和form。首先,在进行分析并使用它来准备一个 15 人的球队之前,我们从球员名单中删除受伤的球员。
injured_players_df = player_df[player_df['status'] != 'a']
接下来,基于拥有最佳得分球员的直觉,我们首先强制性地尝试为每个位置安排最佳得分球员。然后我们去填补其余的 11 名球员。
top_players_positions_to_be_filled = {'GKP':1, 'DEF':1, 'MID': 1, 'FWD': 1}
top_players_positions_filled = {'GKP':0, 'DEF':0, 'MID': 0, 'FWD': 0}
i = 0
while top_players_positions_filled != top_players_positions_to_be_filled and i < len(top_players_point_sort):
if teams_filled[top_players_point_sort.iloc[i]['team_code']] > 0 and top_players_positions_filled[top_players_point_sort.iloc[i]['player_type']] < 1:
team_cost += top_players_point_sort.iloc[i]['now_cost']
team_points += top_players_point_sort.iloc[i]['total_points']
positions_filled[top_players_point_sort.iloc[i]['player_type']] -= 1
top_players_positions_filled[top_players_point_sort.iloc[i]['player_type']] += 1
teams_filled[top_players_point_sort.iloc[i]['team_code']] -= 1 players_in_team.append(top_players_point_sort.iloc[i] ['second_name'])
i += 1
我们利用 Python 库PuLP使用线性编程填充剩余的 11 个玩家。要解决的 LP 问题是最大化一个度量(基于过去的数据),同时保持对预算、每个位置要填充的最大玩家数量以及一个团队允许的最大玩家数量(最后一项是用户可定制的输入)的约束。为每个玩家计算的度量是玩家状态和总点数的组合,并且每个的重要性再次由用户决定。
def add_players_using_lp(metric, costs, player_type, team, budget, team_counts,positions_filled):
num_players = len(metric)
model = pulp.LpProblem("Constrained value maximisation", pulp.LpMaximize)
decisions = [
pulp.LpVariable("x{}".format(i), lowBound=0, upBound=1, cat='Integer')
for i in range(num_players)
]
# objective function:
model += sum(decisions[i] * metric[i] for i in range(num_players)), "Objective" # cost constraint
model += sum(decisions[i] * costs[i] for i in range(num_players)) <= budget # position constraints
model += sum(decisions[i] for i in range(num_players) if player_type[i] == 'GKP') == positions_filled['GKP']
model += sum(decisions[i] for i in range(num_players) if player_type[i] == 'DEF') == positions_filled['DEF']
model += sum(decisions[i] for i in range(num_players) if player_type[i] == 'MID') == positions_filled['MID']
model += sum(decisions[i] for i in range(num_players) if player_type[i] == 'FWD') == positions_filled['FWD'] # club constraint
for team_code in np.unique(team):
model += sum(decisions[i] for i in range(num_players) if team[i] == team_code) <= \
team_counts[team_code] model += sum(decisions) == 11 # total team size try:
model.solve()
except:
st.info('Player roster has not been updated yet. Please be patient')return decisions
对于 FPL 团队制造商的转移模式,我们遵循类似的逻辑。这里唯一的不同是,我们加入了现有的用户团队。我们优先转移受伤的球员。如果在现有的队伍中没有受伤的球员,我们计算前面提到的度量,并转移出返回上述度量的最小值的球员。为了取代被淘汰的球员,我们尝试引进那个位置的最佳得分手,如果由于预算限制而不可能,我们使用 LP 来寻找最有价值的球员;后一部分与组队模式相同。
流线:前端
我们使用了streamlit,它是一个 Python 库,提供了使用纯 Python 创建小型前端应用程序的功能。这使得构建一个概念验证网站变得非常简单快速,可以通过 GitHub 托管在 Heroku 上。

应用程序主页(图片由作者提供)
当你访问这个页面的时候,你会看到,第一个问题是比赛周需要更换队伍。这是为了考虑到前面提到的两个不同的数据集。然后,用户将被要求选择模式,输入剩余的预算,给予形式的重要性和总积分,以及每个团队的最大玩家数组队;或传输模式的传输次数。
结果呢
FPL-TeamMaker 是在 2020/21 赛季开始之前建造的,此后作者每周都用它来组建球队或转移球员。结果的好坏由读者来判断。

每周团队得分与平均得分的对比进度(图片由作者提供)

团队积分与每周平均积分。只有 3 周团队分数低于平均分数(图片由作者提供)

过去几周我的团队的总排名和游戏周排名(图片由作者提供)
正如你从上面的图片中所看到的,(1)使用 FPL 团队制造工具的团队从一开始就一直超越平均水平;㈡只有三个比赛周该队的分数低于平均分数;(iii)在经历了灾难性的上周之后,球队已经成功地保持了前 2%的球员 10 周左右;以及(iv)目前队中有 5 / 15 的球员入选了整个梦之队。
结论
你可以利用 FPL 团队制造者来为你自己的团队谋福利。作者将通过页面上的图表定期更新他的团队的进度
关于 FPL 团队制造者的额外提示:当你使用通配符时,使用团队建设模式。
你可以在这里找到 FPL 团队的代码,在这里查看 FPL 团队在 FPL 的积分。在我写这篇文章的时候,这个团队的排名是 134956。
我很乐意在 Linkedin 上与你联系!
基于 COPOD 的快速准确异常检测
原文:https://towardsdatascience.com/fast-accurate-anomaly-detection-based-on-copulas-copod-3133ce9041fa?source=collection_archive---------11-----------------------
一种基于 python 的快速、无参数、高度可解释的无监督异常检测方法

图片由 Marco Verch 专业摄影师
离群值或异常值是偏离数据集标准的数据点。它们使人怀疑它们是由不同的机制产生的。
异常检测(通常)是一项无监督的学习任务,目标是识别数据中的可疑观察。该任务受限于错误地将正常点标记为异常点以及未能标记实际异常点的成本。
异常检测的应用包括网络入侵检测、数据质量监控和金融市场中的价格套利。
基于 opula 的异常检测算法 COPOD 是一种新的异常检测算法。已经在PyOD python 包中实现了。
它有几个关键特性使其从竞争算法中脱颖而出:
- 确定性的
- 没有超参数!(这很重要,因为很难调整异常值检测任务的超参数,因为真实标签很少、未知或难以获得)
- 基准异常检测数据集上的最高性能
- 可解释且易于可视化的异常
- 快速且计算高效
- 扩展到高维数据集
COPOD 最大的挑战是算法背后复杂的数学。本文包含两个部分:
- copulas 关键概念的总结和 COPOD 算法的描述。
- python 中 COPOD 的教程,展示了如何使用这种方法轻松检测数据中的异常。
异常检测的 Copula 模型
定义的连接函数
什么是系词?
简单来说, copula 描述了随机变量之间的依赖结构。
具体来说,“一个 copula 是一个多元累积分布函数,对于它,每个变量的边际概率分布在区间[0,1]上是均匀”(维基百科)。
换句话说,您可以通过从 copula 中分离出每个变量的边际概率分布来描述高维数据集中的概率分布,copula 是描述变量之间相关性的多元概率分布。
“连接函数是使我们能够从给定多元分布的依赖结构中分离出边际分布的函数。”
COPOD 算法
COPOD 算法进行一些数学估计和转换来计算每行的异常值。

算法总结。下面详细描述。
第一步:对于数据集中的每个维度(即列)
- 拟合左尾经验累积分布函数
- 拟合右尾经验累积分布函数
- 计算偏斜系数
第二步:使用第一步中的函数,计算
- 左尾经验 copula
- 右尾经验 copula
- 每行的偏斜度校正的经验 copula 值
基本上,这将导致数据框中每个标量值(行+列)有 3 个新值。
copula 值可以分别解释为左尾、右尾和偏斜校正概率。
步骤 3: 根据在步骤 2 中计算的值,计算数据集每一行的异常分数。这一步依赖于这样一个事实,即较小的尾部概率(又名耦合概率)会导致较大的负对数值。
对于每一行,
- 对左尾经验 copula 的负对数求和
- 对右尾经验 copula 的负对数求和
- 对偏斜度校正的经验 copula 的负对数求和
一行的异常分数是这些总和的最大值。
异常分数
解释:较高的异常分数表明数据实例的概率较低,因为它位于数据分布的尾部。
异常值分数位于(0,∞)之间,并且可直接比较。它们并不直接指示异常值概率。相反,它们测量一行相对于数据集中其他点的可能性。
要从 COPOD 获得异常值预测,有两个选项:
- 对异常值分数设置阈值;任何得分超过阈值的行都是异常的
- 选择 top- k 或 top-k百分点异常值分数。
(在 *PyOD* 中,根据给定的 *contamination* 或训练数据中预期的异常部分自动设置阈值。稍后将详细介绍。)
Python 教程
COPOD 是用[PyOD](https://github.com/yzhao062/pyod) python 实现的,非常容易使用。
首先,加载或生成异常值检测数据。
这里,我使用 PyOD 中的generate_data函数来生成一个包含 200 个训练样本和 100 个测试样本的合成数据集。正态样本由多元高斯分布生成;异常样本是使用均匀分布生成的。
训练和测试数据集都有 5 个特征,10%的行被标记为异常。我在数据中加入了一点随机噪声,使得完美区分正常点和异常点变得稍微困难一些。
下面,我绘制了训练和测试数据集,使用 PCA 投影到二维空间。

合成的训练和测试数据集,通过 PCA 降低到 2 维。
训练和检测
接下来,我根据训练数据拟合 COPOD,并根据测试数据进行评估。
运行fit和decision_function方法的时间不到 1 秒。
在 PyOD 中,(拟合的)异常值检测器有两个关键功能:decision_function和predict。
decision_function返回每一行的异常值。predict返回一个由 0 和 1 组成的数组,指示每一行被预测为正常(0)还是异常值(1)。predict函数只是对decision_function返回的异常分数应用一个阈值。在初始化检测器时,根据指定的contamination速率参数设置自动校准阈值(例如clf=COPOD(contamination=0.1)。contamination表示训练数据中异常值的预期百分比。
COPOD 不需要训练数据!
在现实世界中,您可能没有标记训练数据,这没关系!您仍然可以使用 COPOD 来检测异常值。

正如你在上面的例子中看到的,我初始化了 COPOD 算法,并简单地通过decision_function传递数据而没有拟合。异常值点的异常值分数通常远大于内点或正常数据点的异常值分数。
在应用中,为 COPOD 异常分数选择阈值通常是有用的,以便识别哪些数据作为异常来研究。
酷功能:异常解释
COPOD 可以解释哪个(些)特征对异常值的贡献最大。当数据集中有许多要素时,或者当您想要告诉人类审阅者算法为什么选择特定行作为异常值时,这尤其有用。

第 182 行和第 193 行的维度离群图。
上面的两个维异常值图用蓝线标出了被 COPOD 检测为异常值的两个真实异常值(行)的特征级异常值分数。x 轴表示特征,y 轴表示该特征单独的异常值分数。还绘制了第 90 和第 99 百分位异常值分数。
在左图中(第 182 行),维度 3 的异常值分数超过了第 99 个百分点,这意味着这是该行异常值的主要原因。
在右图中(第 193 行),维度 1 和维度 2 的异常值分数都超过了第 99 个百分点,这也是该行异常的原因。
感谢您的阅读!
COPOD 是一种快速、强大且易于使用的异常检测算法。我希望它能在您的异常检测工具箱中找到一席之地。
要了解关于 PyOD 和异常检测的更多信息,请查看其他文章。
https://pub.towardsai.net/why-outlier-detection-is-hard-94386578be6c [## PyOD:用于异常检测的统一 Python 库
towardsdatascience.com](/pyod-a-unified-python-library-for-anomaly-detection-3608ec1fe321) https://medium.com/geekculture/replace-outlier-detection-by-simple-statistics-with-ecod-f95a7d982f79 https://pub.towardsai.net/an-in-depth-guide-to-local-outlier-factor-lof-for-outlier-detection-in-python-5a6f128e5871
不是中等会员?今天就加入!
参考
【arxiv.org 【2009.09463】COPOD:基于 Copula 的离群点检测
pyod/copod _ example . py at master yzhao 062/pyod GitHub
具有火焰+光线调节的快速自动控制
原文:https://towardsdatascience.com/fast-automl-with-flaml-ray-tune-64ff4a604d1c?source=collection_archive---------9-----------------------
思想和理论
微软研究人员已经开发了 FLAML(快速轻量级 AutoML) ,它现在可以利用射线调整进行分布式超参数调整,以在集群上扩展 FLAML 的资源高效&易于并行化的算法

FLAML 的算法之一 CFO 调整 XGBoost 的叶子数和树数。这两个热图显示了所有配置的损失和成本分布。黑点是在 CFO 中评估的点。由线连接的黑点是在评估时产生更好损耗性能的点(图片由作者提供)。
作者 : 吴青云,王驰,安东尼·鲍姆,理查德·廖和迈克尔·加拉内克
FLAML 是微软研究院的一个轻量级 Python 库,它使用尖端算法,以一种高效且经济的方式找到精确的机器学习模型,该算法旨在节省资源并易于并行化。FLAML 还可以利用光线调整进行分布式超参数调整,以在集群中扩展这些 AutoML 方法。
这篇博客强调了:
- 对经济的自动化方法的需求
- 带火焰的经济型汽车
- 如何使用光线调节来扩展 FLAML 的优化算法
经济型自动化方法的需求
众所周知,AutoML 是一项耗费资源和时间的操作,因为它需要反复试验才能找到性能良好的超参数配置。由于可能的配置值的空间通常非常大,因此需要一种经济的 AutoML 方法来更有效地搜索它们。
AutoML 中超参数搜索的高资源和时间消耗归结于以下两个因素:
- 需要大量候选超参数配置(试验)来寻找性能良好的配置
- 每个超参数的高“评估”成本,因为评估涉及用给定的训练数据训练和验证机器学习模型。
为了解决这两个因素,微软的研究人员开发了 FLAML (快速轻量级 AutoML)。
什么是 FLAML?
FLAML 是一个新发布的库,包含最先进的超参数优化算法。FLAML 利用搜索空间的结构来同时优化成本和模型性能。它包含由微软研究院开发的两种新方法:
- 成本节约优化(CFO)
- 混合搜索
成本节约优化(CFO)是一种以成本感知方式进行搜索过程的方法。该搜索方法从低成本的初始点开始,并逐渐向更高成本的区域移动,同时优化给定的目标(如模型损失或精度)。
Blendsearch 是 CFO 的扩展,结合了 CFO 的节俭和贝叶斯优化的探索能力。像 CFO 一样,BlendSearch 需要一个低成本的初始点作为输入(如果这样的点存在),并从那里开始搜索。然而,与 CFO 不同,BlendSearch 不会等待本地搜索完全收敛后再尝试新的起点。
FLAML 中的经济 HPO 方法受到两个关键见解的启发:
- 许多机器学习算法具有超参数,这些超参数会导致训练成本的巨大变化。例如,有 10 棵树的 XGBoost 模型比有 1000 棵树的模型训练起来要快得多。
- 参数的“成本”通常是“连续和一致的”——评估树=10 比评估树=100 更便宜,树= 100 本身比评估树=500 更便宜。
总之,这些见解提供了关于成本空间中超参数的有用的结构信息。这些方法,即 CFO 和 BlendSearch,能够有效地利用这些见解来降低沿途发生的成本,而不影响收敛到最优解。
【FLAML 管用吗?
在最新的 AutoML 基准测试中, FLAML 能够在超过 62%的任务上仅使用 10%的计算资源实现与最先进的 AutoML 解决方案相同或更好的性能。
FLAML 的性能归功于其经济的优化方法。新的 HPO 方法(CFO,BlendSearch)利用搜索空间的结构来选择搜索顺序,以获得良好的性能和低成本。在预算有限的情况下,这可以大大提高搜索效率。
图 1 显示了从 FLAML 和最先进的超参数调节库 Optuna 获得的典型结果,用于调节具有 9 维超参数的 LightGBM。你可以看到 FLAML 能够在更短的时间内实现更好的解决方案。

图一。在分类数据集上调整 LightGBM 的验证损失(1-auc)曲线。线条和阴影区域显示了 10 次运行的验证损失的平均值和标准偏差。该图中的结果是在没有并行化的情况下从 1 个 cpu 的实验中获得的(图片由作者提供)。
下面的代码示例展示了如何用几行代码开始使用 FLAML(假设提供了训练数据集并保存为X_train、y_train)。任务是用 60 秒的时间预算调整 LightGBM 模型的超参数。
**from** flaml **import** AutoML
automl = AutoML()
automl.fit(X_train=X_train, y_train=y_train, time_budget=60, estimator_list=['lgbm'])
''' retrieve best model and best configuration found'''
print('Best ML model:', automl.model)
print('Best hyperparameter config:', automl.best_config)
在这个例子中,我们在默认的搜索空间中搜索 LightGBM,FLAML 中已经提供了这个搜索空间。FLAML 提供了丰富的定制选项,如学习者类别、搜索空间、评估标准等。
演练示例
现在我们用一个玩具例子来展示 CFO 在用两个超参数调优 XGBoost 时的成本节约行为:树的数量和叶的数量。
'''create an XGBoost learner class with a customized search space'''
**from** flaml.model **import** XGBoostSklearnEstimator
**from** flaml **import** tune
**class** **MyXGB**(XGBoostSklearnEstimator):
'''XGBoostSklearnEstimator with a customized search space'''
@classmethod
**def** **search_space**(cls, data_size, **params):
upper = min(2**15, int(data_size))
**return** {
'n_estimators': {
'domain': tune.lograndint(lower=4, upper=upper),
'low_cost_init_value': 4,
},
'max_leaves': {
'domain': tune.lograndint(lower=4, upper=upper),
'low_cost_init_value': 4,
},
}
'''Use CFO in FLAML to tune XGBoost'''
**from** flaml **import** AutoML
automl = AutoML()
automl.add_learner(learner_name='my_xgboost', learner_class=MyXGB)
automl.fit(X_train=X_train, y_train=y_train, time_budget=15, estimator_list=['my_xgboost'], hpo_method='cfo')
CFO 和 BlendSearch 如何工作
下面两张 gif 分别演示了 CFO 在损失和评估成本(即评估时间)空间的搜索轨迹。CFO 从一个低成本的初始点(通过搜索空间中的low_cost_init_value指定)开始,并按照其随机化的局部搜索策略执行局部更新。采用这样的策略,CFO 可以快速向低损耗区移动,表现出良好的收敛特性。此外,CFO 倾向于避免探索高成本区域,直到必要时。该搜索策略进一步基于可证明的收敛速度和期望的有界成本。

图二。CFO 在为 XGBoost 调整树叶的数量和树的数量。这两个热图显示了所有配置的损失和成本分布。黑点是在 CFO 中评估的点。由线连接的黑点是在评估时产生更好损耗性能的点(图片由作者提供)。
BlendSearch 进一步将 CFO 中使用的这种局部搜索策略与全局搜索相结合。它利用了 CFO 的节俭和贝叶斯优化等全局搜索方法的空间探索能力。具体来说,BlendSearch 维护一个全局搜索模型,并根据全局模型提出的超参数配置,随着时间的推移逐渐创建局部搜索线程。它进一步根据实时性能和成本来区分全局搜索线程和多个局部搜索线程的优先级。它可以进一步提高 CFO 在具有复杂搜索空间(例如,包含多个不相交、不连续子空间的搜索空间)的任务中的效率。
FLAML 与贝叶斯优化性能
图 3 显示了 FLAML 中经济的 HPO 方法的典型行为(CFO 在该图中标记为“LS ”),对比了使用 11 个超参数调整 XGBoost 的贝叶斯优化(BO)方法。
从图 3(a)中,我们观察到 BO 中建议配置的评估时间可能非常长。当总资源有限时,例如 1 个 cpu 小时(或更少),BO 不能给出令人满意的结果(图 3(b))。
FLAML 的 CFO(标记为 LS)和 BlendSearch 在快速找到好的配置方面具有明显的优势:它们能够专注于具有低评估时间的配置,同时导航具有良好性能(即低损失)的配置。

图 3。(a)是由不同方法提出的超参数配置的散点图,x 轴和 y 轴是评估时间和损失。超参数配置的评估时间是在训练数据上使用超参数配置训练机器学习模型并在验证数据集上验证其性能所花费的时间。这种损失就是验证损失。(b)显示了在挂钟时间内通过不同方法获得的最佳损耗。(图像来源
如何利用 Ray Tune 的分布式调优来扩展 CFO 和 BlendSearch
为了加速超参数优化,您可能想要并行化您的超参数搜索。例如,BlendSearch 能够在并行设置中很好地工作:它利用多个搜索线程,这些线程可以独立执行,而不会明显降低性能。对于诸如贝叶斯优化的现有优化算法来说,这种期望的特性并不总是真实的。
为了实现并行化,FLAML 与 Ray Tune 进行了集成。Ray Tune 是一个 Python 库,通过允许您大规模利用尖端优化算法来加速超参数调整。Ray Tune 还允许您将超参数搜索从笔记本电脑扩展到集群,而无需更改代码。您可以在 FLAML 中使用光线调节,或者在光线调节中运行 FLAML 的超参数搜索方法,以并行化您的搜索。下面的代码示例展示了前一种用法,只需在 FLAML 中配置n_concurrent_trials参数即可实现。
'''Use BlendSearch for hyperparameter search, and Ray Tune for parallelizing concurrent trials (when n_concurrent_trials > 1) in FLAML to tune XGBoost'''
**from** flaml **import** AutoML
automl = AutoML()
automl.add_learner(learner_name='my_xgboost', learner_class=MyXGB)
automl.fit(X_train=X_train, y_train=y_train, time_budget=15, estimator_list=['my_xgboost'], hpo_method='bs', n_concurrent_trials=8)

Logo 来源( XGBoost 、 FLAML 、 Ray Tune )
下面的代码显示了后一种用法,这是一个如何将 BlendSearch 与 Ray Tune 结合使用的端到端示例。
**from** ray **import** tune
**from** flaml **import** CFO, BlendSearch
**import** time
**def** **training_func**(config):
'''evaluate a hyperparameter configuration'''
# we use a toy example with 2 hyperparameters
metric = (round(config['x'])-85000)**2 - config['x']/config['y']
# usually the evaluation takes a non-neglible cost
# and the cost could be related to certain hyperparameters
# in this example, we assume it's proportional to x
time.sleep(config['x']/100000)
# use tune.report to report the metric to optimize
tune.report(metric=metric)
# provide the search space
search_space = {
'x': tune.lograndint(lower=1, upper=100000),
'y': tune.randint(lower=1, upper=100000)
}
# provide the low cost partial config
low_cost_partial_config={'x':1}
# set up BlendSearch
blendsearch = BlendSearch(
metric="metric", mode="min",
space=search_space,
low_cost_partial_config=low_cost_partial_config)
blendsearch.set_search_properties(config={"time_budget_s": 60})
analysis = tune.run(
training_func, # the function to evaluate a config
config=search_space,
metric='metric', # the name of the metric used for optimization
mode='min', # the optimization mode, 'min' or 'max'
num_samples=-1, # the maximal number of configs to try, -1 means infinite
time_budget_s=60, # the time budget in seconds
local_dir='logs/', # the local directory to store logs
search_alg=blendsearch # or cfo
)
print(analysis.best_trial.last_result) # the best trial's result
print(analysis.best_config) # the best config
其他关键光线调节功能包括:
- 与 Tensorboard 和重量/偏差等实验跟踪工具自动集成
- 对 GPU 的支持
- 提前停止
- 一个 scikit-learn API,可以轻松地与 XGBoost 、 LightGBM 、 Scikit-Learn 等集成。
基准测试结果
我们进行了一项实验,以检查 BlendSearch 在高度并行化的设置中与 Optuna(使用多变量 TPE 采样器)和随机搜索相比表现如何。我们使用了来自 AutoML 基准的 12 个数据集的子集。使用 ROC-AUC(多类数据集的加权一对一对比)使用三重交叉验证,平行进行 16 次试验,每次优化运行 20 分钟。用不同的随机种子重复运行三次。复制代码可以在这里找到。

作者图片
BlendSearch 在 12 个数据集的 6 个中获得了最佳交叉验证分数。此外,BlendSearch 比随机搜索平均提高了 2.52%,而 Optuna 的平均提高为 1.96%。值得注意的是,BlendSearch 使用单变量 Optuna-TPE 作为其全局搜索器——使用多变量 TPE 最有可能进一步提高分数。

作者图片
此外,由于其成本节约的方法,BlendSearch 在相同的时间限制内,平均评估的试验次数是其他搜索者的两倍。这表明 BlendSearch 和其他算法之间的差距将随着时间预算的增加而增加。
结论
FLAML 是一个新发布的库,包含最先进的超参数优化算法,该算法利用搜索空间的结构来同时优化成本和模型性能。FLAML 还可以利用射线调优进行分布式超参数调优,从而在集群中扩展这些经济的 AutoML 方法。
有关 FLAML 的更多信息,请参见 GitHub 资源库和项目页面。如果你想了解雷的最新消息,可以考虑关注 twitter 上的@ Ray distributed和注册时事通讯。
原载于https://www.anyscale.com。
