数据可视化可以直观的展示数据之间的关系,但数据可视化最重要的不是你展示的工具和做的图多么漂亮,而是紧紧围绕核心目的,把你想表达的内容传递给目标人群。

所以,数据可视化具体呈现形式,取决于你的目的。

数据可视化工具

类别 工具 适用范围
商业智能分析 Tableau 适合BI工程师、数据分析师
PowerBI 配合excel使用
FineBI 中国帆软出品,国内使用更友好,企业级BI
可视化大屏类 DataV
FineRepor 可以数据大屏和可视化报表
前端可视化组件
(前端工程师适用) Canvas 适用于位图,可用于绘制比较复杂的动画
SVG 可缩放矢量图形,经常用于图标和图表上,任意缩放不会失真
WebGL 3D 绘图协议,可在网页浏览器中呈现 3D 画面技术
编程语言 python常用的 Matplotlib、Seaborn、 Bokeh、Plotly、Pyecharts、Mapbox 和 Geoplotlib,其中适用频率最高的Matplotlib 和 Seaborn
R语言 Graphics 以及工具包 ggplot2、ggmap、timevis 和 plotly

工具使用建议:

如果目标是成为数据挖掘工程师或算法工程师,最重要的是了解和使用python数据可视化工具。

推荐使用Tableau—使用范围广。

使用微图、DataV—成本低,上手快。

散点图

用来反映两个变量之间的关系

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  

|

import numpy as np  
import pandas as pd  
import matplotlib.pyplot as plt  
  
# 数据准备  
N = 1000  
x = np.random.randn(N)  
y = np.random.randn(N)  
  
# 用Matplotlib画散点图  
plt.scatter(x, y,marker='x')  
plt.show()  
  

—|—

折线图

用来反映随时间变化后的关系

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

|

import pandas as pd  
import matplotlib.pyplot as plt  
  
# 数据准备  
x = [2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019]  
y = [5, 3, 6, 20, 17, 16, 19, 30, 32, 35]  
  
# 使用Matplotlib画折线图  
plt.plot(x, y)  
plt.show()  
  

—|—

直方图

反映变量的数值分布

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  

|

import numpy as np  
import pandas as pd  
import matplotlib.pyplot as plt  
  
# 数据准备  
a = np.random.randn(100)  
s = pd.Series(a)  
  
# 用Matplotlib画直方图  
plt.hist(s) #默认为10个直方图plt.hist(x, bins=10)  
plt.show()  
  

—|—

条形图

反映各阶段数值出现的频率

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  

|

import numpy as np  
import pandas as pd  
import matplotlib.pyplot as plt  
  
# 数据准备  
x = ['Cat1', 'Cat2', 'Cat3', 'Cat4', 'Cat5']  
y = [5, 4, 8, 12, 7]  
  
# 用Matplotlib画条形图  
plt.bar(x, y) # plt.bar(x, height) 函数,参数 x 代表位置,height代表y轴的高度  
plt.show()  
  

—|—

箱线图

包括最大值 (max)、最小值 (min)、中位数 (median) 和上下四分位数 (Q3, Q1)几个部分。

用于分析数据差距急离散程度。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  

|

import numpy as np  
import pandas as pd  
import matplotlib.pyplot as plt  
  
# 数据准备  
# 生成10*4维度数据  
data=np.random.normal(size=(10,4))   
lables = ['A','B','C','D']  
  
# 用Matplotlib画箱线图  
plt.boxplot(data,labels=lables) # 使用 plt.boxplot(x, labels=None),x代表数据,labels添加数据标签  
plt.show()  
  

—|—

饼图

用于表示各部分与分部分的比例。

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import matplotlib.pyplot as plt  
  
# 数据准备  
nums = [25, 37, 33, 37, 6]  
labels = ['High-school','Bachelor','Master','Ph.d', 'Others']  
  
# 用Matplotlib画饼图  
plt.pie(x = nums, labels=labels) #使用plt.pie(x, labels=None)函数  
plt.show()  
  

—|—

热力图

直观的进行多元分析,不同颜色表示不同类别的数据。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

|

import matplotlib.pyplot as plt  
import seaborn as sns # 一般使用seaborn模块  
  
# 数据准备  
flights = sns.load_dataset("flights")  
data=flights.pivot('year','month','passengers')  
  
# 用Seaborn画热力图  
sns.heatmap(data) # 使用sns.heatmap(data),其中data表示数据  
plt.show()  
  

—|—

蜘蛛图

显示多维变量对整体的影响程度

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  

|

import numpy as np  
import matplotlib.pyplot as plt  
import seaborn as sns  
from matplotlib.font_manager import FontProperties    
  
# 数据准备  
labels=np.array([u"推进","KDA",u"生存",u"团战",u"发育",u"输出"])  
stats=[83, 61, 95, 67, 76, 88]  
# 画图数据准备,角度、状态值  
angles=np.linspace(0, 2*np.pi, len(labels), endpoint=False)  
stats=np.concatenate((stats,[stats[0]]))  
angles=np.concatenate((angles,[angles[0]]))  
# 用Matplotlib画蜘蛛图  
fig = plt.figure()  
ax = fig.add_subplot(111, polar=True)     
ax.plot(angles, stats, 'o-', linewidth=2)  
ax.fill(angles, stats, alpha=0.25)  
# 设置中文字体  
font = FontProperties(fname=r"C:\Windows\Fonts\simhei.ttf", size=14)    
ax.set_thetagrids(angles * 180/np.pi, labels, FontProperties=font)  
plt.show()  
  

—|—

二元变量间的关系

可以使用 Seaborn呈现散点图、核密度图、Hexbin图、直方图呈现。

sns.jointplot(x, y, data=None, kind) , kind 表示不同的视图类型:“kind=‘scatter’”代表散点图,“kind=‘kde’”代表核密度图,“kind=‘hex’ ”代表 Hexbin 表示对直方图的二维模拟。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  

|

import matplotlib.pyplot as plt  
import seaborn as sns  
  
# 数据准备  
tips = sns.load_dataset("tips") # tips为Seaborn中自带的数据集  
print(tips.head(10))  
  
# 用Seaborn画二元变量分布图(散点图,核密度图,Hexbin图)  
sns.jointplot(x="total_bill", y="tip", data=tips, kind='scatter')  
sns.jointplot(x="total_bill", y="tip", data=tips, kind='kde')  
sns.jointplot(x="total_bill", y="tip", data=tips, kind='hex')  
plt.show()  
  

—|—

参考资料

《数据分析实战45讲》|极课时间

ChangeLog

2020907 数据可视化初版

常见的数据来源

类别 解释 例子
开放数据源 重要是行业数据库,政府公开的各类数据 国家统计局、wind、巨潮资讯网
爬虫爬取的数据 通过爬虫取得的数据 豆瓣、社交网站、电商网站、图片网站
传感器采集 通过传感器采集到的数据
日志采集 应用埋点采集到的数据

爬取数据的工具

火车采集器。 可以做抓取工具,也可以做数据清洗、数据分析、数据挖掘和可视化等工作。

八爪鱼。 可以实现自动云采集。

集搜客。 缺点是没有云采集功能,所有爬虫都是在用户自己电脑上跑的。

爬虫的操作流程

爬虫模拟我们日常获取网页信息的流程,通过计算机程序自动化爬取内容。

包括三个阶段:

  • 打开网页。工具为Requests ,数据包括 HTML 页面以及 JSON 数据。
  • 爬取数据。针对HTML页面使用XPath 提取数据,针对JSON数据使用JSON 库进行解析。
  • 保存数据。可以使用Pandas 保存数据,导出为CSV 文件。

三个阶段解析

Requests

Requests有两种访问方式,Get和Post。

其中Get把参数包含在链接中。

1  

|

r = requests.get('http://www.douban.com')  
  

—|—

Post通过request body传递参数。

1  

|

r = requests.post('http://xxx.com', data = {'key', 'value'})  
  

—|—

data的数据结构是字典,通过key 和 value 对数据储存。

动态数据需要通过XHR发出HTTP请求。

Xpath

XPath 是 XML 的路径语言,帮助定位位置。

使用Xpath解析规则

在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。

表达式 描述
node 选取此节点的所有子节点
/ 从根节点选取
// 选取所有的当前节点,不考虑位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
或,两个节点的合并
text() 当前路径下的文版内容

下面用这个网站上的例子举例:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  

|

<?xml version="1.0" encoding="ISO-8859-1"?>  
  
<bookstore>  
  
<book>  
  <title lang="eng">Harry Potter</title>  
  <price>29.99</price>  
</book>  
  
<book>  
  <title lang="eng">Learning XML</title>  
  <price>39.95</price>  
</book>  
  
</bookstore>  
  

—|—

路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

xpath(’//@id’) 选取所有的 id 属性;

xpath(’//book[@id]’) 选取所有拥有名为 id 的属性的 book 元素;

xpath(’//book[@id=“abc”]’) 选取所有 book 元素,且这些 book 元素拥有 id= “abc”的属性;

xpath(’//book/title | //book/price’) 选取 book 元素的所有 title 和 price 元素。

定位到所有列表项目,需要使用lxml,下面代码为定位到HTML所有列表函数。

1  
2  
3  

|

from lxml import etree  
html = etree.HTML(html)  
result = html.xpath('//li')  
  

—|—

JSON

Json.dumps():将python对象转换成ISON对象。

Json.load():将python对象转换成JSON对象。

1  
2  
3  
4  
5  
6  
7  

|

import json  
  
jsonData = '{"a":1,"b":2,"c":3,"d":4,"e":5}';  
  
input = json.loads(jsonData)  
  
print(input)  
  

—|—

输出结果为:

1  

|

{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}  
  

—|—

项目实战:爬取葛优相关图片

步骤一:打开网页

打开豆瓣图片,输入关键词葛优

image-20200901195719609

步骤二:选择图片

在搜索结果中,可以看到网页是动态的(即往下滑可以看到更多的图片),动态数据需要通过XHR发出HTTP请求,此处需要知道JSON。

我们先来寻找XHR结构,方法是通过:1)谷歌浏览器右键单击检查;2)选择Network;3)选择XHR;4)刷新页面。从图片中可以看到共有1724张葛优相关的图片。

image-20200901200213885

从上图最下面的方框中可以看到JSON。可以看到数据被放到images里面,每张图片通过字典形式储存,元数据包含author、height、id、src、title、width等信息,有了这些信息,我们便可以提取需要的数据。

在看一下图片的组成,limit:20,最大可以显示20张,第一张图片从0开始,我们便可以写个for循环实现所有的下载。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  

|

images: [{src: "https://img3.doubanio.com/view/photo/photo/public/p399074242.jpg", author: "饭",…},…]  
0: {src: "https://img3.doubanio.com/view/photo/photo/public/p399074242.jpg", author: "饭",…}  
author: "饭"  
height: 600  
id: "399074242"  
src: "https://img3.doubanio.com/view/photo/photo/public/p399074242.jpg"  
title: "葛优"  
url: "https://www.douban.com/link2/?url=http%3A%2F%2Fwww.douban.com%2Fphotos%2Fphoto%2F399074242%2F&query=%E8%91%9B%E4%BC%98&cat_id=1025&type=search"  
width: 414  
limit: 20  
more: true  
total: 1724  
  

—|—

提取需要的信息

找到了网页及所需要的信息,接下里就是提取所需要的数据,并且进行储存。

完整代码如下:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  

|

# -*- coding:utf-8 -*-  
import requests  
import json  
query = '葛优'  
  
# 下载图片  
def download(src, id):  
  dir = './' + str(id) + '.jpg'  
  try:  
    pic = requests.get(src, timeout=10)  
    fp = open(dir, 'wb')  
    fp.write(pic.content)  
    fp.close()  
  except requests.exceptions.ConnectionError:  
    print('图片无法下载')  
              
# for 循环 请求全部的 url  
for i in range(0, 1723, 20):  
  url = 'https://www.douban.com/j/search_photo?q='+query+'&limit=20&start='+str(i)  
  html = requests.get(url).text    # 得到返回结果  
  response = json.loads(html, encoding='utf-8') # 将 JSON 格式转换成 Python 对象  
  for image in response['images']:  
    print(image['src']) # 查看当前下载的图片网址  
    download(image['src'], image['id']) # 下载一张图片  
  

—|—

参考资料

Xpath教程

ChangeLog

20201029 补充Xpath案例,仍然出现错误Expecting value: line 1 column 1 (char 0)

20200901 爬虫代码运行错误

1.初识Pandas

Pandas 是“Python的核心数据分析支持库,提供了快速、灵活、明确的数据结构,旨在简单、直观地处理关系型、标记型数据”,主要数据结构是 Series(一维数据)与 DataFrame(二维数据),这两种数据结构可以处理金融、统计、社会科学、工程等领域里的大多数典型用例。

2.数据结构

2.1 Series数据结构

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import numpy as np  
import pandas as pd  
  
s = pd.Series([1, 3, 5, np.nan, 6, 8])  
  
print(s)  
  
d = pd.Series([1, 3, 5, 7], index=['a', '1', 'c', 3])  
print(d)  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  

|

0    1.0  
1    3.0  
2    5.0  
3    NaN  
4    6.0  
5    8.0  
dtype: float64  
    
a    1  
1    3  
c    5  
3    7  
dtype: int64  
  

—|—

数据结构为Series([1, 3, 5, 6], index=[‘a’, ‘b’, ‘c’, ‘d’])。index默认结构为0、1、2、3……

从上面的运行结果可以看出,index后面的文本型结构’a’,必须加引号,否则会报错,而数字是否加引号虽不影响,单加不加引号,数据类型完全不同。

除了采用index外,还可以采用字典形式,如下:

1  
2  
3  
4  
5  
6  
7  
8  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
d = {'a':1, 'b':2, 'c':3, 'd':4}  
  
x1 = Series(d)  
  
print(x1)  
  

—|—

输出结果为:

1  
2  
3  
4  
5  

|

a    1  
b    2  
c    3  
d    4  
dtype: int64  
  

—|—

2.2 DataFrame 数据结构

DataFrame可以理解为两个Series构成的数组结构,在语法上完全相同。

1  
2  
3  
4  
5  
6  
7  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
data = {'列1':[1, 2, 3, 4], '列2':[5, 6, 7, 8], '列3':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['列1', '列2', '列3'])  
  
print(df2)  
  

—|—

输出结果为:

1  
2  
3  
4  
5  

|

       列1  列2  列3  
one     1   5   0  
two     2   6   9  
three   3   7   6  
four    4   8   8  
  

—|—

2.3 导入和导出

Pandas 可以从xlsx, csv中导入数据,也可以输出数据为xlsx, csv。

1  
2  
3  
4  
5  

|

# 写入csv  
df.to_csv('foo.csv')  
  
# 读取csv  
pd.read_csv('foo.csv')  
  

—|—

3.数据清洗

3.1 删除

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
data = {'列1':[1, 2, 3, 4], '列2':[5, 6, 7, 8], '列3':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['列1', '列2', '列3'])  
  
# 数据删除使用drop()  
df2 = df2.drop(columns=['列1'])  
df2 = df2.drop(index=['one'])  
  
print(df2)  
  

—|—

3.2 去除重复的值

1  

|

df = df.drop_duplicates() #去除重复行  
  

—|—

3.3 重命名

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
data = {'列1':[1, 2, 3, 4], '列2':[5, 6, 7, 8], '列3':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['列1', '列2', '列3'])  
  
# 重命名使用rename(columns=new_names, inplace=True) 函数  
df2.rename(index={'one':'第1', 'two':'第2'}, inplace=True)  
  
print(df2)  
  

—|—

3.4 格式相关

使用astype转换格式

1  
2  
3  
4  
5  
6  
7  
8  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
data = {'列1':[1, 2, 3, 4], '列2':[5, 6, 7, 8], '列3':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['列1', '列2', '列3'])  
  
df2['列1'].astype('str')  
df2['列1'].astype(np.int64)  
  

—|—

去除数据之间的空格strip

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
data = {'列1':[1, 2, 3, 4], '列2':[5, 6, 7, 8], '列3':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['列1', '列2', '列3'])  
  
# 删除左右两边的空格  
df2['one'] = df2['one'].map(str.strip)  
  
# 删除左边的空格  
df2['one'] = df2['one'].map(str.lstrip)  
  
# 删除右边的空格  
df2['one'] = df2['one'].map.(str.rstrip)  
  
# 删除特殊的字符  
df2['one']=df2['one'].str.strip('$')  
  

—|—

大小写转换

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
data = {'chinese':[1, 2, 3, 4], 'math':[5, 6, 7, 8], 'English':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['chinese', 'math', 'English'])  
  
# 全部大写  
df2.columns = df2.columns.str.upper()  
  
# 全部小写  
df2.columns = df2.columns.str.lower()  
  
# 首字母大写  
df2.columns = df2.columns.str.title()  
  
print(df2)  
  

—|—

1  
2  
3  
4  
5  
6  
7  
8  

|

# 下面这两行代码也可以一行代码  
data = {'chinese':[1, 2, 3, 4], 'math':[5, 6, 7, 8], 'English':[0, 9, 6, 8]}  
df2 = DataFrame(data, index=['one', 'two', 'three', 'four'], columns=['chinese', 'math', 'English'])  
  
  
df2 = pd.DataFrame({'chinese':[1, 2, 3, 4],   
                     'math':[5, 6, 7, 8],  
                    'English':[0, 9, 6, 8]},index=['one', 'two', 'three', 'four'] )  
  

—|—

查找空值

查找空值使用

1  

|

df.isnull()  
  

—|—

3.5 apply 函数对数据进行清洗

@todo

4.数据分析

Pandas常用的统计函数用法和NumPy类似(资料来源极课时间|数据分析实战45讲),如下:

img

使用describe()函数可以输出所有的统计指标

1  
2  
3  
4  
5  
6  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
df1 = DataFrame({'name':['ZhangFei', 'GuanYu', 'a', 'b', 'c'], 'data1':range(5)})  
  
print(df1.describe())  
  

—|—

输出结果如下:

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

   data1  
count  5.000000  
mean   2.000000  
std    1.581139  
min    0.000000  
25%    1.000000  
50%    2.000000  
75%    3.000000  
max    4.000000  
  

—|—

5.数据表合并

比如下面两个数据表需要合并,使用的是merge()函数。

1  
2  

|

df1 = DataFrame({'name':['One', 'two', 'a', 'b', 'c'], 'data1':range(5)})  
df2 = DataFrame({'name':['one', 'Two', 'A', 'B', 'C'], 'data2':range(5)})  
  

—|—

5.1 基于指定列进行连接

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
df1 = DataFrame({'name':['One', 'Two', 'a', 'b', 'c'], 'data1':range(5)})  
df2 = DataFrame({'name':['One', 'Two', 'A', 'B', 'C'], 'data2':range(5)})  
  
df3 = pd.merge(df1, df2, on='name')  
  
print(df3)  
  

—|—

输出结果

1  
2  
3  

|

name  data1  data2  
0  One      0      0  
1  Two      1      1  
  

—|—

5.2 inner内链接

相当于求两个DataFrame的交集。

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
df1 = DataFrame({'name':['One', 'Two', 'a', 'b', 'c'], 'data1':range(5)})  
df2 = DataFrame({'name':['One', 'Two', 'A', 'B', 'C'], 'data2':range(5)})  
  
df3 = pd.merge(df1, df2, how='inner')  
  
print(df3)  
  

—|—

输出结果为

1  
2  
3  

|

  name  data1  data2  
0  One      0      0  
1  Two      1      1  
  

—|—

5.3 outer外链接

相当于求两个 DataFrame 的并集

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
df1 = DataFrame({'name':['One', 'Two', 'a', 'b', 'c'], 'data1':range(5)})  
df2 = DataFrame({'name':['One', 'Two', 'A', 'B', 'C'], 'data2':range(5)})  
  
df3 = pd.merge(df1, df2, how='outer')  
  
print(df3)  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

 name  data1  data2  
0  One    0.0    0.0  
1  Two    1.0    1.0  
2    a    2.0    NaN  
3    b    3.0    NaN  
4    c    4.0    NaN  
5    A    NaN    2.0  
6    B    NaN    3.0  
7    C    NaN    4.0  
  

—|—

5.4 left左链接

第一个 DataFrame 为主进行的连接,第二个 DataFrame 作为补充

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
df1 = DataFrame({'name':['One', 'Two', 'a', 'b', 'c'], 'data1':range(5)})  
df2 = DataFrame({'name':['One', 'Two', 'A', 'B', 'C'], 'data2':range(5)})  
  
df3 = pd.merge(df1, df2, how='left')  
  
print(df3)  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  

|

name  data1  data2  
0  One      0    0.0  
1  Two      1    1.0  
2    a      2    NaN  
3    b      3    NaN  
4    c      4    NaN  
  

—|—

5.5 right右链接

第二个 DataFrame 为主进行的连接,第一个 DataFrame 作为补充

1  
2  
3  
4  
5  
6  
7  
8  
9  

|

import pandas as pd  
from pandas import Series,DataFrame  
  
df1 = DataFrame({'name':['One', 'Two', 'a', 'b', 'c'], 'data1':range(5)})  
df2 = DataFrame({'name':['One', 'Two', 'A', 'B', 'C'], 'data2':range(5)})  
  
df3 = pd.merge(df1, df2, how='right')  
  
print(df3)  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  

|

 name  data1  data2  
0  One    0.0      0  
1  Two    1.0      1  
2    A    NaN      2  
3    B    NaN      3  
4    C    NaN      4  
  

—|—

参考资料

Pandas中文网

极课时间|数据分析实战45讲-陈旸

ChangeLog

20201026 完成1-3.4

20201027 完成3.5-5

NumPy可以用来干什么?

在python里面已经有了列表list,为什么还要使用NumPy?

这和list与NumPy在数据储存和运算速度有关。提升内存和计算资源的利用效率。

NumPy最重要的知识包括:

  • ndarray(N-dimensional array object),解决多维数组问题,
  • ufunc(universal function object),解决对数组进行处理的函数。

ndarray

array表示数据,ndarray代表多维数组。

创建数组

1  
2  
3  
4  
5  
6  
7  
8  

|

import numpy as np  
a = np.array([1, 2, 3])  
b = np.array([[1, 1, 2], [2, 3, 5], [3, 4, 5]])  
b[1, 1]=10  
print(a.shape)  
print(b.shape)  
print(a.dtype)  
print(b)  
  

—|—

输出结果为

1  
2  
3  
4  
5  
6  

|

(3,)  
(3, 3)  
int64  
[[ 1  1  2]  
 [ 2 10  5]  
 [ 3  4  5]]  
  

—|—

总结:

  • shape用于获取数组的大小;
  • dtype用于获取数组的属性;
  • b[1, 1]表示对数组进行修改,从零开始计数,上面代码把b中的每个数组看成一个整体,当成一个数。

结构数组

创建储存每个数字单元的表格。

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  

|

import numpy as np  
# 定义数组结构  
persontype = np.dtype({  
  'names':['name', 'age', 'chinese', 'math', 'English'],  
  'formats':['S32', 'i', 'i', 'i', 'f']})  
  
peoples = np.array([('A', 1, 2, 3, 4),   
                    ('B', 4, 6, 7, 8),  
                    ('C', 0, 8, 8, 5),  
                    ('D', 3, 7, 8, 1)],  
                   dtype = persontype)  
  
ages = peoples[:]['age']  
chineses = peoples[:]['chinese']  
maths = peoples[:]['math']  
Englishs = peoples[:]['English']  
  
# 计算平均值  
print(np.mean(ages))  
print(np.mean(chineses))  
print(np.mean(maths))  
print(np.mean(Englishs))  
  

—|—

输出结果为:

1  
2  
3  
4  

|

2.0  
5.75  
6.5  
4.5  
  

—|—

ufunc

universal function的缩写。

创建连续数组

1  
2  
3  
4  
5  
6  

|

import numpy as np  
x1 = np.arange(1, 11, 2)  
x2 = np.linspace(1, 9 ,5)  
  
print(x1)  
print(x2)  
  

—|—

输出结果为:

1  
2  

|

[1 3 5 7 9]  
[1. 3. 5. 7. 9.]  
  

—|—

arange是range的内置函数,arange(1, 11, 2)分别代表初始值、终值、步长

linspaces是linear space的缩写,代表线性等分向量。linespace(1, 9,5)分别代表初始值,重值(含),元素个数。

运算

基本运算

可以进行加、剪、乘、除、n次方,取余数等运算

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

|

import numpy as np  
x1 = np.arange(1, 11, 2)  
x2 = np.linspace(1, 9 ,5)  
  
print(np.add(x1, x2))  
print(np.subtract(x1, x2))  
print(np.multiply(x1, x2))  
print(np.divide(x1, x2))  
print(np.power(x1, x2))  
print(np.remainder(x1, x2))  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  
7  

|

[ 2.  6. 10. 14. 18.]  
[0. 0. 0. 0. 0.]  
[ 1.  9. 25. 49. 81.]  
[1. 1. 1. 1. 1.]  
[1.00000000e+00 2.70000000e+01 3.12500000e+03 8.23543000e+05  
 3.87420489e+08]  
[0. 0. 0. 0. 0.]  
  

—|—

在进行计算时,需要保证数组的秩相同,即每个列表的元素个数相同,方可进行运算。

统计学相关的运算

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  

|

# -*- coding:utf-8 -*-  
  
import numpy as np  
  
a = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])  
  
print(np.amin(a)) # 最小值  
print(np.amin(a, 0)) # 每一列中的的最小值  
print(np.amin(a, 1)) # 每一行中的最小值  
print(np.amax(a)) # 最大值  
print(np.amax(a, 0)) # 每一列中的最大值  
print(np.amax(a, 1)) # 每一行的最小值  
  
# 统计最大值与最小值之差  
print(np.ptp(a))  
print(np.ptp(a, 0))  
print(np.ptp(a, 1))  
  
# 求中位数与平均数  
print(np.median(a)) # 整体列表中位数  
print(np.median(a, axis=0)) # 求每一列的中位数  
print(np.median(a, axis=1)) # 求每一行的中位数  
print(np.mean(a)) # 求整体列表的平均数  
print(np.mean(a, axis=0)) # 求每一列的平均数  
print(np.mean(a, axis=1)) # 求每一行的平均数  
  
# 求加权平均数  
a = np.array([1, 2, 3, 4])  
wts = np.array([1, 2, 3, 4])  
print(np.average(a)) # 默认权重相同  
print(np.average(a, weights=wgt)) # 赋予不同权重 (1*1=2*2+3*3+4*4)/(1+2+3+4)  
  
# 求标准差方差  
print(np.std(a)) # 标注差  
print(np.var(a)) # 方差  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  

|

1  
[1 2 3]  
[1 4 7]  
9  
[7 8 9]  
[3 6 9]  
  
8  
[6 6 6]  
[2 2 2]  
  
5.0  
[4. 5. 6.]  
[2. 5. 8.]  
5.0  
[4. 5. 6.]  
[2. 5. 8.]  
  
2.5  
3.0  
  
1.118033988749895  
1.25  
  

—|—

从行列式角度理解,axis=0表示列,axis=1表示行。

NumPy进行排序

排序在数据分析中使用频率很高,一般排序语句结构为:

1  

|

sort(a, axis=-1, kind=‘quicksort’, order=None)  
  

—|—

默认情况下axis=-1,表示对内部列表分别排序。当axis=None时,表示把所有数字按照一个列表排序。

Kind包含三种结构:quicksort、mergesort、heapsort ,分别代表快速排序、合并排序、堆排序。默认情况下,使用的是quicksort排序。

1  
2  
3  
4  
5  
6  
7  
8  

|

import numpy as np  
  
a = np.array([[14, 55, 9], [45, 56, 87], [90, 3, 45]])  
  
print(np.sort(a))  
print(np.sort(a, axis=None))  
print(np.sort(a, axis=0))  
print(np.sort(a, axis=1))  
  

—|—

输出结果为:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

|

[[ 9 14 55]  
 [45 56 87]  
 [ 3 45 90]]  
[ 3  9 14 45 45 55 56 87 90]  
[[14  3  9]  
 [45 55 45]  
 [90 56 87]]  
[[ 9 14 55]  
 [45 56 87]  
 [ 3 45 90]]  
  

—|—

参考资料

《数据分析实战45讲》-极课时间 陈旸

ChangeLog

20201025 完成基本内容

输入输出

1  
2  
3  
4  

|

name = input("What's is your name?")  
a = 1 + 1  
print('hello, %s' %name)  
print('a = %d' %a)  
  

—|—

输出结果为:

1  
2  
3  

|

What's your name?y  
hello,y  
a = 2  
  

—|—

判断语句

1  
2  
3  
4  
5  
6  
7  
8  

|

score = int(input("What's your score?"))  
if score>= 90:  
       print('Excellent')  
else:  
       if score < 60:  
           print('Fail')  
       else:  
           print('Good Job')  
  

—|—

循环语句:for … in

1  
2  
3  
4  

|

sum = 0  
for number in range(11):  
    sum = sum + number  
print(sum)  
  

—|—

输出结果为;

1  

|

55  
  

—|—

循环语句: while

1  
2  
3  
4  
5  
6  
7  

|

sum = 1  
number = 2  
while number <=100:  
  sum = sum + number  
  number = number + 1  
print(sum)  
print(number)  
  

—|—

输出结果为:

1  
2  

|

5050  
101  
  

—|—

数据类型:列表、元组、字典、集合

列表[]

1  
2  
3  
4  
5  
6  
7  

|

lists = ['a','b','c']  
lists.append('d')  
print(lists)  
print(len(lists))  
lists.insert(0,'mm')  
lists.pop()  
print(lists)  
  

—|—

输出结果为:

1  
2  
3  

|

['a', 'b', 'c', 'd']  
4  
['nm', 'a', 'b', 'c']  
  

—|—

增:append() 在尾部添加元素、使用 insert() 在列表中插入元素

删:使用 pop() 删除尾部的元素

查:len() 函数获得 lists 中元素的个数

元组 (tuple)

1  
2  
3  
4  

|

tuples = ('tupleA','tupleB')  
print(tuples[0])  
print(tuples[1])  
print(tuples[-1])  
  

—|—

输出结果为:

1  
2  
3  

|

tupleA  
tupleB  
tupleB  
  

—|—

元组和列表相同,但是初始化后不能够增加和删除元素,上面程序表示的是元素的查找。

字典 {dictionary}

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  

|

# -*- coding: utf-8 -*-  
#定义一个dictionary  
score = {'guanyu':95,'zhangfei':96}  
#添加一个元素  
score['zhaoyun'] = 98  
print(score)  
  
#删除一个元素  
score.pop('zhangfei')  
print(score)  
  
#查看key是否存在  
print('guanyu' in score)  
  
#查看一个key对应的值  
print(score.get('guanyu'))  
print(score.get('yase',99))  
  

—|—

输出结果为:

1  
2  
3  
4  
5  

|

{'guanyu': 95, 'zhangfei': 96, 'zhaoyun': 98}  
{'guanyu': 95, 'zhaoyun': 98}  
True  
95  
99  
  

—|—

增:score[‘zhaoyun’] = 98

删:score.pop()

查:是否存在用print(‘guanyu’ in score)、对应值用print(score.get(‘guanyu’))

集合:set

1  
2  
3  
4  
5  
6  
7  
8  

|

s = set(['a', 'b', 'c'])  
# 增加某个元素  
s.add('d')  
# 移除某个元素  
s.remove('b')  
print(s)  
# 查找某个元素  
print('c' in s)  
  

—|—

输出结果为:

1  
2  

|

{'d', 'a', 'c'}  
True  
  

—|—

集合和字典类似,但在增删查语法上有所区别:

增: add

删:remove

查:使用 in

函数:def

1  
2  
3  

|

def addone(score):  
   return score + 1  
print(addone(99))  
  

—|—

输出结果为:

1  

|

100  
  

—|—

注释

使用# -*- coding: utf-8 -*-,单行注释使用#,多行注释可以使用三个单引号’’’ ‘’’。

引用模块 / 包:import

引用直接使用import module_name调动即可,import本质上是路径的搜索。

针对package,可以采用from … import …,这实际是从一个目录中引用模块

参考资料

《数据分析实战45讲》-极课时间 陈旸

《程序员修炼之道》这本书,算是计算机界的畅销书了,本书的价值是向你展示了最佳实践,无论新手还是老手,阅读中都会产生共鸣。正如书的封底所言,阅读本书,你将会:

  • 与软件腐烂作斗争;
  • 避开重复知识的陷阱;
  • 编写灵活、动态、可适应的代码;
  • 防止考巧合编程;
  • 通过合约、断言及异常使你的代码“防弹”;
  • 捕捉真正的需求;
  • 无情而有效的测试;
  • 让你的用户满意;
  • 建立注重实效程序员的团队,并且通过自动化使你的开发更严谨。

以需求为导向的开发

对于一个企业,唯一关心的是如何增加收入,降低成本。企业需要的并不是程序员,而是能够帮它们完成可以增加收入、降低成本的项目的人。花了很多时间,开发出来的产品,没有市场,那么企业难以在激烈的市场竞争中存活。

面向需求的开发,是软件开发的一条基本准则。

在项目开发中,很容易出现搜集需求、编写需求文档和系统交付割裂的情况,这难以快速的响应市场需求。实际上,需求手机、设计、以及实现,是交付高质量系统的不同方面,所有的步骤并非孤立的,采用瀑布模型进行开发,耗时、灵活度不足等问题,敏捷开发的兴起,解决了瀑布模型的弊端。

以需求为导向的开发,意味着项目更加靠近问题。

无论是用于配置和控制应用程序的简单语言,还是用于指定规则或过程的更为复杂的语言,你都应该考虑让你的项目更靠近问题领域。通过在更高的抽象层面上编码,你获得了专项解决领域问题的自由,并且可以忽略琐碎的实现细节。

注重实效的开发

软件开发需要实现时间、成本、范围三者的平衡,需要作出取舍,从而保证实效。在软件开发过程中,开发易于理解和维护的唯一途径是DRY原则—“系统中的每一项只是都必须具有单一、无歧义、权威的表示”。

我们想要在编写代码的时间更少,想要在开发的早期避免错误,需要进行深思熟虑的编程,遵循以下原则:

  • 总是意识到你在做什么,不要盲目编码,试图构建你不完全理解的应用,或者你不熟悉的技术。
  • 依靠可靠的事物,不要依靠巧合和假定,如果无法说出各种特定情景的区别,就假定是最坏的。
  • 为你的假定建立文档,有助于澄清你头脑中的假定,并且有助于把它们传达给别人。
  • 不要只是测试你的代码,还要测试你的假定,编写断言测试你的假定。
  • 按照计划行事,工作进行优先级排序,把时间花在重要的方法:很又可能,它们是最难的部分。
  • 不要作历史的奴隶,不要让已有的代码支配将来的代码,如果不适用,所有的代码都可被替换。

优雅的代码,简洁、抽象程度高,当代码不满足新的运用场景时,不要犹豫对其进行重构。重构是对代码进行重写、重做和重新设计。重构的场景包括:代码违背了DRY原则(单一、无歧义、权威的表示);非正交设计;代码过时,或者需求变了,原有代码难以满足新的运用情景;需要改善性能。

选择恰当的工具

一个趁手的工具很一个不趁手的工具相比,产生的价值差距不是1倍、2倍,而是百倍、千倍。

从开发语言、编辑器到Shell等等工具。工具并非越多越好,更重要的是熟练度和长时间适用性。

印象比较深刻的是作者提到的使用纯文本写文件。纯文本是指没有对文本进行任何修饰,没有任何粗体,下划线,斜体,图形,符号或特殊字符及特殊打印格式的文本。常见的纯文本格式文件的扩展:txt、htm、asp、bat、c、bas、prg、cmd、log等。与纯文本相对的富文本格式(Rich Text Format)即RTF格式,比如word。HTML则则超文本标记语言,通过一系列标签,可以将要网络上的文字格式统一。纯文本是最简单的文件,任何编译器都可以打开编辑,兼容性极强。坏处是文本过大,访问速度过慢。

依然是首选的职业

注重实效程序员身上有一些特质,比如:

  • 好奇心重。对任何事物保持好奇,喜欢研究新的东西。

  • 独立思考。批判性思维能力强,不人云亦云,不拘泥于规则,能够打破常规寻找解决方法。

  • 接地气。能从现实角度考虑问题,平衡预算,知道工作中最重要的事情,集中力量解决。

保罗 · 格雷厄姆在《如何创造财富》一章中提到致富的方法是创立公司。当然创立公司的失败率非常高,并不适合所有人,创业公司的本质是以小团体的形式满足客户需求,在大公司工作可以理解为个体集中在一起共同满足客户需求。自然而然的,要想致富,工作应当是可测量、可放大的。

  • 可测量量。个人的回报应体现在为客户创造了多大价值,但是在大公司中个人工作无法分开测量,显然不具有可测量性。可测量性工作的理想状态是小团体及工作对组织的依赖性不强。
  • 可放大。流水线上计件工资可以测量,但是依然无法带来个人财富,因为工作性质不具有可放大性。

那么哪些行业、哪些职位的工作成果更具有上述两个特性呢?主要集中在知识密集型行业(高科技行业)、设计行业、程序员、公司高层,其本质是能以小团体形式服务客户,可借助外界资源放大该优势。

从保罗 · 格雷厄姆的回答中可以看出,程序员依然是首选的职业。虽然996、秃顶等词语是大众对程序员的刻板印象,但程序员却是少有的高薪行业,工资的起点已经是很多行业的天花板了。更重要的是,程序员使用的语言是国际化的,其信息流动性,远远高于传统行业。

End.

人均GDP不同增长阶段,一个国家农产品需求结构、农业从业人员结构、农业发展的主要矛盾及农业对经济的贡献呈现不同的特点。一般而言,人均GDP3000美元,1万美元是两个非常重要的参考指标。

农产品需求结构随人均GDP的变化规律

人口的增长或者人均收入的增加都会导致农产品需求结构发生变化。一般情况下,人口增长会导致对农产品的需求增加。随着人均收入的增加,消费的食品重点会从芋类、谷类等淀粉质食品逐渐转向畜禽等高蛋白食品。但对食品的消费量不会随着收入的增加而无限增加,当人均GDP超过1万美元时,对增加食品消费的欲望逐渐趋近于零。

上述现象可以发现食品领域的几个趋势:(1)当前农产品是相对过剩,过多的低品质农产品与人们对高品质农产品需求增长之间的矛盾;(2)人们对畜禽等高蛋白的需求会持续增加;(3)随着收入增加,食品消费中支付给加工、流通、饮食服务部门的费用将会增加,带动农产品供应链体系建设、农产品深加工、冷链、食品研发及营销创新等赛道发展。

农村劳动力结构随人均GDP的变化规律

农村劳动力结构包括:劳动力数量、劳动力质量、劳动力生产效率、劳动力收入等方面。

工业化及城市化的发展,促进农业人口向非农人口转移,农村劳动力会持续流出。农村劳动力持续流出的动力在于,非农收入大大超过农业领域的收入,如果农业领域和其他领域没有足够大的收入差距,农民不会轻易离开农业。

一项研究表明,发达国家年农村劳动力以2.2%的速度减少,但农业生产以年均1.2%的速度增加(2002年数据)。为什么劳动力减少了,农业仍然在发展?根本原因是技术的进步弥补了劳动力减少带来的影响。

经济发展水平越高,伴随农村劳动力的减少,农业的增加对技术的依赖越来越大。技术运用于农业生产需要人才及建设相应的农业技术推广体系,培养适合农业现代化发展的人才,政府及高校建立农业技术推广体系显得尤为必要。

农业领域的主要矛盾的转移规律

发展中国家工业化初期,农业发展的主要矛盾是满足粮食供给。该阶段,通过剪刀差从农业领域积累资本,此时以发展工业为工作重心。而当工业化发展到一定阶段,会面临着城乡二元结构,导致农村贫困问题进一步恶化。此时,农业政策的中心任务是如何解决农村贫困问题。当进入到工业化后期,如何实现城乡协同发展,保护农民权益变成了政策核心。

0%