mi.11.info,有哪些网站可以查看行业数据?
最近,有粉丝留言,问这些行业报告一般都在哪里才能找到。
本着“授人以鱼,不如授人以渔”的职业操守,今天就跟大家分享11个经常闲逛的行业报告网站。
这些行业报告网站,包括免费和付费,接下来看一下。
一、免费行业报告1、IXDC
打开IXDC这个网站,点击行【业报告】,此时会跳转到另一个页面。
https://ixdc.org/
跳转之后的页面,其中就包含很多行业报告案例。
https://ixdc.org/2021/trendreport.html
这里大家可以观看一些最新的报告,找一些不错的案例,点击【完整版】打开看一下。
打开之后,会出现一个在线PDF观看页面,浏览一下,如果感觉不错,直接点击下载按钮。
此时,即可获取一份精美的行业报告PPT,学习一下里面的设计思路。
2、艾瑞网
对于艾瑞网,大家应该有听过,也是一个行业内比较不错的资讯网站。
https://www.iresearch.cn/
打开之后,点击【研究报告】,可以按照行业、类型、时间查找。
之后点击打开,可以先在线浏览一番,如果感觉有用,点击下载。
3、移动观象台
移动观象台,也有很多不错的行业报告。
http://mi.talkingdata.com/index.html
然后点击【数据报告】,根据自己的用途,选择不同类型报告。
选择之后,直接打开,点击【下载此报告】即可下载。
4、镝数据
镝数据,大家应该常用吧,除了一些可视化工具的使用,里面还有很多行业报告。
https://www.dydata.io/
如果你是第一次注册登录,还可以获取一份50套精选行业报告。
打开之后,点击【数据商品】—【行业报告】,此时会有2种类型可供下载,免费和付费。
这里打开一个免费行业报告,直接点击【下载数据】即可下载。
5、中国连锁经营协会
这个网站算是一个小众网站,或许大家知道的少,里面也有一些行业报告可以下载。
http://www.ccfa.org.cn/portal/cn/index.jsp
选择之后,将其打开,点击相应的行业报告即可下载。
6、亿欧
亿欧网不用多说了,打开之后,点击【研究】,即可查看相应的行业报告。
https://www.iyiou.com/research
然后将其打开,可以先在线阅览,最后再点击【下载报告】。
7、报告查一查
报告查一查,不仅可以下载相应的行业报告,还可以直接搜索。
http://report.seedsufe.com/index
这里点击【报告】,可以按照类型选择。
之后将其打开,点击下载即可。
8、极光
对于极光这个网站,大家或许了解的也少,这是一共工具型网站,里面也有一些不错的行业报告。
https://www.jiguang.cn/
打开之后,点击【行业洞察】—【行业报告】。
根据自己的使用需求,即可查看相应的行业报告。
之后,点击【下载报告】打开。
最后,阅览之后,点击下载即可。
说完免费的行业报告网站,还有一些付费的网站,这些付费网站价格可不低,土豪随意。
二、付费行业报告1、发现报告
首先看一下这个【发现报告】付费网站,或许大家应该也有所了解。
https://www.fxbaogao.com/
打开之后,首页就有推荐的热门行业报告,时间也是最新。
选择一个,点击下方的【免费查看完整报告】打开看看。
如果此时想要下载,就需要开通会员或单个下载。
2、蜗牛报告
百度文库蜗牛报告,是一个综合类行业报告网站。
https://snail.baidu.com/#/
下载时,不用说,需要开通会员。
3、艾媒网
这个艾媒网也是只对会员开放使用,不过里面确实有很多不错的行业报告。
https://www.iimedia.cn/c400
点击【行业报告】即可查看。
最后将其拉到底部,如果点击下载,就会弹出一个付费页面。
以上图片案例素材均来源于网络分享,侵删
关于本次的主题内容,就分享这么多,希望对你有帮助。
下期见!
去哪儿找免费的市场调研报告?
在一些相关的官方的网站就可以看到啦!具体的列在下文了~
但是一个网站一个网站的找,确实是很麻烦,也不是所有人都会爬虫。
一共173个行业分析报告,你不用一个一个去找!都已经整理好了!!!
记得点赞收藏,刷过了可就找不着了😎
先给大家挑出其中一些具体介绍下:先上目录~
一、宏观统计数据平台
1. 国家统计数据库
2. 前瞻数据库
3. 中国互联网信息中心(CNNIC)
4. 果汁排行
二、各行业研究分析报告
1. 易观千帆
2. 阿里数据
3. 艾瑞咨询
4. Quest Mobile
5. 极光大数据
6. 美团研究院
一、宏观统计数据平台
1.国家统计数据库
国家统计数据库提供许多数据,如GDP、CPI、PPI、PMI、社会消费品零售总额等数据指标的内容都非常全面,只要在检索页面上输入关键词就可以获得历年来的国家数据。
国家统计局有一个最好用的功能是可以查找到国民经济和社会发展统计公报,还有专业的政策解读等等!我平常查询一些贸易数据、工业生产等数据,首先就是用这一网站,精确而且权威!
2.前瞻数据库
前瞻数据库主要包括中国及各省市宏观经济数据、全球主要国家宏观经济数据、行业经济数据(含产品价格、产量、销量数据)及进出口数据等,数据来源包括国家统计局、海关总署等官方渠道。
3.中国互联网信息中心(CNNIC)
中国互联网络信息中心(CNNIC)负责国家网络基础资源的运行管理和服务,主要开展互联网发展研究并提供咨询服务。
你可以查询到热点新闻资讯、互联网发展状况统计报告、互联网大事记、全球互联网动态.....
这一网站绝对是我撰写行业报告的法宝!相信这些全面的互联网数据也可以在行业宏观描述上助你一臂之力。
二、各行业分析研究报告
1.易观千帆
国内权威的数字经济洞察平台,覆盖国内45领域、300+行业、超5万款APP,以移动端数据为主,专注于互联网行业发展报告。
移动互联网产品竞争分析,帮助APP/ 媒体、广告公司、投资公司、传统企业做好竞争分析、运营分析、投资分析等。
2.阿里数据
阿里官方出品,主要是淘宝平台的市场数据分析,适合想进入电子商务、物流等行业的人。
权威性的科技媒体,随时获取互联网新闻资讯,快速进入互联网行业的首选。
3.艾瑞咨询
知名咨询公司,行业报告全面,提供电子商务、移动互联网、网络营销等行业内容。
艾瑞咨询通过研究咨询等专业服务,助力企业提高对新经济产业的认知水平、盈利能力和综合竞争力。
4.Quest Mobile
全行业研究,行业数据报告量大,更新速度快。
细分行业观察,提供汽车行业、互联网行业、内容生态搜索趋势研究等报告。报告查询神器,同段、同句关键词搜索功能亲测有效,节省案例查找时间。
5.极光大数据
细分行业观察,提供汽车行业、互联网行业、内容生态搜索趋势研究等报告。
6.美团研究院
研究报告分为产业经济、城市经济、服务消费、数字经济、新就业形态五个板块,宏观数据手到擒来
当前共计173个免费网站,包含国家政府及各行各业,如互联网、地产、汽车、交通、传媒、金融、消费零售等,后面还会陆续更新......可以在手机端直接通过搜索关键词,进行报告网站的查询。
也可以通过所属类型直接进行报告的筛选,简直超级无敌方便~
记得关注@伙伴云哦~
主页分享更多干货~
有什么经验可以和dota小白分享吗?
DOTA是一款经久不衰的好游戏。从DOTA1的风靡全球,到DOTA2的全新问世,DOTA见证了两个少年的成长与梦想。无论是DOTA1还是DOTA2,从历年的更新日志我们可以清晰地看到暴雪为DOTA这款游戏所倾注的心血。总的来说,DOTA是一款具有时代意义的游戏产品,值得我们去体验,也值得我们去弘扬游戏所展示的团队精神。
DOTA是一款上手起点比较高的游戏。本人90后,11年开始接触DOTA现在仍然是水货玩家,刚开始玩DOTA这款游戏,就是和同学组队与“简单电脑”对打,然后一步一步升级到“复杂电脑”,经过近两个月的练习才敢上11对战平台实战演练。不得不说,DOTA这款游戏对新手来说非常难,除非你很有游戏的天赋,想要玩好这款游戏,还是需要付出不少功夫的。DOTA里面英雄众多,每个英雄都有自己特色的战斗技能和成长属性,在阵容中的定位和所发挥的作用都不一样。首先,你得根据对方和己方阵容选择合适的英雄,有的需要配合、有的需要克制、有的是全球流的打法、有的是推塔流的打法、有的是4保1。即使是一个英雄根据出装和技能加点的不同,也有很多种玩法和定位,比如撼地神牛,有辅助控制玩法(秘法、跳刀、蓝杖等)、有暴力输出玩法(一刀牛)、有后期玩法(跳刀、大炮、龙心等)。英雄很多,就不在一一列举了,需要题主在平时的游戏中去自己摸索总结。
DOTA是一款游戏技巧很多的游戏。DOTA特有的英雄模型、技能释放、技能动作、技能释放形态、地形、地图阴影、树林等因素,导致DOTA这款游戏小技巧、小细节很多。比如如何预判玩家走位;如何利用地形、事业盲区gank或逃生;如何走位或利用道具躲避技能等等等等,都不是一蹴而就的,都需要在自己的操作中不断摸索,也可以去众多直播平台学习大神们的犀利操作。
DOTA是一款注重团队协作的游戏。每一局游戏都是由5个玩家组成,双方推倒对方水晶方为胜利。这就需要5个游戏玩家朝着一个目标共同奋斗。由于这款游戏起点比较高,现在DOTA的游戏环境还是比较差的。在游戏中因为某些失误对骂互喷的现象几乎每天都在发生;在游戏中因为某些玩家的技术不熟练被喷的现象几乎每天都在发生;在游戏中因为其他原因总是被坑的现象几乎每天都在发生。这就需要我们一定要用平常心来玩这款游戏,切记,这是一款团队游戏。如果有条件的话,建议最好拉上自己的同学、朋友一起组成固定的团队,这样游戏会更有趣味。
每一款上市的游戏一定是倾注开发者心血的产品,需要玩家在体验的过程中慢慢摸索,体会其中的趣味性。DOTA就是这样一款游戏,只有不断地摸索,不断地练习才会有所长进,才能真正享受到游戏的乐趣。
如何提高Python代码的性能?
Python Files 博客发布了几篇主题为「Hunting Performance in Python Code」的系列文章,对提升 Python 代码的性能的方法进行了介绍。在其中的每一篇文章中,作者都会介绍几种可用于 Python 代码的工具和分析器,以及它们可以如何帮助你更好地在前端(Python 脚本)和/或后端(Python 解释器)中找到瓶颈。代码地址:https://github.com/apatrascu/hunting-python-performance
目录
一、环境设置
二、内存分析
三、CPU 分析——Python 脚本
四、CPU 分析——Python 解释器(略)
一、环境设置
设置
在深入到基准测试和性能分析之前,首先我们需要一个合适的环境。这意味着我们需要为这项任务配置我们的机器和操作系统。
我的机器的规格如下:
处理器:Intel(R) Xeon(R) CPU E5-2699 v3 @ 2.30GHz
内存:32GB
操作系统:Ubuntu 16.04 LTS
Kernel:4.4.0-75-generic
我们的目标是得到可复现的结果,因此要确保我们的数据不会受到其它后台进程、操作系统配置或任何其它硬件性能提升技术的影响。
让我们首先从配置用于性能分析的机器开始。
硬件功能
首先,禁用所有硬件性能功能,也就是说要禁用 Intel Turbo Boost 和 Hyper Threading from BIOS/UEFI。
正如其官方网页上说的那样,Turbo Boost 是「一种在处理器内核运行,并可以在低于功耗、电流和温度规格限制的情况下允许它们以高于额定频率的速度运行的技术。」此外,Hyper Threading 是「一种可以更高效地利用处理器资源的技术,能使每个内核都能多线程运行。」
这都是值得我们花钱购买的好东西。那为什么要在性能分析/基准测试中禁用它们呢?因为使用这些技术会让我们无法得到可靠的和可复现的结果。这会让运行过程发生变化。让我们看个小例子 primes.py,代码故意写得很糟糕。
import time
import statistics
def primes(n):
if n==2:
return [2]
elif n<2:
return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
def benchmark():
results = []
gstart = time.time()
for _ in xrange(5):
start = time.time()
count = len(primes(1000000))
end = time.time()
results.append(end-start)
gend = time.time()
mean = statistics.mean(results)
stdev = statistics.stdev(results)
perc = (stdev * 100)/ mean
print "Benchmark duration: %r seconds" % (gend-gstart)
print "Mean duration: %r seconds" % mean
print "Standard deviation: %r (%r %%)" % (stdev, perc)
benchmark()
这段代码可在 GitHub 上查看:https://github.com/apatrascu/hunting-python-performance/blob/master/01.primes.py。你需要运行以下命令安装一个依赖包:
pip install statistics
让我们在一个启用了 Turbo Boost 和 Hyper Threading 的系统中运行它:
python primes.py
Benchmark duration: 1.0644240379333496 seconds
Mean duration: 0.2128755569458008 seconds
Standard deviation: 0.032928838418120374 (15.468585914964498 %)
现在禁用该系统的睿频加速(Turbo Boost)和超线程(Hyper Threading),然后再次运行这段代码:
python primes.py
Benchmark duration: 1.2374498844146729 seconds
Mean duration: 0.12374367713928222 seconds
Standard deviation: 0.000684464852339824 (0.553131172568 %)
看看第一个案例的标准差为 15%。这是一个很大的值!假设我们的优化只能带来 6% 的加速,那我们怎么能将运行过程中的变化(run to run variation)和你的实现的差异区分开?
相对而言,在第二个例子中,标准差减少到了大约 0.6%,我们的新优化方案效果清晰可见。
CPU 节能
禁用所有的 CPU 节能设置,并使用固定的 CPU 频率。这可以通过在 Linux 功率调节器(power governor)中将 intel_pstate 改成 acpi_cpufreq 而实现。
intel_pstate 驱动使用英特尔内核(Sandy Bridge 或更新)处理器的内部调节器实现了一个缩放驱动。acpi_cpufreq 使用了 ACPI Processor Performance States。
下面让我们先来检查一下:
$ cpupower frequency-info
analyzing CPU 0:
driver: intel_pstate
CPUs which run at the same hardware frequency: 0
CPUs which need to have their frequency coordinated by software: 0
maximum transition latency: 0.97 ms.
hardware limits: 1.20 GHz - 3.60 GHz
available cpufreq governors: performance, powersave
current policy: frequency should be within 1.20 GHz and 3.60 GHz.
The governor "powersave" may decide which speed to use
within this range.
current CPU frequency is 1.20 GHz.
boost state support:
Supported: yes
Active: yes
可以看到这里所使用的调节器被设置成了节能模式,而 CPU 的频率范围在 1.20 GHz 到 3.60 GHz 之间。这个设置对日常应用来说是很不错的,但却会影响到基准测试的结果。
那么应该给调节器设置什么值呢?如果我们浏览一下文档,我们可以看到我们可以使用以下设置:
高性能(performance):以最大频率运行 CPU
节能(powersave):以最小频率运行 CPU
自定义(userspace):按用户指定的频率运行 CPU
按需(ondemand):根据当前负载动态调节频率。可能跳至最高频率,空闲时又会降低
保守(conservative):根据当前负载动态调节频率。相比于按需模式,其频率调节更加渐进
我们要使用性能调节器(performance governor),并将频率设置成 CPU 支持的最大频率。如下所示:
$ cpupower frequency-info
analyzing CPU 0:
driver: acpi-cpufreq
CPUs which run at the same hardware frequency: 0
CPUs which need to have their frequency coordinated by software: 0
maximum transition latency: 10.0 us.
hardware limits: 1.20 GHz - 2.30 GHz
available frequency steps: 2.30 GHz, 2.20 GHz, 2.10 GHz, 2.00 GHz, 1.90 GHz, 1.80 GHz, 1.70 GHz, 1.60 GHz, 1.50 GHz, 1.40 GHz, 1.30 GHz, 1.20 GHz
available cpufreq governors: conservative, ondemand, userspace, powersave, performance
current policy: frequency should be within 2.30 GHz and 2.30 GHz.
The governor "performance" may decide which speed to use
within this range.
current CPU frequency is 2.30 GHz.
cpufreq stats: 2.30 GHz:100.00%, 2.20 GHz:0.00%, 2.10 GHz:0.00%, 2.00 GHz:0.00%, 1.90 GHz:0.00%, 1.80 GHz:0.00%, 1.70 GHz:0.00%, 1.60 GHz:0.00%, 1.50 GHz:0.00%, 1.40 GHz:0.00%, 1.30 GHz:0.00%, 1.20 GHz:0.00% (174)
boost state support:
Supported: no
Active: no
现在你已经使用性能调节器将频率设置成了固定的 2.3 GHz。这是最大的可设置的值,没有睿频加速(Turbo Boost),它可以被用在 Xeon E5-2699 v3 上。
要完成设置,请使用管理员权限运行以下命令:
cpupower frequency-set -g performance
cpupower frequency-set --min 2300000 --max 2300000
如果你没有 cpupower,可使用以下命令安装:
sudo apt-get install linux-tools-common linux-header-`uname -r` -y
功率调节器对 CPU 的工作方式有很大的影响。该调节器的默认设置是自动调节频率以减少功耗。我们不想要这样的设置,所以从 GRUB 中禁用它。只需要编辑 /boot/grub/grub.cfg(但是如果你在 kernel 升级上很小心,那么这将会消失)或在 /etc/grub.d/40_custom 中创建一个新的 kernel 入口。我们的 boot 行中必须包含这个 flag: intel_pstate=disable,如下所示:
linux /boot/vmlinuz-4.4.0-78-generic.efi.signed root=UUID=86097ec1-3fa4-4d00-97c7-3bf91787be83 ro intel_pstate=disable quiet splash $vt_handoff
ASLR(地址空间配置随机发生器)
这个设置是有争议的,参见 Victor Stinner 的博客:https://haypo.github.io/journey-to-stable-benchmark-average.html。当我首次建议在基准测试时禁用 ASLR 时,那是为了进一步提升对那时在 CPython 中存在的 Profile Guided Optimizations 的支持。
我为什么要说这个呢?因为在上面给出的特定硬件上,禁用 ASLR 可以将运行之间的标准差降低至 0.4%。
另一方面,根据在我的个人计算机(Intel Core i7 4710MQ)上的测试,禁用 ASLR 会导致 Victor 所提到的同样的问题。在更小的 CPU(比如 Intel Atom)上的测试会带来甚至更大的运行间标准差。
因为这似乎并不是普遍适用的真理,而且很大程度上依赖于硬件/软件配置,所以对于这个设置,我在启用后测量一次,再禁用后测量一次,之后再进行比较。
在我的机器上,我通过在 /etc/sysctl.conf. 中加入以下命令禁用了 ASLR。使用 sudo sysctl -p 进行应用。
kernel.randomize_va_space = 0
如果你想在运行时禁用它:
sudo bash -c 'echo 0 >| /proc/sys/kernel/randomize_va_space'
如果你想重新启用:
sudo bash -c 'echo 2 >| /proc/sys/kernel/randomize_va_space'
二、内存分析
在这一节,我将介绍一些有助于我们解决 Python 中(尤其是使用 PyPy 时)的内存消耗难题的工具。
我们为什么要关心这个问题?为什么我们不仅仅就关心性能?这些问题的答案相当复杂,但我会总结出来。
PyPy 是一个可选的 Python 解释器,其相对于 CPython 有一些巨大的优势:速度(通过其 Just in Time 编译器)、兼容性(几乎可以替代 CPython)和并发性(使用 stackless 和 greenlets)。
PyPy 的一个缺点是因为其 JIT 和垃圾一样的回收站实现,它通常会使用比 CPython 更多的内存。但是在某些案例中,其的内存消耗会比 CPython 少。
下面我们来看看你可以如何测量你的应用使用了多少内存。
诊断内存使用
memory_profiler
memory_profiler 是一个可用来测量解释器运行一个负载时的内存用量的库。你可以通过 pip 安装它:
pip install memory_profiler
另外还要安装 psutil 依赖包:
pip install psutil
这个工具的优点是它会在一个 Python 脚本中一行行地显示内存消耗。这可以让我们找到脚本中可以被我们重写的位置。但这种分析有一个缺点。你的代码的运行速度比一般脚本慢 10 到 20 倍。
怎么使用它?你只需要在你需要测量的函数上直接加上 @profile() 即可。
让我们看看实际怎么操作!我们将使用之前用过的素材脚本作为模型,但做了一点修改,移除了统计部分。代码也可在 GitHub 查看:https://github.com/apatrascu/hunting-python-performance/blob/master/02.primes-v1.py
from memory_profiler import profile
@profile(precision=6)
def primes(n):
if n == 2:
return [2]
elif n < 2:
return []
s = range(3, n + 1, 2)
mroot = n ** 0.5
half = (n + 1) / 2 - 1
i = 0
m = 3
while m <= mroot:
if s[i]:
j = (m * m - 3) / 2
s[j] = 0
while j < half:
s[j] = 0
j += m
i = i + 1
m = 2 * i + 3
return [2] + [x for x in s if x]
len(primes(100000))
开始测量时,使用以下 PyPy 命令:
pypy -m memory_profiler 02.primes-v3.py
或者直接在脚本中导入 memory_profiler:
pypy -m memory_profiler 02.primes-v3.py
在执行完这行代码之后,我们可以看到 PyPy 得到这样的结果:
Line # Mem usage Increment Line Contents
================================================
54 35.312500 MiB 0.000000 MiB @profile(precision=6)
55 def primes(n):
56 35.351562 MiB 0.039062 MiB if n == 2:
57 return [2]
58 35.355469 MiB 0.003906 MiB elif n < 2:
59 return []
60 35.355469 MiB 0.000000 MiB s = []
61 59.515625 MiB 24.160156 MiB for i in range(3, n+1):
62 59.515625 MiB 0.000000 MiB if i % 2 != 0:
63 59.515625 MiB 0.000000 MiB s.append(i)
64 59.546875 MiB 0.031250 MiB mroot = n ** 0.5
65 59.550781 MiB 0.003906 MiB half = (n + 1) / 2 - 1
66 59.550781 MiB 0.000000 MiB i = 0
67 59.550781 MiB 0.000000 MiB m = 3
68 59.554688 MiB 0.003906 MiB while m <= mroot:
69 59.554688 MiB 0.000000 MiB if s[i]:
70 59.554688 MiB 0.000000 MiB j = (m * m - 3) / 2
71 59.554688 MiB 0.000000 MiB s[j] = 0
72 59.554688 MiB 0.000000 MiB while j < half:
73 59.554688 MiB 0.000000 MiB s[j] = 0
74 59.554688 MiB 0.000000 MiB j += m
75 59.554688 MiB 0.000000 MiB i = i + 1
76 59.554688 MiB 0.000000 MiB m = 2 * i + 3
77 59.554688 MiB 0.000000 MiB l = [2]
78 59.679688 MiB 0.125000 MiB for x in s:
79 59.679688 MiB 0.000000 MiB if x:
80 59.679688 MiB 0.000000 MiB l.append(x)
81 59.683594 MiB 0.003906 MiB return l
我们可以看到这个脚本使用了 24.371094 MiB 的 RAM。让我们简单分析一下。我们看到其中大多数都用在了数值数组的构建中。它排除了偶数数值,保留了所有其它数值。
我们可以通过调用 range 函数而对其进行一点改进,其使用一个增量参数。在这个案例中,该脚本看起来像是这样:
from memory_profiler import profile
@profile(precision=6)
def primes(n):
if n == 2:
return [2]
elif n < 2:
return []
s = range(3, n + 1, 2)
mroot = n ** 0.5
half = (n + 1) / 2 - 1
i = 0
m = 3
while m <= mroot:
if s[i]:
j = (m * m - 3) / 2
s[j] = 0
while j < half:
s[j] = 0
j += m
i = i + 1
m = 2 * i + 3
l = [2]
for x in s:
if x:
l.append(x)
return l
len(primes(100000))
如果我们再次测量,我们可以得到以下结果:
Line # Mem usage Increment Line Contents
================================================
27 35.343750 MiB 0.000000 MiB @profile(precision=6)
28 def primes(n):
29 35.382812 MiB 0.039062 MiB if n == 2:
30 return [2]
31 35.382812 MiB 0.000000 MiB elif n < 2:
32 return []
33 35.386719 MiB 0.003906 MiB s = range(3, n + 1, 2)
34 35.417969 MiB 0.031250 MiB mroot = n ** 0.5
35 35.417969 MiB 0.000000 MiB half = (n + 1) / 2 - 1
36 35.417969 MiB 0.000000 MiB i = 0
37 35.421875 MiB 0.003906 MiB m = 3
38 58.019531 MiB 22.597656 MiB while m <= mroot:
39 58.019531 MiB 0.000000 MiB if s[i]:
40 58.019531 MiB 0.000000 MiB j = (m * m - 3) / 2
41 58.019531 MiB 0.000000 MiB s[j] = 0
42 58.019531 MiB 0.000000 MiB while j < half:
43 58.019531 MiB 0.000000 MiB s[j] = 0
44 58.019531 MiB 0.000000 MiB j += m
45 58.019531 MiB 0.000000 MiB i = i + 1
46 58.019531 MiB 0.000000 MiB m = 2 * i + 3
47 58.019531 MiB 0.000000 MiB l = [2]
48 58.089844 MiB 0.070312 MiB for x in s:
49 58.089844 MiB 0.000000 MiB if x:
50 58.089844 MiB 0.000000 MiB l.append(x)
51 58.093750 MiB 0.003906 MiB return l
很好,现在我们的内存消耗下降到了 22.75 MiB。使用列表解析(list comprehension),我们还可以将消耗再降低一点。
from memory_profiler import profile
@profile(precision=6)
def primes(n):
if n == 2:
return [2]
elif n < 2:
return []
s = range(3, n + 1, 2)
mroot = n ** 0.5
half = (n + 1) / 2 - 1
i = 0
m = 3
while m <= mroot:
if s[i]:
j = (m * m - 3) / 2
s[j] = 0
while j < half:
s[j] = 0
j += m
i = i + 1
m = 2 * i + 3
return [2] + [x for x in s if x]
len(primes(100000))
再次测量:
Line # Mem usage Increment Line Contents
================================================
4 35.425781 MiB 0.000000 MiB @profile(precision=6)
5 def primes(n):
6 35.464844 MiB 0.039062 MiB if n == 2:
7 return [2]
8 35.464844 MiB 0.000000 MiB elif n < 2:
9 return []
10 35.464844 MiB 0.000000 MiB s = range(3, n + 1, 2)
11 35.500000 MiB 0.035156 MiB mroot = n ** 0.5
12 35.500000 MiB 0.000000 MiB half = (n + 1) / 2 - 1
13 35.500000 MiB 0.000000 MiB i = 0
14 35.500000 MiB 0.000000 MiB m = 3
15 57.683594 MiB 22.183594 MiB while m <= mroot:
16 57.683594 MiB 0.000000 MiB if s[i]:
17 57.683594 MiB 0.000000 MiB j = (m * m - 3) / 2
18 57.683594 MiB 0.000000 MiB s[j] = 0
19 57.683594 MiB 0.000000 MiB while j < half:
20 57.683594 MiB 0.000000 MiB s[j] = 0
21 57.683594 MiB 0.000000 MiB j += m
22 57.683594 MiB 0.000000 MiB i = i + 1
23 57.683594 MiB 0.000000 MiB m = 2 * i + 3
24 57.847656 MiB 0.164062 MiB return [2] + [x for x in s if x]
我们最后的脚本仅消耗 22.421875 MiB。相比于第一个版本,差不多下降了 10%。
三、CPU 分析——Python 脚本
在这一节,我将介绍一些有助于我们解决 Python 中的分析 CPU 使用的难题的工具。
CPU 性能分析(profiling)的意思是通过分析 CPU 执行代码的方式来分析这些代码的性能。也就是说要找到我们代码中的热点(hot spot),然后看我们可以怎么处理它们。
接下来我们会看看你可以如何追踪你的 Python 脚本的 CPU 使用。我们将关注以下分析器(profiler):
* cProfile
* line_profiler
* pprofile
* vprof
测量 CPU 使用
import time
def primes(n):
if n == 2:
return [2]
elif n < 2:
return []
s = []
for i in range(3, n+1):
if i % 2 != 0:
s.append(i)
mroot = n ** 0.5
half = (n + 1) / 2 - 1
i = 0
m = 3
while m <= mroot:
if s[i]:
j = (m * m - 3) / 2
s[j] = 0
while j < half:
s[j] = 0
j += m
i = i + 1
m = 2 * i + 3
l = [2]
for x in s:
if x:
l.append(x)
return l
def benchmark():
start = time.time()
for _ in xrange(40):
count = len(primes(1000000))
end = time.time()
print "Benchmark duration: %r seconds" % (end-start)
benchmark()
这一节我将使用与前一节基本一样的脚本,你也可以在 GitHub 上查看:https://gist.github.com/apatrascu/8524679175de08a54a95e22001a31d3b
另外,记住在 PyPy2 上,你需要使用一个支持它的 pip 版本:
pypy -m ensure pip
其它东西可以通过以下指令安装:
pypy -m pip install
cProfile
在 CPU 性能分析上最常用的一个工具是 cProfile,主要是因为它内置于 CPython2 和 PyPy2 中。这是一个确定性的分析器,也就是说它会在运行我们的负载时收集一系列统计数据,比如代码各个部分的执行次数或执行时间。此外,相比于其它内置的分析器(profile 或 hotshot),cProfile 对系统的开销更少。
当使用 CPython2 时,其使用方法是相当简单的:
python -m cProfile 03.primes-v1.py
如果你使用的是 PyPy2:
pypy -m cProfile 03.primes-v1.py
其输出如下:
Benchmark duration: 30.11158514022827 seconds
23139965 function calls in 30.112 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 30.112 30.112 03.primes.py:1(<module>)
40 19.760 0.494 29.896 0.747 03.primes.py:3(primes)
1 0.216 0.216 30.112 30.112 03.primes.py:31(benchmark)
40 0.000 0.000 0.000 0.000 {len}
23139840 6.683 0.000 6.683 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
40 3.453 0.086 3.453 0.086 {range}
2 0.000 0.000 0.000 0.000 {time.time}
即使是这样的文本输出,我们也可以直接看到我们脚本的大多数时间都在调用 list.append 方法。
如果我们使用 gprof2dot,我们可以用图形化的方式来查看 cProfile 的输出。要使用这个工具,我们首先必须安装 graphviz。在 Ubuntu 上,可以使用以下命令:
apt-get install graphviz
pip install gprof2dot
再次运行我们的脚本:
python -m cProfile -o output.pstats 03.primes-v1.py
gprof2dot -f pstats output.pstats | dot -Tpng -o output.png
然后我们会得到下面的 output.png 文件:
这样看起来就轻松多了。让我们仔细看看它输出了什么。你可以看到来自你的脚本的一个函数调用图(callgraph)。在每个方框中,你可以一行一行地看到:
第一行:Python 文件名、行数和方法名
第二行:这个方框所用的时间占全局时间的比例
第三行:括号中是该方法本身所用时间占全局时间的比例
第四行:调用次数
比如说,在从上到下第三个红色框中,方法 primes 占用了 98.28% 的时间,65.44% 的时间是在该方法之中做什么事情,它被调用了 40 次。剩下的时间被用在了 Python 的 list.append(22.33%)和 range(11.51%)方法中。
这是一个简单的脚本,所以我们只需要重写我们的脚本,让它不用使用那么多 append,结果如下:
import time
def primes(n):
if n==2:
return [2]
elif n<2:
return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
def benchmark():
start = time.time()
for _ in xrange(40):
count = len(primes(1000000))
end = time.time()
print "Benchmark duration: %r seconds" % (end-start)
benchmark()
如果我们在此之前和之后使用 CPython2 测量脚本的时间:
python 03.primes-v1.py
Benchmark duration: 15.768115043640137 seconds
python 03.primes-v2.py
Benchmark duration: 6.56312108039856 seconds
用 PyPy2 测量:
pypy 03.primes-v1.py
Benchmark duration: 1.4009230136871338 seconds
pypy 03.primes-v2.py
Benchmark duration: 0.4542720317840576 seconds
我们在 CPython2 上得到了 2.4 倍的提升,在 PyPy2 上得到了 3.1 倍的提升。很不错,其 cProfile 调用图为:
你也可以以程序的方式查看 cProfile:
import cProfile
pr = cProfile.Profile()
pr.enable()
function_to_measure()
pr.disable()
pr.print_stats(sort='time')
这在一些场景中很有用,比如多进程性能测量。更多详情请参阅:https://docs.python.org/2/library/profile.html#module-cProfile
line_profiler
这个分析器可以提供逐行水平的负载信息。这是通过 C 语言用 Cython 实现的,与 cProfile 相比计算开销更少。
其源代码可在 GitHub 上获取:https://github.com/rkern/line_profiler,PyPI 页面为:https://pypi.python.org/pypi/line_profiler/。和 cProfile 相比,它有相当大的开销,需要多 12 倍的时间才能得到一个分析结果。
要使用这个工具,你首先需要通过 pip 添加:pip install pip install Cython ipython==5.4.1 line_profiler(CPython2)。这个分析器的一个主要缺点是不支持 PyPy。
就像在使用 memory_profiler 时一样,你需要在你想分析的函数上加上一个装饰。在我们的例子中,你需要在 03.primes-v1.py 中的 primes 函数的定义前加上 @profile。然后像这样调用:
kernprof -l 03.primes-v1.py
python -m line_profiler 03.primes-v1.py.lprof
你会得到一个这样的输出:
Timer unit: 1e-06 s
Total time: 181.595 s
File: 03.primes-v1.py
Function: primes at line 3
Line # Hits Time Per Hit % Time Line Contents
==============================================================
3 @profile
4 def primes(n):
5 40 107 2.7 0.0 if n == 2:
6 return [2]
7 40 49 1.2 0.0 elif n < 2:
8 return []
9 40 44 1.1 0.0 s = []
10 39999960 34410114 0.9 18.9 for i in range(3, n+1):
11 39999920 29570173 0.7 16.3 if i % 2 != 0:
12 19999960 14976433 0.7 8.2 s.append(i)
13 40 329 8.2 0.0 mroot = n ** 0.5
14 40 82 2.0 0.0 half = (n + 1) / 2 - 1
15 40 46 1.1 0.0 i = 0
16 40 30 0.8 0.0 m = 3
17 20000 17305 0.9 0.0 while m <= mroot:
18 19960 16418 0.8 0.0 if s[i]:
19 6680 6798 1.0 0.0 j = (m * m - 3) / 2
20 6680 6646 1.0 0.0 s[j] = 0
21 32449400 22509523 0.7 12.4 while j < half:
22 32442720 26671867 0.8 14.7 s[j] = 0
23 32442720 22913591 0.7 12.6 j += m
24 19960 15078 0.8 0.0 i = i + 1
25 19960 16170 0.8 0.0 m = 2 * i + 3
26 40 87 2.2 0.0 l = [2]
27 20000000 14292643 0.7 7.9 for x in s:
28 19999960 13753547 0.7 7.6 if x:
29 3139880 2417421 0.8 1.3 l.append(x)
30 40 33 0.8 0.0 return l
我们可以看到两个循环在反复调用 list.append,占用了脚本的大部分时间。
pprofile
地址:http://github.com/vpelletier/pprofile
据作者介绍,pprofile 是一个「行粒度的、可感知线程的确定性和统计性纯 Python 分析器」。
它的灵感来源于 line_profiler,修复了大量缺陷,但因为其完全是用 Python 写的,所以也可以通过 PyPy 使用。和 cProfile 相比,使用 CPython 时分析的时间会多 28 倍,使用 PyPy 时的分析时间会长 10 倍,但具有粒度更大的细节水平。
而且还支持 PyPy 了!除此之外,它还支持线程分析,这在很多情况下都很有用。
要使用这个工具,你首先需要通过 pip 安装:pip install pprofile(CPython2)/ pypy -m pip install pprofile(PyPy),然后像这样调用:
pprofile 03.primes-v1.py
其输出和前面工具的输出不同,如下:
Benchmark duration: 886.8774709701538 seconds
Command line: ['03.primes-v1.py']
Total duration: 886.878s
File: 03.primes-v1.py
File duration: 886.878s (100.00%)
Line #| Hits| Time| Time per hit| %|Source code
------+----------+-------------+-------------+-------+-----------
1| 2| 7.10487e-05| 3.55244e-05| 0.00%|import time
2| 0| 0| 0| 0.00%|
3| 0| 0| 0| 0.00%|
4| 41| 0.00029397| 7.17e-06| 0.00%|def primes(n):
5| 40| 0.000231266| 5.78165e-06| 0.00%| if n == 2:
6| 0| 0| 0| 0.00%| return [2]
7| 40| 0.000178337| 4.45843e-06| 0.00%| elif n < 2:
8| 0| 0| 0| 0.00%| return []
9| 40| 0.000188112| 4.70281e-06| 0.00%| s = []
10| 39999960| 159.268| 3.98171e-06| 17.96%| for i in range(3, n+1):
11| 39999920| 152.924| 3.82312e-06| 17.24%| if i % 2 != 0:
12| 19999960| 76.2135| 3.81068e-06| 8.59%| s.append(i)
13| 40| 0.00147367| 3.68416e-05| 0.00%| mroot = n ** 0.5
14| 40| 0.000319004| 7.9751e-06| 0.00%| half = (n + 1) / 2 - 1
15| 40| 0.000220776| 5.51939e-06| 0.00%| i = 0
16| 40| 0.000243902| 6.09756e-06| 0.00%| m = 3
17| 20000| 0.0777466| 3.88733e-06| 0.01%| while m <= mroot:
18| 19960| 0.0774016| 3.87784e-06| 0.01%| if s[i]:
19| 6680| 0.0278566| 4.17015e-06| 0.00%| j = (m * m - 3) / 2
20| 6680| 0.0275929| 4.13067e-06| 0.00%| s[j] = 0
21| 32449400| 114.858| 3.5396e-06| 12.95%| while j < half:
22| 32442720| 120.841| 3.72475e-06| 13.63%| s[j] = 0
23| 32442720| 114.432| 3.5272e-06| 12.90%| j += m
24| 19960| 0.0749919| 3.75711e-06| 0.01%| i = i + 1
25| 19960| 0.0765574| 3.83554e-06| 0.01%| m = 2 * i + 3
26| 40| 0.000222206| 5.55515e-06| 0.00%| l = [2]
27| 20000000| 68.8031| 3.44016e-06| 7.76%| for x in s:
28| 19999960| 67.9391| 3.39696e-06| 7.66%| if x:
29| 3139880| 10.9989| 3.50295e-06| 1.24%| l.append(x)
30| 40| 0.000155687| 3.89218e-06| 0.00%| return l
31| 0| 0| 0| 0.00%|
32| 0| 0| 0| 0.00%|
33| 2| 8.10623e-06| 4.05312e-06| 0.00%|def benchmark():
34| 1| 5.00679e-06| 5.00679e-06| 0.00%| start = time.time()
35| 41| 0.00101089| 2.4656e-05| 0.00%| for _ in xrange(40):
36| 40| 0.232263| 0.00580657| 0.03%| count = len(primes(1000000))
(call)| 40| 886.644| 22.1661| 99.97%|# 03.primes-v1.py:4 primes
37| 1| 5.96046e-06| 5.96046e-06| 0.00%| end = time.time()
38| 1| 0.000678062| 0.000678062| 0.00%| print "Benchmark duration: %r seconds" % (end-start)
39| 0| 0| 0| 0.00%|
40| 0| 0| 0| 0.00%|
41| 1| 5.79357e-05| 5.79357e-05| 0.00%|benchmark()
(call)| 1| 886.878| 886.878|100.00%|# 03.primes-v1.py:33 benchmark
我们现在可以看到更详细的细节。让我们稍微研究一下这个输出。这是这个脚本的整个输出,每一行你可以看到调用的次数、运行它所用的时间(秒)、每次调用的时间和占全局时间的比例。此外,pprofile 还为我们的输出增加了额外的行(比如 44 和 50 行,行前面写着 (call)),这是累积指标。
同样,我们可以看到有两个循环在反复调用 list.append,占用了脚本的大部分时间。
vprof
地址:https://github.com/nvdv/vprof
vprof 是一个 Python 分析器,为各种 Python 程序特点提供了丰富的交互式可视化,比如运行时间和内存使用。这是一个图形化工具,基于 Node.JS,可在网页上展示结果。
使用这个工具,你可以针对相关 Python 脚本查看下面的一项或多项内容:
CPU flame graph
代码分析(code profiling)
内存图(memory graph)
代码热图(code heatmap)
要使用这个工具,你首先需要通过 pip 安装:pip install vprof(CPython2)/ pypy -m pip install vprof(PyPy)。
在 CPython2 上,要显示代码热图(下面的第一行调用)和代码分析(下面的第二行调用):
vprof -c h 03.primes-v1.py
vprof -c p 03.primes-v1.py
在 PyPy 上,要显示代码热图(下面的第一行调用)和代码分析(下面的第二行调用):
pypy -m vprof -c h 03.primes-v1.py
pypy -m vprof -c p 03.primes-v1.py
在上面的两个例子中,你都会看到如下的代码热图:
以及如下的代码分析:
结果是以图形化的方式展示的,你可以将鼠标悬浮或点击每一行,从而查看更多信息。
同样,我们可以看到有两个循环在反复调用 list.append,占用了脚本的大部分时间。
如何计算变量之间的相关性?
本文介绍了几个重要的变量相关性的度量,包括皮尔逊相关系数、距离相关性和最大信息系数等,并用简单的代码和示例数据展示了这些度量的适用性对比。
从信号的角度来看,这个世界是一个嘈杂的地方。为了弄清楚所有的事情,我们必须有选择地集中注意力到有用的信息上。
通过数百万年的自然选择过程,我们人类已经变得非常擅长过滤背景信号。我们学会将特定的信号与特定的事件联系起来。
例如,假设你正在繁忙的办公室中打乒乓球。
为了回击对手的击球,你需要进行大量复杂的计算和判断,将多个相互竞争的感官信号考虑进去。
为了预测球的运动,你的大脑必须重复采样球的位置并估计它未来的轨迹。更厉害的球员还会将对手击球时施加的旋转考虑进去。
最后,为了击球,你需要考虑对手的位置、自己的位置、球的速度,以及你打算施加的旋转。
所有这些都涉及到了大量的潜意识微分学。一般来说,我们理所当然的认为,我们的神经系统可以自动做到这些(至少经过一些练习之后)。
同样令人印象深刻的是,人类大脑是如何区别对待它所接收到的无数竞争信号的重要性的。例如,球的位置被认为比你身后发生的对话或你面前打开的门更重要。
这听起来似乎不值得一提,但实际上这证明了可以多大程度上学习从噪声数据中做出准确预测。
当然,一个被给予连续的视听数据流的空白状态机将会面临一个困难的任务,即确定哪些信号能够最好地预测最佳行动方案。
幸运的是,有统计和计算方法可以用来识别带噪声和复杂的数据中的模式。
相关性
一般来说,当我们谈到两个变量之间的「相关性(correlation)」时,在某种意义上,我们是指它们的「关系(relatedness)」。
相关变量是包含彼此信息的变量。两个变量的相关性越强,其中一个变量告诉我们的关于另一个变量的信息就越多。
你可能之前就看过:正相关、零相关、负相关
你可能已经对相关性、它的作用和它的局限性有了一定了解。事实上,这是一个数据科学的老生常谈:
「相关性不意味着因果关系」
这当然是正确的——有充分的理由说明,即使是两个变量之间有强相关性也不保证存在因果关系。观察到的相关性可能是由于隐藏的第三个变量的影响,或者完全是偶然的。
也就是说,相关性确实允许基于另一个变量来预测一个变量。有几种方法可以用来估计线性和非线性数据的相关性。我们来看看它们是如何工作的。
我们将用 Python 和 R 来进行数学和代码实现。本文示例的代码可以在这里 (https://gist.github.com/anonymous/fabecccf33f9c3feb568384f626a2c07) 找到:
GitHub 地址:https://gist.github.com/anonymous/fabecccf33f9c3feb568384f626a2c07
皮尔逊相关系数
皮尔逊相关系数是一种广泛使用的线性相关性的度量,它通常是很多初级统计课程的第一课。从数学角度讲,它被定义为「两个向量之间的协方差,通过它们标准差的乘积来归一化」。
两个成对的向量之间的协方差是它们在均值上下波动趋势的一种度量。也就是说,衡量一对向量是否倾向于在各自平均值的同侧或相反。
让我们看看在 Python 中的实现:
协方差的计算方法是从每一对变量中减去各自的均值。然后,将这两个值相乘。
如果都高于(或都低于)均值,那么结果将是一个正数,因为正数 × 正数 = 正数;同样的,负数 × 负数 = 负数。
如果在均值的不同侧,那么结果将是一个负数(因为正数 × 负数 = 负数)。
一旦我们为每一对变量都计算出这些值,将它们加在一起,并除以 n-1,其中 n 是样本大小。这就是样本协方差。
如果这些变量都倾向于分布在各自均值的同一侧,协方差将是一个正数;反之,协方差将是一个负数。这种倾向越强,协方差的绝对值就越大。
如果不存在整体模式,那么协方差将会接近于零。这是因为正值和负值会相互抵消。
最初,协方差似乎是两个变量之间「关系」的充分度量。但是,请看下面的图:
协方差 = 0.00003
看起来变量之间有很强的关系,对吧?那为什么协方差这么小呢(大约是 0.00003)?
这里的关键是要认识到协方差是依赖于比例的。看一下 x 和 y 坐标轴——几乎所有的数据点都落在了 0.015 和 0.04 之间。协方差也将接近于零,因为它是通过从每个个体观察值中减去平均值来计算的。
为了获得更有意义的数字,归一化协方差是非常重要的。方法是将其除以两个向量标准差的乘积。
在希腊字母中ρ经常用来表示皮尔逊相关系数
在 Python 中:
这样做的原因是因为向量的标准差是是其方差的平方根。这意味着如果两个向量是相同的,那么将它们的标准差相乘就等于它们的方差。
有趣的是,两个相同向量的协方差也等于它们的方差。
因此,两个向量之间协方差的最大值等于它们标准差的乘积(当向量完全相关时会出现这种情况)。这将相关系数限制在 -1 到 +1 之间。
箭头指向哪个方向?
顺便说一下,一个定义两个向量的 PCC 的更酷的方法来自线性代数。
首先,我们通过从向量各自的值中减去其均值的方法来「集中」向量。
a = [1,2,3,4,5] ; b = [5,4,3,2,1]
a_centered = [i - mean(a) for i in a]
b_centered = [j - mean(b) for j in b]
现在,我们可以利用向量可以看做指向特定方向的「箭头」的事实。
例如,在 2-D 空间中,向量 [1,3] 可以代表一个沿 x 轴 1 个单位,沿 y 轴 3 个单位的箭头。同样,向量 [2,1] 可以代表一个沿 x 轴 2 个单位,沿 y 轴 1 个单位的箭头。
两个向量 (1,3) 和 (2,1) 如箭头所示。
类似的,我们可以将数据向量表示为 n 维空间中的箭头(尽管当 n > 3 时不能尝试可视化)。
这些箭头之间的角度 ϴ 可以使用两个向量的点积来计算。定义为:
或者,在 Python 中:
点积也可以被定义为:
其中 ||**x**|| 是向量 **x **的大小(或「长度」)(参考勾股定理),ϴ 是箭头向量之间的角度。
正如一个 Python 函数:
def magnitude(x):
x_sq = [i ** 2 for i in x]
return math.sqrt(sum(x_sq))
我们通过将点积除以两个向量大小的乘积的方法得到 cos(ϴ)。
def cosTheta(x,y):
mag_x = magnitude(x)
mag_y = magnitude(y)
return dotProduct(x,y) / (mag_x * mag_y)
现在,如果你对三角学有一定了解,你可能会记得,余弦函数产生一个在 +1 和 -1 之间震荡的图形。
cos(ϴ) 的值将根据两个箭头向量之间的角度而发生变化。
当角度为零时(即两个向量指向完全相同的方向),cos(ϴ) 等于 1。
当角度为 -180°时(两个向量指向完全相反的方向),cos(ϴ) 等于 -1。
当角度为 90°时(两个向量指向完全不相关的方向),cos(ϴ) 等于零。
这可能看起来很熟悉——一个介于 +1 和 -1 之间的衡量标准似乎描述了两个向量之间的关系?那不是 Pearson』s *r *吗?
那么——这正是它的解释!通过将数据视为高维空间中的箭头向量,我们可以用它们之间的角度 ϴ 作为相似度的衡量。
A) 正相关向量; B) 负相关向量; C) 不相关向量
该角度 ϴ 的余弦在数学上与皮尔逊相关系数相等。当被视为高维箭头时,正相关向量将指向一个相似的方向。负相关向量将指向相反的方向。而不相关向量将指向直角。
就我个人而言,我认为这是一个理解相关性的非常直观的方法。
统计显著性?
正如频率统计一样,重要的是询问从给定样本计算的检验统计量实际上有多重要。Pearson』s r* *也不例外。
不幸的是,PCC 估计的置信区间不是完全直接的。
这是因为 Pearson』s r 被限制在 -1 和 +1 之间,因此不是正态分布的。而估计 PCC,例如 +0.95 之上只有很少的容错空间,但在其之下有大量的容错空间。
幸运的是,有一个解决方案——用一个被称为 Fisher 的 Z 变换的技巧:
1. 像平常一样计算 Pearson』s r 的估计值。
2. 用 Fisher 的 Z 变换将 r→z,用公式 z = arctanh(r) 完成。
3. 现在计算 z 的标准差。幸运的是,这很容易计算,由 SDz = 1/sqrt(n-3) 给出,其中 n 是样本大小。
4. 选择显著性阈值,alpha,并检查与此对应的平均值有多少标准差。如果取 alpha = 0.95,用 1.96。
5. 通过计算 *z* +(1.96 × SD*z*) 找到上限,通过计算 *z -* **(1.96 × SD*z*) 找到下限。
6. 用 r = tanh(z) 将这些转换回 r。
7. 如果上限和下限都在零的同一侧,则有统计显著性!
这里是在 Python 中的实现:
r = Pearsons(x,y)
z = math.atanh(r)
SD_z = 1 / math.sqrt(len(x) - 3)
z_upper = z + 1.96 * SD_z
z_lower = z - 1.96 * SD_z
r_upper = math.tanh(z_upper)
r_lower = math.tanh(z_lower)
当然,当给定一个包含许多潜在相关变量的大数据集时,检查每对的相关性可能很吸引人。这通常被称为「数据疏浚」——在数据集中查找变量之间的任何明显关系。
如果确实采用这种多重比较方法,则应该用适当的更严格的显著性阈值来降低发现错误相关性的风险(即找到纯粹偶然相关的无关变量)。
一种方法是使用 Bonferroni correction。
小结
到现在为止还好。我们已经看到 Pearson』s *r* 如何用来计算两个变量之间的相关系数,以及如何评估结果的统计显著性。给定一组未知的数据,用于开始挖掘变量之间的重要关系是很有可能的。
但是,有一个重要的陷阱——Pearson』s r 只适用于线性数据。
看下面的图。它们清楚地展示了一种看似非随机的关系,但是 Pearson』s *r *非常接近于零。
原因是因为这些图中的变量具有非线性关系。
我们通常可以将两个变量之间的关系描绘成一个点云,分散在一条线的两侧。点云的分散度越大,数据越「嘈杂」,关系越弱。
然而,由于它将每个单独的数据点与整体平均值进行比较,所以 Pearson』s r 只考虑直线。这意味着检测非线性关系并不是很好。
在上面的图中,Pearson』s r 并没有显示研究对象的相关性。
然而,这些变量之间的关系很显然是非随机的。幸运的是,我们有不同的相关性方法。
让我们来看看其中几个。
距离相关性
距离相关性与 Pearson』s *r *有一些相似之处,但是实际上是用一个相当不同的协方差概念来计算的。该方法通过用「距离」类似物替代常用的协方差和标准差(如上所定义)的概念。
类似 Pearson』s r,「距离相关性」被定义为「距离协方差」,由「距离标准差」来归一化。
距离相关性不是根据它们与各自平均值的距离来估计两个变量如何共同变化,而是根据与其他点的距离来估计它们是如何共同变化的,从而能更好捕捉变量之间非线性依赖关系。
深入细节
出生于 1773 年的 Robert Brown 是一名苏格兰植物学家。当布朗在显微镜下研究植物花粉时,注意到液面上有随机运动的有机颗粒。
他没有想到,这一观察竟使他名垂千古——他成为了布朗运动的(重新)发现者。
他更不会知道,近一个世纪的时间后爱因斯坦才对这种现象做出了解释,从而证实了原子的存在。同年,爱因斯坦发表了关于狭义相对论的论文(E=MC²),并打开了量子理论的大门。
布朗运动是这样一个物理过程:由于与周围粒子的碰撞,微小粒子随机运动。
布朗运动背后的数学原理可以被推广为维纳过程(Weiner process),维纳过程在数学金融中最著名的模型 Black-Scholes 中也扮演着重要的角色。
有趣的是,Gabor Szekely 在 20 世纪中期的研究表明,布朗运动和维纳过程和一个非线性关联度量相关。
让我们来看看如何由长度为 N 的向量 x 和 y 计算这个量。
1. 首先,我们对每个向量构建 N×N 的距离矩阵。距离矩阵和地图中的道路距离表非常类似——每行、每列的交点显示了相应城市间的距离。在距离矩阵中,行 i 和列 j 的交点给出了向量的第 i 个元素和第 j 个元素之间的距离。
2. 第二,矩阵是「双中心」的。也就是说,对于每个元素,我们减去了它的行平均值和列平均值。然后,我们再加上整个矩阵的总平均值。
上述公式中,加「^」表示「双中心」,加「-」表示「平均值」。
3. 在两个双中心矩阵的基础上,将 X 中每个元素的均值乘以 Y 中相应元素的均值,则可计算出距离协方差的平方。
4. 现在,我们可以用类似的办法找到「距离方差」。请记住,若两个向量相同,其协方差与其方差相等。因此,距离方差可表示如下:
5. 最后,我们利用上述公式计算距离相关性。请记住,(距离)标准差与(距离)方差的平方根相等。
如果你更喜欢代码实现而非数学符号,那么请看下面的 R 语言实现:
任意两变量的距离相关性都在 0 和 1 之间。其中,0 代表两变量相互独立,而接近于 1 则表明变量间存在依赖关系。
如果你不想从头开始编写距离相关方法,你可以安装 R 语言的 energy 包(https://cran.r-project.org/web/packages/energy/index.html),设计此方案的研究者提供了本代码。在该程序包中,各类可用方案调用的是 C 语言编写的函数,因此有着很大的速度优势。
置信区间?
我们可以采取「重采样(resampling)」方法为距离相关性估计建立置信区间。一个简单的例子是 bootstrap 重采样。
这是一个巧妙的统计技巧,需要我们从原始数据集中随机抽样(替换)以「重建」数据。这个过程将重复多次(例如 1000 次),每次都计算感兴趣的统计量。
这将为我们感兴趣的统计量产生一系列不同的估计值。我们可以通过它们估计在给定置信水平下的上限和下限。
请看下面的 R 语言代码,它实现了简单的 bootstrap 函数:
如果你想建立统计显著性,还有另一个重采样技巧,名为「排列检验(permutation test)」。
排列检验与上述 bootstrap 方法略有不同。在排列检验中,我们保持一个向量不变,并通过重采样对另一个变量进行「洗牌」。这接近于零假设(null hypothesis)——即,在变量之间不存在依赖关系。
这个经「洗牌」打乱的变量将被用于计算它和常变量间的距离相关性。这个过程将被执行多次,然后,结果的分布将与实际距离相关性(从未被「洗牌」的数据中获得)相比较。
然后,大于或等于「实际」结果的经「洗牌」的结果的比例将被定为 P 值,并与给定的显著性阈值(如 0.05)进行比较。
以下是上述过程的代码实现:
最大信息系数
最大信息系数(MIC)于 2011 年提出,它是用于检测变量之间非线性相关性的最新方法。用于进行 MIC 计算的算法将信息论和概率的概念应用于连续型数据。
深入细节
由克劳德·香农于 20 世纪中叶开创的信息论是数学中一个引人注目的领域。
信息论中的一个关键概念是熵——这是一个衡量给定概率分布的不确定性的度量。概率分布描述了与特定事件相关的一系列给定结果的概率。
概率分布的熵是「每个可能结果的概率乘以其对数后的和」的负值。
为了理解其工作原理,让我们比较下面两个概率分布:
X 轴标明了可能的结果;Y 轴标明了它们各自的概率
左侧是一个常规六面骰子结果的概率分布;而右边的六面骰子不那么均匀。
从直觉上来说,你认为哪个的熵更高呢?哪个骰子结果的不确定性更大?让我们来计算它们的熵,看看答案是什么。
不出所料,常规骰子的熵更高。这是因为每种结果的可能性都一样,所以我们不会提前知道结果偏向哪个。但是,非常规的骰子有所不同——某些结果的发生概率远大于其它结果——所以它的结果的不确定性也低一些。
这么一来,我们就能明白,当每种结果的发生概率相同时,它的熵最高。而这种概率分布也就是传说中的「均匀」分布。
交叉熵是熵的一个拓展概念,它引入了第二个变量的概率分布。
crossEntropy <- function(x,y){
prX <- prop.table(table(x))
prY <- prop.table(table(y))
H <- sum(prX * log(prY,2))
return(-H)
}
两个相同概率分布之间的交叉熵等于其各自单独的熵。但是对于两个不同的概率分布,它们的交叉熵可能跟各自单独的熵有所不同。
这种差异,或者叫「散度」可以通过 KL 散度(Kullback-Leibler divergence)量化得出。
两概率分布 X 与 Y 的 KL 散度如下:
概率分布 X 与 Y 的 KL 散度等于它们的交叉熵减去 X 的熵。
KL 散度的最小值为 0,仅当两个分布相同。
KL_divergence <- function(x,y){
kl <- crossEntropy(x,y) - entropy(x)
return(kl)
}
为了发现变量具有相关性,KL 散度的用途之一是计算两个变量的互信息(MI)。
互信息可以定义为「两个随机变量的联合分布和边缘分布之间的 KL 散度」。如果二者相同,MI 值取 0。如若不同,MI 值就为一个正数。二者之间的差异越大,MI 值就越大。
为了加深理解,我们首先简单回顾一些概率论的知识。
变量 X 和 Y 的联合概率就是二者同时发生的概率。例如,如果你抛掷两枚硬币 X 和 Y,它们的联合分布将反映抛掷结果的概率。假设你抛掷硬币 100 次,得到「正面、正面」的结果 40 次。联合分布将反映如下。
P(X=H, Y=H) = 40/100 = 0.4
jointDist <- function(x,y){
N <- length(x)
u <- unique(append(x,y))
joint <- c()
for(i in u){
for(j in u){
f <- x[paste0(x,y) == paste0(i,j)]
joint <- append(joint, length(f)/N)
}
}
return(joint)
}
边缘分布是指不考虑其它变量而只关注某一特定变量的概率分布。假设两变量独立,二者边缘概率的乘积即为二者同时发生的概率。仍以抛硬币为例,假如抛掷结果是 50 次正面和 50 次反面,它们的边缘分布如下:
P(X=H) = 50/100 = 0.5 ; P(Y=H) = 50/100 = 0.5
P(X=H) × P(Y=H) = 0.5 × 0.5 = 0.25
marginalProduct <- function(x,y){
N <- length(x)
u <- unique(append(x,y))
marginal <- c()
for(i in u){
for(j in u){
fX <- length(x[x == i]) / N
fY <- length(y[y == j]) / N
marginal <- append(marginal, fX * fY)
}
}
return(marginal)
}
现在让我们回到抛硬币的例子。如果两枚硬币相互独立,边缘分布的乘积表示每个结果可能发生的概率,而联合分布则为实际得到的结果的概率。
如果两硬币完全独立,它们的联合概率在数值上(约)等于边缘分布的乘积。若只是部分独立,此处就存在散度。
这个例子中,P(X=H,Y=H) > P(X=H) × P(Y=H)。这表明两硬币全为正面的概率要大于它们的边缘分布之积。
联合分布和边缘分布乘积之间的散度越大,两个变量之间相关的可能性就越大。两个变量的互信息定义了散度的度量方式。
X 和 Y 的互信息等于「二者边缘分布积和的联合分布的 KL 散度」
mutualInfo <- function(x,y){
joint <- jointDist(x,y)
marginal <- marginalProduct(x,y)
Hjm <- - sum(joint[marginal > 0] * log(marginal[marginal > 0],2))
Hj <- - sum(joint[joint > 0] * log(joint[joint > 0],2))
return(Hjm - Hj)
}
此处的一个重要假设就是概率分布是离散的。那么我们如何把这些概念应用到连续的概率分布呢?
分箱算法
其中一种方法是量化数据(使变量离散化)。这是通过分箱算法(bining)实现的,它能将连续的数据点分配对应的离散类别。
此方法的关键问题是到底要使用多少「箱子(bin)」。幸运的是,首次提出 MIC 的论文给出了建议:穷举!
也就是说,去尝试不同的「箱子」个数并观测哪个会在变量间取到最大的互信息值。不过,这提出了两个挑战:
1. 要试多少个箱子呢?理论上你可以将变量量化到任意间距值,可以使箱子尺寸越来越小。
2. 互信息对所用的箱子数很敏感。你如何公平比较不同箱子数目之间的 MI 值?
第一个挑战从理论上讲是不能做到的。但是,论文作者提供了一个启发式解法(也就是说,解法不完美,但是十分接近完美解法)。他们也给出了可试箱子个数的上限。
最大可用箱子个数由样本数 N 决定
至于如何公平比较取不同箱子数对 MI 值的影响,有一个简单的做法……就是归一化!这可以通过将每个 MI 值除以在特定箱子数组合上取得的理论最大值来完成。我们要采用的是产生最大归一化 MI 总值的箱子数组合。
互信息可以通过除以最小的箱子数的对数来归一化
最大的归一化互信息就是 X 和 Y 的最大信息系数(MIC)。我们来看看一些估算两个连续变量的 MIC 的代码。
MIC <- function(x,y){
N <- length(x)
maxBins <- ceiling(N ** 0.6)
MI <- c()
for(i in 2:maxBins) {
for (j in 2:maxBins){
if(i * j > maxBins){
next
}
Xbins <- i; Ybins <- j
binnedX <-cut(x, breaks=Xbins, labels = 1:Xbins)
binnedY <-cut(y, breaks=Ybins, labels = 1:Ybins)
MI_estimate <- mutualInfo(binnedX,binnedY)
MI_normalized <- MI_estimate / log(min(Xbins,Ybins),2)
MI <- append(MI, MI_normalized)
}
}
return(max(MI))
}
x <- runif(100,-10,10)
y <- x**2 + rnorm(100,0,10)
MIC(x,y) # --> 0.751
以上代码是对原论文中方法的简化。更接近原作的算法实现可以参考 R package *minerva*(https://cran.r-project.org/web/packages/minerva/index.html)。
在 Python 中的实现请参考 minepy module(https://minepy.readthedocs.io/en/latest/)。
MIC 能够表示各种线性和非线性的关系,并已得到广泛应用。它的值域在 0 和 1 之间,值越高表示相关性越强。
置信区间?
为了建立 MIC 估计值的置信区间,你可以简单地使用一个像我们之前介绍过的 bootstrap 函数。我们可以利用 R 语言的函数式编程,通过传递我们想要用作参数的函数来泛化 bootstrap 函数。
bootstrap <- function(x,y,func,reps,alpha){
estimates <- c()
original <- data.frame(x,y)
N <- dim(original)[1]
for(i in 1:reps){
S <- original[sample(1:N, N, replace = TRUE),]
estimates <- append(estimates, func(S$x, S$y))
}
l <- alpha/2 ; u <- 1 - l
interval <- quantile(estimates, c(u, l))
return(2*(func(x,y)) - as.numeric(interval[1:2]))
}
bootstrap(x,y,MIC,100,0.05) # --> 0.594 to 0.88
小结
为了总结相关性这一主题,我们来测试下各算法在人工生成数据上的处理能力。
完整代码:https://gist.github.com/anonymous/fabecccf33f9c3feb568384f626a2c07
噪声函数
set.seed(123)
# Noise
x0 <- rnorm(100,0,1)
y0 <- rnorm(100,0,1)
plot(y0~x0, pch = 18)
cor(x0,y0)
distanceCorrelation(x0,y0)
MIC(x0,y0)
皮尔逊相关系数 r = - 0.05
距离相关性 = 0.157
MIC = 0.097
简单线性函数
# Simple linear relationship
x1 <- -20:20
y1 <- x1 + rnorm(41,0,4)
plot(y1~x1, pch =18)
cor(x1,y1)
distanceCorrelation(x1,y1)
MIC(x1,y1)
皮尔逊相关系数 r =+0.95
距离相关性 = 0.95
MIC = 0.89
简单二次函数
# y ~ x**2
x2 <- -20:20
y2 <- x2**2 + rnorm(41,0,40)
plot(y2~x2, pch = 18)
cor(x2,y2)
distanceCorrelation(x2,y2)
MIC(x2,y2)
Pearson』s r =+0.003
距离相关性 = 0.474
MIC = 0.594
三次函数
# Cosine
x3 <- -20:20
y3 <- cos(x3/4) + rnorm(41,0,0.2)
plot(y3~x3, type='p', pch=18)
cor(x3,y3)
distanceCorrelation(x3,y3)
MIC(x3,y3)
皮尔逊相关系数 r =- 0.035
距离相关性 = 0.382
MIC = 0.484
圆函数
# Circle
n <- 50
theta <- runif (n, 0, 2*pi)
x4 <- append(cos(theta), cos(theta))
y4 <- append(sin(theta), -sin(theta))
plot(x4,y4, pch=18)
cor(x4,y4)
distanceCorrelation(x4,y4)
MIC(x4,y4)
皮尔逊相关系数 r < 0.001
距离相关性 = 0.234
MIC = 0.218
还没有评论,来说两句吧...