1.另一种形式的绘制多个图(之前是用坐标系来控制每个图的样式)
python
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 假设这里从某个数据源读取数据,为了演示方便,创建一个包含相关字段的示例DataFrame
data_dict = {
'SALARY_MILLIONS': np.random.randn(100) * 5 + 20, # 模拟球员薪水,单位百万,围绕20波动
'RPM': np.random.randn(100), # 模拟效率值,围绕0波动
'AGE': np.random.randint(20, 40, 100) # 模拟年龄,在20到40之间
}
data = pd.DataFrame(data_dict)
# 设置seaborn的面板风格
sns.set_style('darkgrid')
# 设置图形大小
plt.figure(figsize=(12, 12))
# 拆分页面,多图展示
# 绘制薪水分布及核密度图
plt.subplot(3, 1, 1)
sns.distplot(data['SALARY_MILLIONS'])
plt.xticks(np.linspace(0, 40, 9)) # 把0--40之间,分成9个间隔(包含0和40)
plt.ylabel('Salary', size=10)
# 绘制效率值分布及核密度图
plt.subplot(3, 1, 2)
sns.distplot(data['RPM'])
plt.xticks(np.linspace(-10, 10, 9))
plt.ylabel('RPM', size=10)
# 绘制年龄分布及核密度图
plt.subplot(3, 1, 3)
sns.distplot(data['AGE'])
plt.xticks(np.linspace(20, 40, 11))
plt.ylabel('AGE', size=10)
plt.show()
学习第一点:plt可以和sns结合使用,plt可以设置sns图的一些格式很全面。
学习第二点:sns.set_style('darkgrid')
设置 seaborn
的绘图风格为 darkgrid
,即深色网格背景,让图形更美观
学习第三点:plt.subplot(a,b,c)
plt.subplot(a, b, c)
是 matplotlib.pyplot
模块中用于创建和选择子图位置的函数 ,在需要在一个图形画布上绘制多个子图时非常有用,其参数含义如下:
参数解释
a
(行数) :表示将整个图形画布划分成a
行。比如a = 3
,意味着图形画布在垂直方向上会被等分成 3 行区域用于绘制子图。b
(列数) :表示将整个图形画布划分成b
列 。若b = 2
,即图形画布在水平方向上会被等分成 2 列区域。c
(子图编号) :指定当前要绘制子图的位置编号 。编号从 1 开始,按先行后列的顺序递增。例如,在3×2
的子图布局中(a = 3
,b = 2
),c = 4
代表第 2 行第 1 列的子图位置(第 1 行有 2 个子图,编号 1、2;第 2 行第 1 个就是编号 3,第 2 个是编号 4 )。
示例
python
import matplotlib.pyplot as plt
# 创建一个2行2列的子图布局,并在第1个位置绘制子图
plt.subplot(2, 2, 1)
plt.plot([1, 2, 3], [4, 5, 6])
plt.title('Subplot 1')
# 在第2个位置绘制子图
plt.subplot(2, 2, 2)
plt.scatter([1, 2, 3], [4, 5, 6])
plt.title('Subplot 2')
# 在第3个位置绘制子图
plt.subplot(2, 2, 3)
plt.bar([1, 2, 3], [4, 5, 6])
plt.title('Subplot 3')
# 在第4个位置绘制子图
plt.subplot(2, 2, 4)
plt.pie([1, 2, 3])
plt.title('Subplot 4')
plt.show()
在上述示例中:
plt.subplot(2, 2, 1)
将画布划分为 2 行 2 列的子图布局,并选中第 1 个位置绘制折线图。- 后续的
plt.subplot(2, 2, 2)
、plt.subplot(2, 2, 3)
、plt.subplot(2, 2, 4)
分别在相应位置绘制散点图、柱状图和饼图 。 - 最后
plt.show()
展示包含 4 个子图的整个图形。
通过 plt.subplot(a, b, c)
函数,能灵活地在一个图形中组织和绘制多个不同类型的子图,方便进行数据的多维度可视化展示。是不是比以前的那种方法方便了
2.agg函数:
我们知道在分组后聚合已经讲过,但是我们会发现DataFrame.groupby(by=[,])[].聚合函数,或者DataFrame[].groupby().聚合函数都只可以对一个特征进行聚合,而agg可以对多个特征进行聚合
代码 dat_grp = data.groupby(by=['TEAM'], as_index=False).agg({'SALARY_MILLIONS': np.mean, 'RPM': np.mean, 'PLAYER': np.size})
里,.agg
函数用法如下:
应用背景
.agg
函数用在 groupby
分组操作之后,用于对分组后的数据进行聚合计算。这里先按 'TEAM'
列对 data
这个 DataFrame
进行分组 ,然后使用 .agg
对每个分组内的数据进行聚合统计。
具体参数用法
- 字典形式传参 :
.agg
函数接收一个字典作为参数。字典的键是要进行聚合操作的列名,即'SALARY_MILLIONS'
、'RPM'
、'PLAYER'
,分别对应要聚合计算的薪资、效率值、球员数量相关列。 - 聚合函数指定 :字典的值是具体的聚合函数,这里
np.mean
是numpy
库中的求均值函数,对'SALARY_MILLIONS'
列和'RPM'
列分别计算分组内的均值;np.size
用于计算分组内'PLAYER'
列的元素数量(即球员数量 ) 。通过这种方式,对不同列应用了不同的聚合计算,得到每个球队分组对应的平均薪资、平均效率值和球员数量统计结果。
3.数据的筛选
dat_grp4 = data[data['TEAM'].isin(['GS', 'CLE', 'SA', 'LAC', 'OKC', 'UTAH', 'CHA', 'TOR', 'NO', 'BOS'])]
这行代码主要实现了从 DataFrame
对象 data
中筛选数据的功能 ,具体步骤如下:
data['TEAM'].isin(...)
:data['TEAM']
表示从data
这个DataFrame
中提取名为'TEAM'
的列 ,该列应该是记录球队名称等相关信息。.isin(...)
是pandas
中用于判断元素是否在指定列表中的方法 。这里传入的列表['GS', 'CLE', 'SA', 'LAC', 'OKC', 'UTAH', 'CHA', 'TOR', 'NO', 'BOS']
包含了多个球队标识。它会返回一个布尔值的Series
,其中每个元素表示对应行的'TEAM'
列值是否在给定列表中。例如,如果某行'TEAM'
列的值是'CLE'
,对应位置就是True
,否则为False
。
data[ ... ]
:- 这里是利用布尔索引来筛选
DataFrame
。将前面得到的布尔值Series
放在data
后面的方括号中,data
会根据这些布尔值筛选出对应为True
的行 。最终将筛选后的结果赋值给dat_grp4
,即dat_grp4
是一个新的DataFrame
,包含了data
中'TEAM'
列值在指定球队列表中的所有行数据。
- 这里是利用布尔索引来筛选
4.住房案例(告诉我们拿到一个数据应该怎么样进行分析)
大体流程:
1.数据获取,大概阅览数据
数据.shape/info()/describe() 注意describe只可以描述数值类型的数据
2.数据基本处理
2.1重复值与空值处理
首先重复值有一个函数去检测,并且删除:
duplicated
函数
- 函数作用 :用于检测
DataFrame
中是否存在重复行,返回一个表示重复行的布尔序列,True
代表该行是重复行,False
代表该行不是重复行 。 - 常用参数 :
subset
:列标签或标签序列,可选参数。默认情况下,duplicated
函数会基于所有列来判断行是否重复。但当指定subset
时,就仅依据指定的列来判断。比如subset=['col1', 'col2']
,则只有当col1
和col2
对应值都相同的行才会被视为重复行。keep
:取值为'first'
、'last'
或False
,默认是'first'
。'first'
表示除了第一次出现的行外,将其余重复行标记为True
。例如有三行数据,第一行和第三行重复,那么第三行对应的布尔值为True
,第一行和第二行(若不重复 )为False
。'last'
则是除了最后一次出现的行外,将其余重复行标记为True
。还是上述例子,此时第一行对应的布尔值为True
。False
会把所有重复行都标记为True
,即只要有重复情况,不管第几次出现,都标记为True
。
- 实例:
drop_duplicates
函数
- 函数作用 :用于删除
DataFrame
中的重复行,返回一个删除重复行后的新DataFrame
。 - 常用参数 :
subset
:同duplicated
函数中的subset
,指定依据哪些列来判断重复行并删除 。keep
:取值和含义也与duplicated
函数中的keep
一致,决定保留哪一个不被视为重复的行 。
空值处理之前已经讲过:判断空值类型,判断是否含有空值,空值的处理(删除,替换)可以用循坏来实现。
2.2数据类型转换
将无法处理的特征转化为方便处理的特征,大体方法是创建一个空的列表,然后循环这个要改的特征,做一些改变后加在这个列表里,最后用这个列表替换要换的特征,但是要注意对应的方法要统一,比如说创建一个空列表,那么修改方法就是列表的方法,你要创建一个空的narry,那么对narry处理的方法就是np.列表对应方法。
例子:比如说住房面积不是数值类型的而是如下,我们现在要转化为方便处理的数据:
python
import pandas as pd
import numpy as np
# 假设从文件读取数据,这里用示例数据代替
data_dict = {
"区域": ["东城", "东城", "东城", "东城", "东城"],
"小区名称": ["万国城MOMA", "北官厅胡同2号院", "和平里三区", "菊儿胡同", "交道口北二条35号院"],
"户型": ["1室0厅", "3室0厅", "1室1厅", "2室1厅", "1室1厅"],
"面积(㎡)": ["59.11㎡", "56.92㎡", "40.57㎡", "57.09㎡", "42.67㎡"],
"价格(元/月)": [10000, 6000, 6900, 8000, 5500]
}
file_data = pd.DataFrame(data_dict)
# 创建一个空数组,用于存储处理后的面积数据
data_new = np.array([])
# 取出"面积"一列数据
data = file_data['面积(㎡)'].values
# 遍历"面积"列的数据,将每个数据末尾的中文字符去除
for i in data:
data_new = np.append(data_new, np.array(i[:-2]))
# 通过astype()方法将str类型转换为float64类型
data = data_new.astype(np.float64)
# 用新的数据替换原DataFrame中"面积(㎡)"列的数据
file_data.loc[:, '面积(㎡)'] = data
print(file_data)
注意你看这个创建的空列表是array类型的,所以用的是np.append() ,里边的那个切片是把米平方去掉,最后转化为浮点数就可以。再比如
python
import pandas as pd
# 假设从文件读取数据,这里用示例数据代替
data_dict = {
"区域": ["东城", "东城", "东城", "东城", "东城"],
"小区名称": ["万国城MOMA", "北官厅胡同2号院", "和平里三区", "菊儿胡同", "交道口北二条35号院"],
"户型": ["1室0厅", "3室0厅", "1室1厅", "2室1厅", "4房间2卫"], # 模拟含特殊格式的数据
"面积(㎡)": ["59.11㎡", "56.92㎡", "40.57㎡", "57.09㎡", "42.67㎡"],
"价格(元/月)": [10000, 6000, 6900, 8000, 5500]
}
file_data = pd.DataFrame(data_dict)
# 获取"户型"列数据
housetype_data = file_data['户型']
temp_list = []
# 通过replace()方法进行替换
for i in housetype_data:
new_info = i.replace('房间', '室')
temp_list.append(new_info)
file_data.loc[:, '户型'] = temp_list
#如果想通过列名选择列并赋值,除了 loc 索引器,
#还可以使用 file_data['户型'] = temp_list
print(file_data)
这里思路也是一样,就是循环替换,你看这里因为创建的是列表,所以里面的方法就是列表的方法
3.图表分析
这一步可以根据数据进行绘图,利用sns,plt库进行绘图
学到的新知识:
1.对于创建DataFrame的理解,就是你传入字典的时候,如果值要存入多个就是用一个列表来存储
pythonimport pandas as pd # 假设从文件读取数据,这里用示例数据代替 data_dict = { "区域": ["东城", "西城", "东城", "西城", "朝阳"], "小区名称": ["万国城MOMA", "北官厅胡同2号院", "和平里三区", "菊儿胡同", "交道口北二条35号院"], "户型": ["1室0厅", "3室0厅", "1室1厅", "2室1厅", "4室2卫"], "面积(㎡)": ["59.11㎡", "56.92㎡", "40.57㎡", "57.09㎡", "42.67㎡"], "价格(元/月)": [10000, 6000, 6900, 8000, 5500] } file_data = pd.DataFrame(data_dict) # 创建一个DataFrame对象,该对象只有两列数据:区域和数量 new_df = pd.DataFrame({ '区域': file_data['区域'].unique(), '数量': [0] * len(file_data['区域'].unique()) }) print(new_df)
这里主要是看创建新数组的方法file_data['区域'].unique()的返回值就是一个数组,这个经常用来去重。[0] * len(file_data['区域'].unique()就是生成长度一样的另一列。
2.统计数量的方法(我这里有4种)
2.1就是对于很简单的数据,比如说你的区域这个特征只是单一的数据,不像每个电影的类型会有很多,区域只有一个我们可以使用groupby(by=特征).count()来统计,细节的话就是创建一个要统计的空表一列是这些要统计的,一列为数目,我们做统计后,把数目替换即可
pythonimport pandas as pd # 假设从文件读取数据,这里用示例数据代替 data_dict = { "区域": ["东城", "西城", "东城", "朝阳", "西城", "昌平", "朝阳", "海淀", "石景山", "西城", "通州", "丰台", "顺义", "大兴", "昌平", "房山", "门头沟", "亦庄开发区"], "其他列": ["value1", "value2", "value3", "value4", "value5", "value6", "value7", "value8", "value9", "value10", "value11", "value12", "value13", "value14", "value15", "value16", "value17", "value18"] } file_data = pd.DataFrame(data_dict) # 获取每个区域房源数量 area_count = file_data.groupby(by="区域").count() # 创建一个DataFrame对象,该对象只有两列数据:区域和数量 new_df = pd.DataFrame({ '区域': file_data['区域'].unique(), '数量': [0] * len(file_data['区域'].unique()) }) new_df["数量"] = area_count.values new_df = new_df.sort_values(by="数量") print(new_df)
2.2这个是我自己想的,目前没有代码,先创建一个和2.1一样的空列表用于保存最终数据,然后我们去循环原表的每一行数据,找到它对应的那个种类,比如是a类,我们就在创建的表里把那个对应的数据加1即可。(创建表.loc[a,数量].+=1)
2.3这个就是之前讲过的案例,统计电影种类数,就是创建一个大小为记录个行,种类个列的0列表,然后你去遍历原始数据对应的那个种类,比如说第78个数据种类是a,那么你就把78行的a计为1,反正就是一行就代表一个记录,对应的是否为1就表示是不是这个种类,最后你按列求和出来就是种类的个数,然后你把你创建的那个表第二列替换了就行(无论如何你都得生成最后的数据表的)
2.4看一个最难的,但是很简便的方法很巧妙:
pythonimport pandas as pd import numpy as np # 构造示例数据,模拟包含户型数据的DataFrame data_dict = { "户型": ["1室0厅", "2室1厅", "1室0厅", "3室2厅", "2室1厅"] "价格": [..............................................] } file_data = pd.DataFrame(data_dict) # 定义函数,用于计算各户型的数量 def all_house(arr): key = np.unique(arr) result = {} for k in key: mask = (arr == k) arr_new = arr[mask] v = arr_new.size result[k] = v return result # 获取户型数据 house_array = file_data['户型'] house_info = all_house(house_array) # 使用字典推导式讲房价大于50的筛选出来 house_type = dict((key, value) for key, value in house_info.items() if value > 50) #生成最终我们要的结果表格,可以看到字典里键的位置还是列表 show_houses = pd.DataFrame({'户型':[x for x in house_type.keys()], '数量':[x for x in house_type.values()]})
主要是看那个函数的作用,你把那个户型数据传进去后,首先key得到了去重的数据,它是一个列表,接下来要对这个列表里的数据统计个数,创建一个字典是为了方便的先存储对应的键值对(后面也可以看到字典很好的转化为二维数据,所以告诉我们如果要生成一个二维数据可以先方便的先存储为一个字典,再转换,因为直接存在二维数据里不方便),之后进入循环,就那一步来举例把,比如第一次循环其实就是为了直接得出对应的数量,首先你 mask = (arr == k)中arr是传进来的数据==k是在判断是否为k,结果会得到一个大小相同的布尔表,arr.对应的元素是k,就是true,反之就是false,我们用mask来承接,之后arr_new = arr[mask]就是在筛选是true的数据,如果是那么就保存下来,最后.size函数就是来计算你是true的有多少个,用v承接,最后生成键值对即可。然后就把每个不同种类的数量都得到了。注意这里 v = arr_new.size因为这里v是一维数据(Series)所以你size可以直接看数量,当然我们也可以用.shape[0]
这里就学到了一种统计数据的方法还有字典到DataFrame的转换过度
3.画图函数text,用来在图中对应的地方出现对应的文字
你比如:
pythonimport matplotlib.pyplot as plt import pandas as pd # 假设show_houses是之前已经处理好的包含户型和数量的DataFrame # 这里构造示例数据模拟show_houses data = { '户型': ['1室0厅', '1室1卫', '1室1厅', '2室1卫', '2室1厅', '2室2厅', '3室1卫', '3室1厅', '3室2厅', '4室1厅', '4室2厅'], '数量': [244, 126, 844, 120, 2249, 265, 92, 766, 489, 58, 191] } show_houses = pd.DataFrame(data) house_type = show_houses['户型'] house_type_num = show_houses['数量'] plt.barh(range(len(house_type)), house_type_num, height=0.7, color='steelblue', alpha=0.8) plt.yticks(range(len(house_type)), house_type)#设置y轴刻度名称,有中文要这样用 plt.xlim(0, 2500) # 把x轴坐标延长到2500 plt.xlabel("数量") plt.ylabel("户型种类") plt.title("北京地区各户型房屋数量") #在对应的柱子上显示数量 for x, y in enumerate(house_type_num): plt.text(y + 0.2, x - 0.1, '%s' % y) plt.show()
讲解:
pythonfor x, y in enumerate(house_type_num): plt.text(y + 0.2, x - 0.1, '%s' % y)
enumerate(house_type_num)会生成这样的一个列表
而我们每次循环的x和y不就是我们要添加的纵横坐标嘛,所以text里就是这样,但是加减就是单纯的在微调,而那个%s后面%y这是格式化输出,在text里就表示要显示的内容。
如果我们需要不从0开始,要求对应的纵坐标(比如说l这个列表),我们可以这样做:
pythonfor i, (_x, _y) in enumerate(zip(l, price)): plt.text(_x + 0.2, _y, price[i])
在
for i, (_x, _y) in enumerate(zip(l, price)):
这行代码里:
l
和price
是两个可迭代对象(这里是列表 ),zip(l, price)
将它们对应位置的元素打包成元组。假设l = [0, 1, 2]
,price = [10, 20, 30]
,zip(l, price)
会得到[(0, 10), (1, 20), (2, 30)]
。enumerate
函数用于遍历可迭代对象,同时返回元素的索引和值。在这里,通过enumerate(zip(l, price))
,循环中i
就是索引(从 0 开始 ),(_x, _y)
分别是打包后元组中的两个元素,也就是l
和price
对应位置的元素。这样在循环体中就可以方便地对每个位置上的l
和price
的元素进行操作,比如代码里使用plt.text(_x + 0.2, _y, price[i])
来在对应坐标位置添加文本标签 。4.绘制双胞胎图(就是横坐标一样但是纵坐标一个左边一个右边)
pythonimport matplotlib.pyplot as plt import pandas as pd # 构造示例数据,模拟合并后的数据df_merge data = { '数量': [100, 120, 80, 90, 110, 130, 70, 140, 105, 125, 95, 85, 115], '每平米租金(元)': [50, 60, 45, 55, 65, 70, 40, 75, 52, 62, 58, 48, 68], '区域': [f'区域_{i}' for i in range(1, 14)] } df_merge = pd.DataFrame(data) num = df_merge['数量'] # 数量 price = df_merge['每平米租金(元)'] # 价格 l = [i for i in range(13)] lx = df_merge['区域'] fig = plt.figure(figsize=(10, 8), dpi=100) # 显示折线图 ax1 = fig.add_subplot(111) ax1.plot(l, price, 'or-', label='价格') # "or-" 显示那个小红圆点 for i, (_x, _y) in enumerate(zip(l, price)): plt.text(_x, _y, price[i]) ax1.set_ylim([0, 200]) ax1.set_ylabel('价格') plt.legend(loc='upper left') # 显示条形图 ax2 = ax1.twinx() # 显示次坐标轴ax2=ax1.twinx() plt.bar(l, num, alpha=0.3, color='green', label='数量') ax2.set_ylabel('数量') plt.legend(loc="upper right") plt.xticks(l, lx) plt.show()
ax1 = fig.add_subplot(111)
在图形fig
中添加一个子图,111
表示 1 行 1 列的第 1 个子图。之后用ax1画图(这不就是我们第一次讲的吗,用坐标对象来绘制图)ax2 = ax1.twinx()
创建与ax1
共享 x 轴但有独立 y 轴的次坐标轴ax2
这样可以把两幅图画在一幅图里,而如果只是简单的用plot再次画,无法得到这个效果,注意这只是一个板块里有两个图,而不是我们讲的多个板块(用subplot(a,b,c)来绘制单独的图),
所以我们最大最细的框架就是绘制多个板块使用subplot(a,b,c),然后每个板块去绘制不同的图,可以绘制我们这个双胞胎图(但如果要绘制双胞胎图就得用这样的格式创建多个模块了,不能直接创建。),可以绘制热力图,核密度图等等我们学过的图(sns混合使用)。