夜天之书 #73 Apache Pulsar 的社群指标

news2024/10/2 10:28:07

去年十一月,我成为了 Apache Pulsar[1] 社群 Committers 的一员。

成为 Committer 之前和之后,我都积极参与了代码仓库上 Issue 和 Pull Request (PR) 的处理回应和评审。去年十二月期间,我把未解决的 Issue 和 PR 数量分别从接近 2000 个和 400 个,在动态增长的情况下缩小到了小于 1000 个和接近 200 个。

关于如何高效地、分门别类地处理社群积压的议题和补丁,我可能会用另一篇文章来讨论。本文想讲的是在这个过程里我产生的另一个想法,那就是如何分析在这类过程中产生的活动,从而判断社群的健康情况呢?

85329eb2cfdb0201f503b95fa5507bba.png

tisonkun activities in 2022

数据源与展示

要想做数据分析,首先应该确定使用什么数据源。不同数据源的数据质量和数据口径有很大的差距,可能导致同一个逻辑概念最终算出来的结果之间大相径庭。

我们的目标是分析社群成员的在 GitHub 平台上的活动指标和趋势,而 GitHub 本身提供了 Event 接口[2]获取平台上的所有公开活动。

不过,这个接口返回的数据只有最近九十天的数据,要想获取完整的数据,就需要依靠一个从近十年前已经开始爬取 GitHub Events 数据并提供开放下载的项目 GH Archive[3] 了。关于如何从这个数据集构建一个自有的数据源,可以参考《基于 ClickHouse 的 GitHub 事件数据库》这篇文章。

GH Archive 提供的是 GitHub 全平台所有活动的数据。由于 GitHub 日益活跃,这个数据集的总体大小在几个 TB 的量级。虽然对于一个具体项目社群比如 Pulsar 来说,筛选出与己相关的仓库的活动最终大约只有 MB 量级的数据,但是使用 GH Archive 的数据始终要过一遍全量的活动数据。

另一种方案是把 GH Archive 的工作也集成到数据源制作的工具集里,直接从 GitHub 的 Event 接口里仅拉取相关仓库的活动。这样做可行的原因是往往最近的数据才是最有价值的,一个仓库五年前是什么活跃水平,很大程度上只是一个图一乐的数字。不过,要想这么做,数据源生成的流水线也不简单,需要维护一个定期任务的调度,以及数据去重和补偿的逻辑。

最终,我选择了国内 X-Lab 开放实验室[4]维护的、基于 GH Archive 数据的、存储在 ClickHouse 当中并提供相同查询接口的 GitHub Event 数据集。该数据集的数据模式[5]定义是公开的,相比 ClickHouse.com 提供的 github_events[6] 数据集,X-Lab 的数据集包含了实验室在开源数据指标工作中发现的有效提高数据可用性的字段,包括 repo_id 和 actor_id 等等。此外,X-Lab 的数据集本身是其开源数据指标工作的数据基础,维护投入力度和问题响应速度更可靠。

在数据展示层面,我选择了 ClickHouse 去年官方支持的 Apache SuperSet 系统。你可以选择 follow 下面两个文档自建系统,或者直接使用 Preset[7] 提供的 Free Tier 服务,其中已经预装了连接 ClickHouse 所需的组件。

•Connect Superset to ClickHouse[8]•Connecting to Databases - ClickHouse[9]

配置好数据源并连接上展示系统之后,就可以开始探索社群活跃指标了。

评审人数人次

我们想要评估的活跃指标是参与 Issue 和 PR 处理与评审的状况,可以从最基本的指标开始做起:数数。

把活动范围限制在 Pulsar 主仓库当中,我们可以用如下 SQL 来取得一段时间内评审 PR 的人数:

SELECT
  toStartOfMonth(toDateTime(`created_at`)) AS timestamp,
  COUNT(DISTINCT actor_login),
  COUNT(DISTINCT actor_login, issue_number)
FROM `opensource`.`gh_events`
WHERE `created_at` >= '2022-01-01 00:00:00'
  AND `created_at` < '2023-01-01 00:00:00'
  AND `repo_name` = 'apache/pulsar'
  AND `type` IN ('PullRequestReviewEvent',
                 'PullRequestReviewCommentEvent')
GROUP BY timestamp
ORDER BY timestamp

其中第一个 COUNT DISTINCT 结果表示的是在给定时间段内有多少个不同的 Reviewers 参与,而第二个 COUNT DISTINCT 的结果则是计算人次,即只把同一个人在同一个 PR 当中的 Review 动作合并,而不是把同一个人所有的 Review 活动都做合并。

在 SuperSet 的 UI 上做一些配置,我们可以把上面的 SQL 对应到下面的折线图:

019e0c8e98d27cd2e41730bd847ba652.png

Pulsar Reviews Count in 2022

可以看到,Pulsar 社群在去年一年的 Reviewer 数量维持在 60~70 人之间,而在 10 月之后 Review 人次计数有所下降。

对这个数字的解释,人数上,Pulsar 社群的活跃 Reviewer 群体没有大的变化。人次上,在年中开始发布 2.11 之后,出现了一系列测试出来的回退,核心开发者专注于解决这些回退,而解决回退最好是尽量少的改动,因此 PR 的总体新增数量少了。同时,cheery-pick 和处理回退的 PR 相对直观,往往只需要一两名 reviewer 赞同就已经合并,而不会像新功能一样有大量的成员发表意见。

交叉对比 PR 创建数量的图,可以印证这个想法:

39d1387279cc8e5ccd7c082b1bc105fd.png

Pulsar Pull Request Opened Count

最后,我们以 DISTINCT Reviewr 的人数为指标,比较一下知名的消息系统项目的活跃情况:

b80f3c7d5637653c53fd79d2e8f2d7c7.png

Reviewers Comparation

可以看到,在这一维度下,Pulsar 的活跃度是最高的,Kafka 次之;RocketMQ 和 RedPanda 是下一个梯队;而 ActiveMQ 和 RabbitMQ 的活跃度就快见底了。当然,这个数据只计算和主仓库的数值,而没有把所有仓库群加总起来算,但是加总的结果应当也是大同小异的。另一方面,核心能力的绝大部分开发工作还是发生在主仓库上,计算主仓库的活跃度可以见微知著。

平均响应时间

除了最基本的计数工作,我们还可以计算跨越一定时间的指标。例如,在工程效能乃至服务保障中都常包括的平均响应时间。结合我们这里想要评估的 Issue 和 PR 处理与评审的状况,我们可以分别计算 Issue 和 PR 从创建到首次被响应的时间,以及从创建到关闭的时间。

数据口径和挑战

不过,不同于简单计数指标,在事件粒度的数据集上做具有时间跨度的指标必定涉及数据之间的关联。如果不是类似 Apache Pulsar 活跃参与者每月看板[10]这样,在取出数字后再经过程序处理,而是类似上面直接使用 SuperSet 作为一站式数据分析展示工具,那么关联数据的需求映射到 SQL 当中就是 JOIN 的语义,这会导致查询复杂度陡然上升一个量级,写出正确或者说合理的查询面临诸多挑战。

第一个是数据关联本身的复杂性。

要想知道具体一个 Issue 从创建到首次响应,再到被处理完毕一共花了多久,最准确的方法是通过 GitHub API 直接查询 Issue 的元数据,首先看它是否被关闭,如果被关闭,是何时被关闭。进一步地,列举 Issue 上的 comment 并取得最先提出的那个的时间,与 Issue 创建时间求差。

•Get an issue[11]•List issue comments[12]

然而,这类数据是变化的,即使每天定时任务同步,也只能给特定的一个查询使用,从数据治理角度来说成本太高,最好是就写一个程序加上缓存硬算。这不仅脱离了 SuperSet 提供的框架,本身也会被 GitHub API 的 rate limit 限制。

从 GH Archive 数据集的事件信息出发,要想做这样的计算,就得首先取得 Issue 编号,再和其他 Issue 系列事件关联,适当排序后才能得到正确的结果。为了减少关联的复杂性,往往分析存储的每行会冗余信息。ClickHouse.com 和 X-Lab 的数据集都是一张大宽表,而 X-Lab 的数据更是每行都有 issue_created_at 和 issue_closed_at 这样的预聚合信息。

第二个是数据关联对数据质量的高要求。

SQL 查询要想得到正确的结果,能不能正确关联上多个事件是非常重要的。而实际数据集至少会面临以下问题:

1.数据丢失。GitHub 本身的服务就是不稳定的,API 上能查到服务恢复后的状态,但是事件信息说丢了就是丢了。很多基于 GH Archive 数据集计算的结果在涉及 2021 年 10 月的数据时就容易出错,正是因为 GitHub 那一个月丢了近三天的事件数据。2.异构社群。例如,有些社群会用机器人来做首次回复,计算社群活跃度计入和不计入机器人的行为显然是不一样的。而不同的社群有不同的自动化工具和流程,进行数据关联时需要处理这些细节问题。3.不单调的数据。计算 Issue 创建到首次响应的指标相对还是简单的,因为第一次响应是个确定的指标,一旦得出结果就不再变化。但是从创建到关闭并不是,因为还有 Reopen 的情况。如果要把 Reopen 的情况考虑进去,数据口径和计算方法就需要重新评估。

第三个是关联时间的指标的口径差异。

OSSInsight[13] 和 OpenDigger[14] 对上述两个时间数据的算法是以结束时间为基准的,即它们所说的 2022 年 12 月 Pulsar 社群 Issue 创建到关闭的平均时间,指的是在 2022 年 12 月被关闭的 Issue 从创建到关闭的平均时间。这样说你可能还没感觉出问题,我举个实例:我在 2022 年 11~12 月关闭了一大批年代久远的 Issue 和 PR 的行为导致 OSSInsight 上的指标在这两个月突然变差:

fa688bc7a6220742a620b9b827abcc16.png

OSSInsight Pull Request Time to Merge

676ea90653ab75ac11940b7d3d0f25f1.png

OSSInsight Issue Time to Respond

但是,实际上这与这两个月新创建的 Issue 和 PR 的处理实效关联不大。

同时,可以注意到 OSSInsight 只给出了 Issue 首次响应,以及 PR Merge 的时效。PR 关闭后可以重新打开,但是合并后则不再能转变状态。这一点,加上从结束时间倒算时间跨度,都是为了好算而做的选择。

下面,我会说明我所采用的指标口径、结果及社群之间的对比。

指标结果与对比

我所计算的时效指标,时间分区的逻辑是以开始时间作为基准的,即 2022 年 12 月 Pulsar 社群 Issue 创建到首次回应的平均时间,指的是在 2022 年 12 月期间新建的 Issue 被首次回应的平均时间。这个首次响应可能发生在 2022 年 12 月以后的任何一个时间。

不过,马上你就会发现两个问题:第一个是如果 Issue 迄今为止还没被响应,那这个时间就是未定义的;第二个是按照这种算法,计算出来的时效一定不会超过从创建到当前的时间,换言之,这个月的时效一定不会超过一个月。

为了辅助以开始时间作基准的指标逻辑,这里需要额外计算 Issue 和 PR 被处理的比例。我先展示最终计算得到的图表:

c488498ae936e20bce06161a1c45810b.png

Pulsar 社群 Issue 和 PR 响应时效

右边一列就是区分所有在给定时间段内创建的 Issue 或 PR 的响应情况占比。可以看到,如同上面说明的,Issue 和 PR 创建的时间越近,其得到处理的比例就越小。而处理时效仅考虑被处理的情况,近期的数据总是相对较好。

实际在指导社群开展工作的时候,可以使用类似:在未处理 Issue/PR 比例不超过 N% 的前提下,平均响应时间不超过 M 小时。

不过,由于后来响应会导致原先的平均值被离群值拉高,所以在执行时可以采用分位数的维度来减少离群值的影响,同时仅在每月一号当天查看上个月的情况,而不是一直监控。

具体的计算 SQL 语句,以 PR 的回复时效为例,写作:

SELECT issue_number,
    groupArray(b.actor_login)[1] AS author,
    groupArray(actor_login)[1] AS reviewer,
    dateDiff('hour', opened_at, commented_at) AS time_to_comment,
    groupArray(b.created_at)[1] AS opened_at,
    groupArray(created_at)[1] AS commented_at,
    groupArray(type)[1] AS type,
    groupArray(action)[1] AS action_type
   FROM
     (SELECT actor_login,
             created_at,
             type,
             action,
             issue_number
      FROM `opensource`.`gh_events`
      WHERE repo_name = 'apache/pulsar'
        AND type IN ('PullRequestEvent',
                     'PullRequestReviewCommentEvent',
                     'PullRequestReviewEvent',
                     'IssueCommentEvent')
        AND actor_login NOT IN ('github-actions[bot]',
                                'codecov-commenter')
      ORDER BY created_at) a
   JOIN
     (SELECT actor_login,
             created_at,
             issue_number
      FROM `opensource`.`gh_events`
      WHERE repo_name = 'apache/pulsar'
        AND type = 'PullRequestEvent'
        AND action = 'opened') b ON a.issue_number = b.issue_number
   WHERE a.actor_login != b.actor_login
   GROUP BY issue_number
   ORDER BY issue_number

由于 X-Lab 的数据做了一些预聚合,计算 PR 合并时效可以不用 JOIN 语句:

SELECT issue_number,
    MIN(issue_created_at) AS opened_at,
    MAX(pull_merged_at) AS merged_at,
    dateDiff('hour', opened_at, merged_at) AS time_to_merge
   FROM `opensource`.`gh_events`
   WHERE repo_name = 'apache/pulsar'
     AND type IN ('PullRequestEvent',
                  'PullRequestReviewCommentEvent',
                  'PullRequestReviewEvent')
   GROUP BY issue_number
   HAVING notEmpty(groupArray(pull_merged_at))

计算比例的 SQL 查询结构相同,但是在最外层 SELECT 仅保留 issue_number 和 opened_at 两列,并用 CASE WHEN 来区分类别:

-- Respond --
(CASE
    WHEN COUNT(DISTINCT actor_login) > 1 THEN 'respond'
    WHEN notEmpty(groupArray(pull_merged_at)) THEN 'selfmerge'
    WHEN notEmpty(groupArray(issue_closed_at)) THEN 'selfclose'
    ELSE 'norespond'
END) AS status


-- Merge --
(CASE
    WHEN notEmpty(groupArray(pull_merged_at)) THEN 'merged'
    WHEN notEmpty(groupArray(issue_closed_at)) THEN 'closed'
    ELSE 'open'
END) AS status

Issue 的情况大同小异,需要注意的是也需要从 IssuesEvent + opened 的查询里获取 issue_number 的值,而不能直接用 gh_events 里的 issue_number 字段,因为后者还包括 PR 的情况。

最后是同类社群之间做一个指标的比较。由于 Kafka 的 Issue 用 JIRA 来管理,同时不同体量的社群在时效上会有较大的区别,例如线下集中开发的为主的团体几乎总是能立即响应团队内部的需要,因此这里只对 PR 的响应和合并时效,针对 Apache Pulsar 社群和 Apache Kafka 社群做一个比较。

PR 响应时效的比较结果如下:

3e3921997e09f7a45c633c78b88bca0a.png

Pulsar vs. Kafka PR 响应时效

PR 合并时效的比较结果如下:

bbae617f332d0f9d097c6d5e5146a47e.png

Pulsar vs. Kafka PR 合并时效

可以看到,在去年一年的时间里,Pulsar 的社群活跃度在这个维度上全面超过了 Kafka 社群。

小结

应该说,Pulsar 社群的活跃,一方面得益于其丰富的功能集带来的海量开发需求,另一方面也是社群多样化的成员组成带来的活力促进的。不同于 Kafka 上游主要依靠 Confluent 公司的雇员开发,一旦公司技术转向就会活力骤减,Pulsar 社群从提供商、集成商到应用企业,再到喜欢完整的 MQ 语义的个人开发者,不同人的需求在这里交汇和实现,与参与者同等数量的 Reviewer 参与评审,为软件长青提供了坚实的社群保障。

应该说,Pulsar 目前的开发者社群活跃度,还能够接纳不少寻找分布式消息系统的架构师,以及想要学习消息系统概念与实现的开发者。我在过去的半年里,逐步撰写了 Pulsar 社群的开发者指南[15],以期能够帮助到有意愿的开发者快速搭建一个本地开发环境,并参与到典型的开发活动当中来。此外,我还会在接下来的一两个月里,梳理出 Pulsar 社群生态的全貌,从而展示 Pulsar 生态的价值与其目前需要帮助的部分。按照《共同创造价值》的思路,这样就可以回答“我能为社群做什么”以及“应该怎么做到”的两个问题。

References

[1] Apache Pulsar: http://github.com/apache/pulsar
[2] Event 接口: https://docs.github.com/en/rest/activity/events?apiVersion=2022-11-28
[3] GH Archive: https://www.gharchive.org/
[4] X-Lab 开放实验室: http://www.x-lab.info/
[5] 数据模式: https://github.com/X-lab2017/open-digger/blob/master/docs/assets/data_description.csv
[6] github_events: https://ghe.clickhouse.tech/#how-to-choose-the-structure-for-the-data
[7] Preset: https://preset.io/
[8] Connect Superset to ClickHouse: https://clickhouse.com/docs/en/connect-a-ui/superset-and-clickhouse/
[9] Connecting to Databases - ClickHouse: https://superset.apache.org/docs/databases/clickhouse/
[10] Apache Pulsar 活跃参与者每月看板: https://town.korandoru.io/dashboard/pulsar-active-contributors-monthly/
[11] Get an issue: https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue
[12] List issue comments: https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments
[13] OSSInsight: https://ossinsight.io/
[14] OpenDigger: http://www.x-lab.info/open-digger/#/
[15] Pulsar 社群的开发者指南: http://pulsar.apache.org/contribute/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374349.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32学习笔记-I2C通信协议

文章目录介绍&#xff1a;两种实现方式&#xff1a;I2C设备的常用连接方式&#xff1a;I2C协议时序&#xff1a;STM32硬件I2C框架图I2C外设通讯过程**I2C读写EEPROM**&#xff08;硬件I2C&#xff09;介绍&#xff1a; 两根通信线SCL&#xff08;时钟线&#xff09;、SDA&#…

C语言中的强制类型转换

强制类型转换是把变量从一种类型转换为另一种数据类型。例如&#xff0c;如果您想存储一个 long 类型的值到一个简单的整型中&#xff0c;您需要把 long 类型强制转换为 int 类型。您可以使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型&#xff0c;如下所示&am…

“ChatGPT之父”Sam Altman:如何成功?

背靠微软&#xff0c;OpenAI能拳打谷歌&#xff0c;脚踢Meta&#xff0c;它背后的男人&#xff0c;必然不简单。 让我们来看一看&#xff0c;Sam Altman是如何一步步成长为今天这个搅动全世界的男人。 山姆奥特曼&#xff08;Sam Altman&#xff09; 成长和创业经历 在YC创始…

代码随想录【Day27】| 39. 组合总和、40. 组合总和 II、131. 分割回文串

39. 组合总和 题目链接 题目描述&#xff1a; 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明&#xff1a; 所有数字&#xff08;包括 tar…

JavaScript 高级2 :构造函数和原型 d331702016e84f54b3594ae05e0eeac

JavaScript 高级2 &#xff1a;构造函数和原型 Date: January 16, 2023 Text: 构造函数和原型、继承、ES5中的新增方法 目标 能够使用构造函数创建对象 能够说出原型的作用 能够说出访问对象成员的规则 能够使用 ES5新增的一些方法 构造函数和原型 概述 在典型的 OOP 的…

MySQL之EXPLAIN

使用方法 查询结果分析 id&#xff1a;识别符 select_type&#xff1a;表示查询的类型 table&#xff1a;输出结果集的表 partitions&#xff1a;匹配的分区 type&#xff1a;表示表的连接类型 possible_keys&#xff1a;表示查询时&#xff0c;可能使用的索引 key&#xff1a…

jni-Demo-基于linux(c++ java)

跑一个jni 的最简单的Demo需要提前准备 VsCode 编译器、win10下&#xff0c;vscode中集成linux操作系统、c编译器&#xff08;gcc、g&#xff09;&#xff0c;java编译器&#xff08;jdk1.8&#xff09;参考&#xff1a;https://mangocool.com/1653030123842.htmlJniDemo类&…

【分享】灌溉制度设计小程序VB源代码

说明 根据作物需水特性和当地气候、土壤、农业技术及灌水技术等因素制定的灌水方案。主要内容包括灌水次数、灌水时间、灌水定额和灌溉定额。灌溉制度是规划、设计灌溉工程和进行灌区运行管理的基本资料&#xff0c;是编制和执行灌区用水计划的重要依据。 1—计划湿润土层允…

spring面试题总结

1、spring是什么&#xff1f; spring是一个轻量级IOC和AOP容器框架&#xff0c;是为Java应用程序提供基础性服务的一套框架&#xff0c;目的是用于简化企业应用的开发&#xff0c;开发者只需要关注业务需求即可&#xff1a; core container 容器组件 spring context&#xff0c…

@ConfigurationProperties在方法上的使用

文章目录1. 前言2. 先说结论3. 代码解释1. Component ConfigurationProperties2. EnableConfigurationProperties ConfigurationProperties3. Bean ConfigurationProperties1. 前言 在学习spring的时候&#xff0c;ConfigurationProperties应该经常被使用到&#xff0c;作用…

一文弄清混合云架构模式

当我们在说云架构的时候&#xff0c;通常指的并不是云平台的自身架构&#xff0c;而是基于云平台的软件系统基础架构。云平台的自身架构满足了很多通用层面的需求&#xff0c;例如对象存储&#xff0c;弹性主机&#xff0c;虚拟网络等等&#xff0c;只有云服务厂商的工程师才会…

Win10系统内置杀毒软件Windows Defender卸载方法

Windows10系统自带的Windows Defender杀毒软件&#xff0c;偶尔会使你的电脑CPU满负荷运载。 其实我们完全可以删除这个Windows10自带的杀毒软件&#xff0c;我们忍耐总是有限度的&#xff0c;所以&#xff0c;你可以选择删除它&#xff1b; 怎么删除呢&#xff1f;下面有个方…

Pthon--自动化实用技巧篇--文件目录处理

为什么要讲这一篇&#xff0c;主要是因为这个在自动化测试框架或者脚本的编写的时候会用到&#xff0c;还是比较方便的。看上述两个函数。getcwd()、chdir()。使用 os.getcwd() 函数获得当前工作目录。使用 os.chdir()函数改变当前工作目录。所以在用chdir()函数的时候别忘记指…

数据类型和变量

目录 ​编辑 一、字面常量 1、字面常量的定义 2、字面常量的分类 二、数据类型 三、变量 1、变量的概念 2、语法格式 3、整型变量 &#xff08;1&#xff09;整型变量 &#xff08;2&#xff09;长整型变量 &#xff08;3&#xff09;短整型变量 &#xff08;4&a…

带你深入了解c语言指针后续

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍c语言中有关指针更深层的知识. 金句分享: ✨在该…

shell学习3

目录 一、查找并删除重复文件 二、列举文件类型统计信息 三、只列出目录 一、查找并删除重复文件 重复文件是同一个文件的多个副本。有时候我们需要删除重复的文件&#xff0c;只保留其中一份。通过查看文件内容来识别重复文件是件挺有意思的活儿。可以结合多种shell工具来完…

阶段六:服务框架基础(第二章Day-MQ(服务异步通讯))

阶段六&#xff1a;服务框架基础&#xff08;第二章Day-MQ&#xff08;服务异步通讯&#xff09;&#xff09;Day-RabbitMQ1.初识MQ1.1.同步和异步通讯1.1.1.同步通讯1.1.2.异步通讯1.2.技术对比&#xff1a;2.快速入门2.1.安装RabbitMQ 【重要】2.1.1、安装RabbitMQ&#xff0…

换掉 Maven,我就用Gradle,急速编译

相信使用Java的同学都用过Maven&#xff0c;这是一个非常经典好用的项目构建工具。但是如果你经常使用Maven&#xff0c;可能会发现Maven有一些地方用的让人不太舒服&#xff1a; Maven的配置文件是XML格式的&#xff0c;假如你的项目依赖的包比较多&#xff0c;那么XML文件就…

[Datawhale][CS224W]图机器学习(六)

目录一、简介二、概述三、算法四、PageRank的缺点五、Python实现迭代法参考文献一、简介 PageRank&#xff0c;又称网页排名、谷歌左侧排名、PR&#xff0c;是Google公司所使用的对其搜索引擎搜索结果中的网页进行排名的一种算法。 佩奇排名本质上是一种以网页之间的超链接个…

蒙特卡洛随机模拟

蒙特卡洛随机模拟 简介 蒙特卡洛模拟是在计算机上模拟项目实施了成千上万次&#xff0c;每次输入都随机选择输入值。由于每个输入很多时候本身就是一个估计区间&#xff0c;因此计算机模型会随机选取每个输入的该区间内的任意值&#xff0c;通过大量成千上万甚至百万次的模拟…