再谈“去虚拟化”对深度学习系统的必要性

“虚拟化”溯源

 

“虚拟化”可能是计算机科学历史上*伟大的思想之一,对此,计算机先驱David Wheeler有一句名言:

All problems in computer science can be solved by another level of indirection, except for the problem of too many layers of indirection.

这句话被尊称为“软件工程的基本定理”,并被C++之父Bjarne Stroustrup在专著《The C++ Programming Language》的序言处引用。不过,大部分人记住了前半句,却忽略了后半句,而后半句正是本文想展开讨论的。

“another level of indirection”刻画了“虚拟化”的精髓:通过引入一层新的抽象,把与上层应用无关的细节隐藏掉,有选择的给上层用户暴露一些功能供其使用,也称底层细节对用户透明,既不损害上层应用的功能,又能享受“关注点分离”(separation of concerns)的好处,增加易用性

可以说:虚拟化的案例无处不在,无往而不利。

操作系统是对硬件资源的虚拟化:计算核心被虚拟化成进程;硬件内存变成虚拟内存;存储介质被虚拟化成文件系统;网络传输通过多层协议栈被虚拟化成文件描述符,使得数据传输就像读写普通的文件一样。

分布式存储把通过网络互联多个单机文件系统虚拟化成的一个网络文件系统,使得用户不用再关心网络数据传输的细节,可以像访问本地文件一样访问其它节点上的数据。

当然,虚拟化造就了今天伟大的云计算技术和市场,云原生如火如荼,开发者只需要基于云服务的API编程,而不需要关于API 之下的物理细节,成本低,可靠性又高。

虚拟化的成功不可否认。但是,成也萧何,败也萧何,虚拟化也有其代价。

%title插图%num

“虚拟化”和*致性能的矛盾

 

一层层的抽象层次,一步步降低编程的复杂度,每一层都向上隐藏了一些东西,上层就相应地丢失一些操控能力。每经过一层抽象,就引入一些对上层而言的“不确定性”,*终的结果是,性能的天花板一步一步下降。

以Hadoop的分布式存储为例,虚拟化的结果是掩盖了数据真实的存储位置,不管在哪个节点上,访问接口都是一样的。不过,当向这个集群调度一些MapReduce的数据处理任务时,如果Task被调度到数据所在的节点上,那么就不需要网络传输,如果调度到另外的节点,就需要把数据读到内存并通过网络发送到计算所在的节点。也就是,被隐藏掉的数据位置信息有可能被调度器用来提升系统效率。

随着摩尔定律放缓,不止在深度学习领域,在任何追求*致性能的场景就出现了一种击穿、击碎中间抽象层次,一竿子插到底进行协同优化的强烈需求。 

让我们看六个例子。

1、操作系统把CPU资源抽象成被操作系统调度和管理的内核线程,避免了用户调度计算资源的麻烦,但是,在高并发场景,用户级线程(如coroutine)越来越多,计算资源更多的在用户态(user mode)来调度,而不是完全依靠内核的调度。

2、操作系统通过页表机制实现虚拟内存,神不知鬼不觉实现数据的换入换出,但在高性能场景,用户程序会通过编程接口禁止这一行为,譬如在RDMA和GPU异步数据传输都依赖于锁页内存,确保操作系统不会帮倒忙。

3、传统的网络传输协议栈TCP/IP都在操作系统内核,为了避免内核态和用户态的数据拷贝和上下文切换,人们先是使用用户态协议栈(如Intel dpdk),仍无法满足需求的话,就使用支持内核旁路(bypass)的RDMA技术,完全跳过了操作系统这一层。

4、缓存(Cache)技术也可以视为一种虚拟化,命中就直接使用,没命中就花时间从更慢的存储取过来,在局部性较好的负载下工作得很好。但在特定领域,譬如深度学习领域,工作负载有特别的规律,如果用软件来控制缓存数据的弹出以及读取甚至可以做到100%的命中率,这种办法被称为Scratchpad,现在几乎所有的AI芯片都没有用Cache,而是使用Scratchpad技术。

5、操作系统的文件系统或者网络文件系统,把底层硬件的细节隐藏了,编程时不需要考虑数据在哪台机器的哪块磁盘上,编程更简单了,但访问近处的数据速度快,访问远处的数据速度慢,为了提高效率,有人就提出了locality aware(局部性感知)的调度,把计算任务尽可能调度到存储数据的节点上去。

6、工业级的大型软件系统里,通常不会使用库函数,恨不得把底层代码库重新打造一遍,譬如Chromium, OceanBase等大型C++项目,我想,这也可以算作反虚拟化的例子。

%title插图%num

“虚拟化”的舒适区

 

虚拟化试图向上层提供一剂一劳永逸的灵丹妙药,既解决易用性,又不损害上层应用对性能的需求。但是,“虚拟化”到底好不好应该具体问题具体分析,那该如何判断虚拟化到底合不合适呢?

这里尝试抛出一个用来判断是否有必要引入一层抽象(虚拟化)的量化指标,供参考。每引入一层虚拟化,就向上层隐藏了一些东西,向上提供的服务的延迟就因此引入了不确定性(以分布式存储为例,有的数据近,有的数据远;以分布式GPU资源池为例,有的近,有的远),服务的响应延迟有一个波动范围,也就是[min, max],延迟越小越好。

%title插图%num如果上层某一个应用需要保证的*低延迟仍大于max,那么这层虚拟化对这个应用就没有损害,可以大胆的引入这个虚拟化技术(也就是上图的C区域)。

如果上层某一个应用需要保证的*低延迟介于min和max之间,那么引入这层虚拟化就对这个应用是有损害的,把这层虚拟化敲掉,就可以确保以min值满足这个应用对延迟的需求(也就是上图的B区域)。

如果上层某一个应用需要保证的*低延迟小于min,那么即使敲掉当前层的虚拟化,仍不能满足这个应用,就需要继续向下敲,进行联合优化,直到延迟得到满足(也就是上图的A区域)。

%title插图%num

鱼与熊掌兼得?

 

如上所述,虚拟化的基本思路是通过“隐藏细节”给上层应用提供一种假象,降低上层应用使用底层资源的复杂度。不过,有时候,“隐藏”掉的信息会阻碍上层应用挖掘*致的性能。有没有两全其美的办法呢?

David Patterson在《计算机体系结构的黄金时代》一文中开出的药方是从算法到硬件直接打通,结合算法和硬件的特点全部搞定制。定制表现在DSL和DSA,一方面是设计领域特定语言,方便编程,另一方面为面向领域应用设计领域特定架构,挖掘*致效率。

David Patterson的药方既不是在已有方案中插入新的抽象层次,也不是完全不要中间抽象,而是把原有的抽象层次全部敲碎重建,这种重建是基于对算法和硬件特点的充分挖掘。我理解这里有俩关键:软硬件协同设计(分工),以及编译器技术。

一方面,这里的思路和虚拟化不同之处是:不是向上层应用隐藏什么,而是强调要向上层暴露什么,或者说向上层让渡什么职责。

另一方面,这里没有期待只要提供一层API的抽象就包打天下,而是对从算法到硬件的映射复杂性有充足的认识。这种映射既包含任务相关但硬件无关的问题,也包含硬件相关的问题,本质等同于人们熟知的编译器技术。

说到这里,想到另一个“去虚拟化”的*佳例子,就是软件定义网络(Software defined network,SDN)。对于同一套硬件基础设施,承接不同的工作负载,交换机的*优转发规则是不同的。SDN的思路是,令交换机的转发规则是可编程的,对每一个不同的业务负载,都用静态分析得到*优的转发规则或策略(control plane),并按照这个规则对交换机编程,交换机在转发数据时只需要按照已编程的规则执行即可(data plane)。

在SDN的例子里,交换机让渡了一部分职责给软件,软件根据不同的业务负载都生成不一样的路由规则(策略),这是SDN平衡灵活性和*致性能的关键。

在OneFlow解决分布式深度学习的难题时,也是类似的思路,它不是靠额外引入的一个抽象层次实现,而是分成了控制平面和数据平面。在控制平面,编译器根据特定深度学习模型的任务负载和底层硬件拓扑生成对这个配置*优的执行计划(execution plan), 这个plan不是一劳永逸的,它需要上层模型的知识,也需要底层硬件的知识,模型或硬件拓扑一旦变化,plan就会变化。但是,编译器生成plan的机制可以认为是不变的,也是整套系统的精髓。

 

%title插图%num

结语

 

这篇文章补充解释了我对“通过向上层算法隐藏硬件信息”的虚拟化思路,以及“通过向上层算法暴露硬件信息和让渡职责”的去虚拟化的思路的理解。

简单来说,虚拟化的思想强调的是隐藏(底层细节)和限制(上层的功能范围),潜台词是:我认为对上层应用的需求足够了解,告诉上层应用太多细节也没有用,我把底层的东西一揽子处理好了,你只管调用我为你提供的API就可以了。

软件定义的思想强调的是暴露(底层细节)和让渡(硬件的策略可被软件分析和配置),潜台词是:我对上层应用的需求了解不足,没有办法为上层应用提供一个足够令人满意的一揽子的策略,于是干脆把底层策略暴露给上层,上层应用对自己的需求和任务负载*清楚,上层自己来负责生成策略并来配置底层。

致力于提升深度学习系统性能的朋友对“去虚拟化”的思路应该是习以为常的,这应该是整个社区经过探索和碰壁逐渐收敛出来的主流思路,不是我个人的发明,我只是把观察和理解写下来而已。

顺着这个思路,算法-软件-硬件协同优化领域正在发生一些令人兴奋的进展。毫无疑问,广义上的编译器技术(无论是单设备代码生成,还是分布式执行计划生成)是里面*核心的部分。

ios 汉字转拼音

from:http://blog.csdn.net/meegomeego/article/details/23253939

之前做通讯录相关的一些App时,有一个比较常用的算法是将汉字转换成拼音。当时采用的做法是:将各个拼音段的首个汉字(按Unicode排序)做成两个数组,一个数组存拼音,另一个数组存拼音对应首个汉字的Unicode。如果要获取某个汉字的拼音,可以折半查找法找的对应拼音。*近无意间发现CFStringTransform这篇文章后,发现系统本身已经提供了一个这样的函数。

CFStringTransform

iOS在CoreFoundation中提供了CFStringTransform函数,但在Foundation中却没有相对应的方法。它的定义如下:

Boolean CFStringTransform(CFMutableStringRef string, CFRange *range, CFStringRef transform, Boolean reverse);

其中string参数是要转换的string,比如要转换的中文,同时它是mutable的,因此也直接作为*终转换后的字符串。range是要转换的范围,同时输出转换后改变的范围,如果为NULL,视为全部转换。transform可以指定要进行什么样的转换,这里可以指定多种语言的拼写转换。reverse指定该转换是否必须是可逆向转换的。如果转换成功就返回true,否则返回false

如果要进行汉字到拼音的转换,我们只需要将transform设定为kCFStringTransformMandarinLatin或者kCFStringTransformToLatinkCFStringTransformToLatin也可适用于非汉字字符串):

  1. CFMutableStringRef string = CFStringCreateMutableCopy(NULL, 0, CFSTR(“中国”));
  2. CFStringTransform(string, NULL, kCFStringTransformMandarinLatin, NO);
  3. NSLog(@”%@”, string);

这段代码将输出:

2013-11-22 14:41:14.644 Test[2436:907] zhōng guó

可以看出,CFStringTransform正确的输出了“中国”的拼音,而且还带上了音标。有时候我们不需要音标怎么办?还好CFStringTransform同时提供了将音标字母转换为普通字母的方法kCFStringTransformStripDiacritics。我们在上面的代码基础上再加上这个:

  1. CFStringTransform(string, NULL, kCFStringTransformStripDiacritics, NO);
  2. NSLog(@”%@”, string);

那么*终将输出:

2013-11-22 14:47:00.380 Test[2470:907] zhong guo

refence doc:http://nshipster.cn/cfstringtransform/

IOS开发中判断文件是否存在,不存在则拷贝

  1.     首先,先总结一下如何获取Documents目录,在ios开发中,我们经常需要检索Documents目录的完整路径以便读取和写入文件,我总结了以下两种方法:
  2. 1NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@”Documents”];
  3. 2NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  4.    NSString *documentsDirectory = [paths objectAtIndex:0];
  5.  以上documentsDirectory就是获取的Documents的完整路径。
  6.  程序的所有资源文件,存储在程序包中,获取程序包路径的方法是:
  7. NSString *appDirectory = [[NSBundle mainBundle] bundlePath];
  8. 在实际开发中我们有时需要判断Documents下的某个资源文件是否存在,如果不存在,则从程序包中拷贝进去,现在假设要判定的文件是“xxx.txt”,判定代码如下:
  9. NSFileManager *fileManager = [NSFileManager defaultManager];
  10. NSString *filePath = [self [documentsDirectory stringByAppendingPathComponent:@”xxx.txt”]];
  11. if(![fileManager fileExistsAtPath:filePath]) //如果不存在
  12. {
  13.      NSLog(@”xxx.txt is not exist”);
  14.      NSString *dataPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@”/xxx.txt”];//获取程序包中相应文件的路径
  15.      NSError *error;
  16.      if([fileManager copyItemAtPath:dataPath toPath:filePath error:&error]) //拷贝
  17.      {
  18.           NSLog(@”copy xxx.txt success”);
  19.      }
  20.      else
  21.      {
  22.           NSLog(@”%@”,error);
  23.      }
  24. }

ios判断程序文件夹下是否存在指定文件

  1. NSString *fileName=@”04.jpg”;
  2. NSArray *DocumentPath= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  3. NSString *filepath=[[NSString alloc] initWithString:[NSString stringWithFormat:@”%@/%@”,DocumentPath[0],fileName]];
  4. NSFileManager *fileManager = [NSFileManager defaultManager];
  5. if ([fileManager fileExistsAtPath:filepath]) {
  6. NSLog(@”file is exists”);
  7. } else{
  8. NSLog(@”file is not exists”);
  9. };

iOS-判断沙盒中是否已经存在文件

判断方法,类内方法:

//判断文件是否已经在沙盒中已经存在?
-(BOOL) isFileExist:(NSString *)fileName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *filePath = [path stringByAppendingPathComponent:fileName];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL result = [fileManager fileExistsAtPath:filePath];
NSLog(@”这个文件已经存在:%@”,result?@”是的”:@”不存在”);
return result;
}

以上是判断cache文件夹,如果判断document文件将:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);

替换为:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

即可。

日本 vultr 服务器怎么样

配置:内存 8G、100G SSD、4CPU、4000G BD

用途:直播

vultr SSD CPU g9 条回复 • 2018-08-10 09:33:37 +08:00
zrj766 1
zrj766 2017-10-24 11:23:48 +08:00
算了,经常 boom,尤其是电信
zlfzy 2
zlfzy 2017-10-24 11:25:15 +08:00
广州,体验不咋的,电信时不时 BOOM,联通绕路北京
ninay 3
ninay 2017-10-25 20:26:56 +08:00
check http://zhujidot.com/
lzl119 4
lzl119 2017-10-25 23:20:47 +08:00
移动还可以,电信有点绕
yinjygg 5
yinjygg 2017-11-08 10:55:33 +08:00 via Android
买阿里云稳点
WangYouGX 6
WangYouGX 2018-01-25 09:34:18 +08:00 via Android
深圳,用 miami 的,能达到 40mbps,前提是开启 bbr,,加密方式是 chacha20
WangYouGX 7
WangYouGX 2018-01-25 09:35:05 +08:00 via Android
日本的我曾经用过,竟然没有 miami 稳,很容易被封 IP
wunonglin 8
wunonglin 2018-08-09 15:18:22 +08:00
已经废了,别考虑了
NELSONCHI 9
NELSONCHI 2018-08-10 09:33:37 +08:00
@wunonglin 这个时间节点,蛮尴尬的

怎么从理论上估计一台服务器的*大负载

有一台 2 核 4G 的阿里云服务器,不停向服务器建立 tcp 连接,并每秒发送 100 字节左右的包并在 1s 内返回 100 字节左右的包.

现在发现 1000 个连接读包有些已经超过 3s 了.

怎么根据服务器的配置去估计大概能抗住多少个这样的连接.

通过 tcp 的结构能估计出内存的*大负载,但是 CPU 的*大负载不知道怎么去估计,难道是去估计一个读写执行了多少个 CPU 指令然后通过嘛?

估计 负载 服务器 TCP6 条回复 • 2018-02-02 09:50:43 +08:00
MungBeanSoup 1
MungBeanSoup 2018-02-01 20:09:10 +08:00
用 Go 实现的
helloworld12 2
helloworld12 2018-02-01 20:39:04 +08:00
你这种情况, 可能是内存 gc 问题
helloworld12 3
helloworld12 2018-02-01 20:39:52 +08:00
可以用 pprof,trace 来分析
alcarl 4
alcarl 2018-02-01 21:25:56 +08:00 via Android
建立连接和收发数据本身并没有意义,还要加上业务负载才好评估,云上的鸡还要考虑带宽问题,100 字节的包 x1000 刚好 1mbps
MungBeanSoup 5
MungBeanSoup 2018-02-02 09:49:40 +08:00
@helloworld12

pprof 使用了,cpu 和 heap 基本都是 rpc 库的占*多
MungBeanSoup 6
MungBeanSoup 2018-02-02 09:50:43 +08:00
@alcarl 带宽是临时升级的 50Mbps,业务负载已经去掉了,现在就想知道怎么去评估一台服务器处理连接的能力.

iOS-NSLineBreakMode-lineBreakMode属性详解(UILabel省略号位置)

apple文档:

@property(nonatomic) NSLineBreakMode lineBreakMode; // default is NSLineBreakByTruncatingTail. used for single and multiple lines of text

用于多行和多行文本->字符截断类型(lineBreakMode //设置文字过长时的显示格式 )

// NSParagraphStyle
typedef NS_ENUM(NSInteger, NSLineBreakMode) {
NSLineBreakByWordWrapping = 0, // Wrap at word boundaries, default
NSLineBreakByCharWrapping, // Wrap at character boundaries
NSLineBreakByClipping, // Simply clip
NSLineBreakByTruncatingHead, // Truncate at head of line: “…wxyz”
NSLineBreakByTruncatingTail, // Truncate at tail of line: “abcd…”
NSLineBreakByTruncatingMiddle // Truncate middle of line: “ab…yz”
} NS_ENUM_AVAILABLE(10_0, 6_0);

解释:
UILineBreakModeWordWrap = 0,
以单词为单位换行,以单词为单位截断。

UILineBreakModeCharacterWrap,
以字符为单位换行,以字符为单位截断。
->
label.lineBreakMode = NSLineBreakByCharWrapping;以字符为显示单位显
示,后面部分省略不显示。
label.lineBreakMode = NSLineBreakByClipping;剪切与文本宽度相同的内
容长度,后半部分被删除。
label.lineBreakMode = NSLineBreakByTruncatingHead;前面部分文字
以……方式省略,显示尾部文字内容。
label.lineBreakMode = NSLineBreakByTruncatingMiddle;中间的内容
以……方式省略,显示头尾的文字内容。
label.lineBreakMode = NSLineBreakByTruncatingTail;结尾部分的内容
以……方式省略,显示头的文字内容。
label.lineBreakMode = NSLineBreakByWordWrapping;以单词为显示单位显
示,后面部分省略不显示。

精心总结了10个matplotlib绘图技巧,短小精悍,威力无穷!

快速且正确的显示中文

在matplotlib中,默认是没法显示中文的,原因很简单,默认使用的字体文件中不含中文。

当你搜索如何设置中文时,会有很多教程,我曾经也详细的介绍过3种方法,但是在有的电脑上折腾很久也搞不定,所以当你浪费了太多时间也没法解决,或者在一台陌生的设备上使用,需要快速的让matplotlib显示中文可以使用下面的代码。

  1. import matplotlib as mpl
  2. WRYH = mpl.font_manager.FontProperties(
  3.     fname=‘/Users/liuzaoqi/Desktop/可视化图鉴/font/WeiRuanYaHei-1.ttf’)
  4. # 微软雅黑字体
  5. plt.title(‘可视化图鉴’, fontproperties=WRYH)

很简单,既然修改默认的字体搞不定,那么就准备一个中文字体的路径,并在每次需要显示中文的组件(标题、注释、图例等)时,就强制指定使用这个字体。

这个办法一定可以让你图中的中文快速且正确显示出来,但是如果涉及中文的地方过多,还是需要在以后尝试一劳永逸的将中文字体添加到默认字体集中。

%title插图%num

提高分辨率

如果感觉默认生成的图形分辨率不够高,可以尝试修改 dpi 来提高分辨率,在matplotlib中可以一次性通过plt.rcParams修改,也可以在创建画布时为这一次的绘制修改,例如将分辨率调整至100

plt.figure(figsize = (7,6),dpi =100)

如果你的设备是配备Retina屏幕的mac,可以在jupyter notebook中,使用下面一行代码有效提高图像画质

%config InlineBackend.figure_format = 'retina'

%title插图%num

添加数学公式

有时我们在绘图时需要添加带有数学符号、公式的文字,如果直接使用默认的方法添加,虽然可以实现,但是不够美观,其实在matplotlib中也支持输出数学公式,就像下面的文字注释一样

%title插图%num只用在需要出现公式的位置使用r’你的公式’即可,比如plt.text(11000,0.45,r’拟合的曲线为$f(x) = 1.3x^2-6.54x+0.5$’)注意$$中是latex格式的表达方式,如果不熟悉的话需要自行搜索。

当然,也可以使用plt.rcParams[‘text.usetex’] = True设置默认tex输出,异曲同工,此处不多介绍。

%title插图%num

一行代码提高颜值

有时我们会觉得matplotlib默认制作出来的图片太朴素了,不够高级,其实开发者也内置了几十种主题让我们自己选择,只要使用plt.style.use(‘主题名’)指定主题即可,具体的支持主题我也整理出来的在《一行代码提高颜值,给你的matplotlib换个样式!》这篇文章中。

例如使用R语言中经典的ggplot主题

plt.style.use('ggplot')

%title插图%num另外一种提高图形颜值的代码是借用seaborn调色板修改配色,也是一行代码,通过sns.set_palette(xxxx)就能直接调整全局配色方案,下面是我比较喜欢的一种配色

  1. import seaborn as sns
  2. sns.set_palette(“pastel”8)

%title插图%num%title插图%num

调整图例位置

虽然matplotlib在生成图例时,默认会选择一个*合适的位置,但是有时依旧不能让人满意。

相信你知道我们可以使用loc = xxxx自己设置图例位置,但是对于xxxx每次都要查,看文字说明左上右下不如一张图来的快,下面是我们可以选择的9个位置,可以根据自己的需要进行选择。

%title插图%num%title插图%num

获得当前绘图区域

在原理篇文章中,我曾经说到如果想要获得更精细的调整,就需要使用ax.xxxx进行调整,但有时我们开始没有使用fig,ax=plt.subplots(figsize = (6,4),dpi=100)来定义ax,在需要使用时重新开始并定义是不推荐的,这时可以使用

ax = plt.gca()

获得当前Figure中的Axes,并继续正常使用ax.xxxx调整

%title插图%num

隐藏坐标轴

对于有些图形我们希望通过隐藏坐标轴来显得更加美观,这时可以ax.spines获取对应位置的的Spine对象,之后便可以任意修改是否显示与显示颜色,比如让右边和上面的线消失或者修改颜色

  1. ax.spines[‘right’].set_color(‘None’)
  2. ax.spines[‘top’].set_color(‘None’)

%title插图%num

  1. ax.spines[‘right’].set_color(‘deeppink’)
  2. ax.spines[‘top’].set_color(‘blue’)
  3. ax.spines[‘bottom’].set_color(‘green’)
  4. ax.spines[‘left’].set_color(‘red’)

%title插图%num%title插图%num

指定坐标轴刻度

如果对于默认生成的坐标轴刻度不满意,我们可以使用plt.yticks([])来自定义刻度,注意需要传入一个你想要的刻度list,并且长度需要和刻度对应,例如下方代码

  1. label = [‘2月7日’,‘3月25日’,‘5月13日’,‘7月2日’,‘8月21日’,’10月10日’,’11月29日’,’12月31日’]
  2. plt.yticks(range(0,400,50), labels=label,rotation = 40,color = ‘black’)

%title插图%num当然如果label是空的话,可以结合上一个技巧把对应的坐标轴干掉,彻底消失

plt.yticks([])

%title插图%num

添加自定义图片

有时我们希望在对数据可视化后添加一些图片来丰富元素,虽然可以使用ps添加,但其实matplotlib也可以独立完成,总共分两步:打开图片——添加图片

首先导入相关的包并根据图片路径导入

  1. from matplotlib.offsetbox import (OffsetImage,AnnotationBbox)
  2. import matplotlib.image as mpimg
  3. arr_img = mpimg.imread(‘你的图片位置’)

接着就可以根据坐标将图片添加到指定位置,详细的设置可以参考官方文档,下面的代码可以拿走就用,根据效果调整坐标即可

  1. imagebox = OffsetImage(arr_img, zoom=0.2)
  2. ab = AnnotationBbox(imagebox, [0.15,0.5],
  3.                     xybox=(170.-50.),
  4.                     xycoords=‘data’,
  5.                     boxcoords=“offset points”,
  6.                     pad=0.5
  7.                     )
  8. ax.add_artist(ab)

%title插图%num

添加表格

有时只用图片很难传递更多的信息,幸运的是,在matplotlib中也可以使用plt.table来为图片添加一张与之对应的表格,只需要将你的数据按照指定格式传入即可。

下面是我在官方文档示例的基础上,添加渐变效果的表格

  1. data = [[ 66386174296,  75131577908,  32015],
  2.         [ 58230381139,  78045,  99308160454],
  3.         [ 89135,  80552152558497981603535],
  4.         [ 78415,  81858150656193263,  69638],
  5.         [139361331509343164781380,  52269]]
  6. columns = (‘Freeze’‘Wind’‘Flood’‘Quake’‘Hail’)
  7. rows = [‘%d year’ % x for x in (1005020105)]
  8. values = np.arange(02500500)
  9. value_increment = 1000
  10. colors = plt.cm.BuPu(np.linspace(00.5len(rows)))
  11. n_rows = len(data)
  12. index = np.arange(len(columns)) + 0.3
  13. bar_width = 0.4
  14. plt.figure(figsize = (8,5),dpi =100)
  15. y_offset = np.zeros(len(columns))
  16. cell_text = []
  17. for row in range(n_rows):
  18.     plt.bar(index, data[row], bar_width, bottom=y_offset, color=colors[row])
  19.     y_offset = y_offset + data[row]
  20.     cell_text.append([‘%1.1f’ % (x / 1000.0for x in y_offset])
  21. colors = colors[::-1]
  22. cell_text.reverse()
  23. the_table = plt.table(cellText=cell_text,
  24.                       rowLabels=rows,
  25.                       rowColours=colors,
  26.                       colLabels=columns,
  27.                       loc=‘bottom’
  28.                      ,cellColours=plt.cm.Blues(x))
  29. plt.subplots_adjust(left=0.2, bottom=0.2)
  30. plt.ylabel(“Loss in ${0}’s”.format(value_increment))
  31. plt.yticks(values * value_increment, [‘%d’ % val for val in values])
  32. plt.xticks([])
  33. plt.title(‘Loss by Disaster’)
  34. plt.show()

%title插图%num以上就是本文和大家总结的10个matplotlib小技巧,在未来的文章中我会继续分享相关知识,也希望大家可以在使用一些数据可视化工具时可以不断总结,不断进步。

  1. ☞可供货华为!ARM 十年来首次发布*新架构 Armv9☞多出20倍?Android 收集用户数据量远超 iPhone
  2. ☞将 Linux 移植到 M1 Mac 真的太难了!%title插图%num

iOS 代码块在程序中只执行一次

//开启定位的同时展示用户可行驶范围
static dispatch_once_t disOnce;
dispatch_once(&disOnce, ^ {
//根据电量判断展示的圆的半径
CGFloat radius = 1500.0f;
_circle = [MACircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(self.mapView.userLocation.location.coordinate.latitude, self.mapView.userLocation.location.coordinate.longitude) radius:radius];

//根据半径判断缩放级别
[_mapView setZoomLevel:[self zoomlevelAccordingRadius:radius] animated:YES];
//在地图上添加圆
[_mapView addOverlay: _circle];
});