目录
一、数据合并之join
二、数据合并之merge
(1)连接键 on
(2)索引连接 lef/right_index
三、应用实例
分组与聚合
调用聚合方法
问题:统计中国每个省店铺的数量。
问题:数据按照多个条件进行分组。
复合索引
修改index
重新设定index
指定某一列为index
返回index的唯一值df.index.unique()
复合索引
Demo1
Demo2
一、数据合并之join
join:默认情况下他是把行索引相同的数据合并到一起。
二、数据合并之merge
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True,
suffixes=('_x', '_y'), copy=True, indicator=False,
validate=None)
参数如下:
- left: 拼接的左侧DataFrame对象
- right: 拼接的右侧DataFrame对象
- on: 要加入的列或索引级别名称。 必须在左侧和右侧DataFrame对象中找到。 如果未传递且left_index和right_index为False,则DataFrame中的列的交集将被推断为连接键。
- left_on:左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
- right_on: 左侧DataFrame中的列或索引级别用作键。 可以是列名,索引级名称,也可以是长度等于DataFrame长度的数组。
- left_index: 如果为True,则使用左侧DataFrame中的索引(行标签)作为其连接键。 对于具有MultiIndex(分层)的DataFrame,级别数必须与右侧DataFrame中的连接键数相匹配。
- right_index: 与left_index功能相似。
- how: One of ‘left’, ‘right’, ‘outer’, ‘inner’. 默认inner。inner是取交集,outer取并集。比如left:[‘A’,‘B’,‘C’];right[’'A,‘C’,‘D’];inner取交集的话,left中出现的A会和right中出现的买一个A进行匹配拼接,如果没有是B,在right中没有匹配到,则会丢失。'outer’取并集,出现的A会进行一一匹配,没有同时出现的会将缺失的部分添加缺失值。
- sort: 按字典顺序通过连接键对结果DataFrame进行排序。 默认为True,设置为False将在很多情况下显着提高性能。
- suffixes: 用于重叠列的字符串后缀元组。 默认为(‘x’,’ y’)。
- copy: 始终从传递的DataFrame对象复制数据(默认为True),即使不需要重建索引也是如此。
- indicator:将一列添加到名为_merge的输出DataFrame,其中包含有关每行源的信息。
(1)连接键 on
df_1 = pd.DataFrame({'a':[1,2],'x':[5,6]})
df_1
df_2 = pd.DataFrame({'a':[2,1,0],'y':[6,7,8]})
df_2
pd.merge(df_1,df_2,on = 'a')
按照a这一列,从左到右进行连接
(2)索引连接 lef/right_index
可以直接按索引进行连接。
pd.merge(df_1,df_2,left_index = True,right_index = True,suffixes = ("_1","_2"))
>>>
本例中,两个表都有同名的a列,用suffixes参数设置后缀来进行区分。
三、应用实例
现在我们有一组关于全球星巴克店铺的统计数据,如果我想知道美国的星巴克数量和中国的哪个多,或者我想知道中国每个省份星巴克的数量的情况,那么应该怎么办?
数据来源:https://www.kaggle.com/starbucks/store-locations/data
查看数据的基本信息:
import pandas as pd
import numpy as np
file_path = "./starbucks_store_worldwide.csv"
df = pd.read_csv(file_path)
print(df.head(1))
>>>
<class 'pandas.core.frame.DataFrame'> RangeIndex: 25600 entries, 0 to 25599 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Brand 25600 non-null object 1 Store Number 25600 non-null object 2 Store Name 25600 non-null object 3 Ownership Type 25600 non-null object 4 Street Address 25598 non-null object 5 City 25585 non-null object 6 State/Province 25600 non-null object 7 Country 25600 non-null object 8 Postcode 24078 non-null object 9 Phone Number 18739 non-null object 10 Timezone 25600 non-null object 11 Longitude 25599 non-null float64 12 Latitude 25599 non-null float64 dtypes: float64(2), object(11) memory usage: 2.5+ MB None
分组与聚合
# 分组与聚合
grouped = df.groupby(by = "Country")
grouped
>>> <pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fc04bfd4370>
.DataFrameGroupBy
可以遍历
调用聚合方法
for i,j in grouped:
print(i)
print("-"*100)
print(j)
print("*"*100)
AD ---------------------------------------------------------------------------------------------------- Brand Store Number Store Name Ownership Type Street Address \ 0 Starbucks 47370-257954 Meritxell, 96 Licensed Av. Meritxell, 96 City State/Province Country Postcode Phone Number \ 0 Andorra la Vella 7 AD AD500 376818720 Timezone Longitude Latitude 0 GMT+1:00 Europe/Andorra 1.53 42.51 **************************************************************************************************** AE ---------------------------------------------------------------------------------------------------- Brand Store Number Store Name Ownership Type \ 1 Starbucks 22331-212325 Ajman Drive Thru Licensed 2 Starbucks 47089-256771 Dana Mall Licensed 3 Starbucks 22126-218024 Twofour 54 Licensed 4 Starbucks 17127-178586 Al Ain Tower Licensed 5 Starbucks 17688-182164 Dalma Mall, Ground Floor Licensed .. ... ... ... ... 140 Starbucks 34253-62541 Bukhatir Tower Licensed 141 Starbucks 1359-138434 Qanat Al Qasba Licensed 142 Starbucks 34259-54260 Sahara Center Licensed 143 Starbucks 34217-27108 American University of Sharjah Licensed 144 Starbucks 22697-223524 UAQ Drive Thru Licensed Street Address City State/Province Country \ 1 1 Street 69, Al Jarf Ajman AJ AE 2 Sheikh Khalifa Bin Zayed St. Ajman AJ AE 3 Al Salam Street Abu Dhabi AZ AE 4 Khaldiya Area, Abu Dhabi Island Abu Dhabi AZ AE 5 Dalma Mall, Mussafah Abu Dhabi AZ AE .. ... ... ... ... 140 Sharjah Buheira Corniche Sharjah SH AE 141 Al Taawun Road Sharjah SH AE 142 Alnahda Sharjah SH AE 143 Airport Road, Universities Complex Sharjah SH AE 144 King Faisal Highway Umm Al Quwain UQ AE
按照Country来分组,将相同Country的数据放到一起,成为一个DataFrame。
另一种方式:
df[df["Copuntry"] = "US"]
这里的length就是我们想要的结果,即美国有13608家Starbucks.
调用聚合方法
grouped.count()
grouped["Brand"].count()
>>>
country_count = grouped["Brand"].count()
print(country_count["US"])
print(country_count["CN"])
>>>
13608
2734
问题:统计中国每个省店铺的数量。
china_data = df[df["Country"] == "CN"]
grouped = china_data.groupby(by = "State/Province").count()["Brand"]
print(len(grouped.index))
print(grouped.index)
>>>
length = 31 State/Province 11 236 12 58 13 24 14 8 15 8 21 57 22 13 23 16 31 551 32 354 33 315 34 26 35 75 36 13 37 75 41 21 42 76 43 35 44 333 45 21 46 16 50 41 51 104 52 9 53 24 61 42 62 3 63 3 64 2 91 162 92 13 Name: Brand, dtype: int64
总结:
china_data = df[df["Country"] == "CN"]
grouped = china_data.groupby(by = "State/Province")
抽象为:
grouped = df.groupby(by = "columns_name")
grouped是一个DataFrameGroupBy对象,是可迭代的。
grouped中的每一个元素是一个元组。
元组里面的内容结构为:【索引(按照Country分组,对应分组的值),分组之后的DataFrame】
DataFrameGroupBy对象有很多经过优化的方法。
问题:数据按照多个条件进行分组。
根据国家和省份进行分组。
相当于两个索引,一个索引为Country,另一个为State/Province
df["Brand"].groupby(by = ["Country","State/Province"])
这个有问题吗?
不能直接按照两个同时分组。
# 正确的分组方法
grouped = df["Brand"].groupby(by = [df["Country"],df["State/Province"]]).count()
print(grouped)
>>> type:<class 'pandas.core.series.Series'>
这个结果有两个索引,Country和State/Province
但是这个不包含列名"Brand",所以是个Series类型。
需要强调的是,Series类型的数据是index+value一一对应的。不存在columns。
可以对比一下这两种
前者是df["Brand"].groupby(by = " "),最后的结果是Series类型,index+value一一对应。
后者是df[["Brand"]].groupby(by = " "),最后的结果是DataFrame类型,既有索引和Columns。
第一个就是按照这个列名去里面取,第二个取出来的结果是带着列名的。
还有另外的方法2:
grouped_1 = df.groupby(by =[df["Country"],df["State/Province"]])[["Brand"]].count()
grouped_1
>>>输出类型是DataFrame,如果要Series就去掉一个[ ]
还有另外的方法3:
当前两列都是index,因为按照两个条件进行分组的,即复合索引。
复合索引
修改index
重新设定index
df.reindex
里面传入的参数是一个list
指定某一列为index
基础语法为:df.set_index("key",drop = True/False)
里面默认为True,表示把key这一列拿去当索引,然后key就移出去了。
传入参数:drop=False
目的:我们想仍旧保留key这一列
返回index的唯一值df.index.unique()
这个unique的输出结果是一个可迭代对象,可以遍历,可以转成list、tuple都可。
查看索引的内容:
复合索引
一、如果要进行索引的数据是Series类型,可以用两种方式。
(1)df["Columns1"]["Columns2"]
(2)df.loc["Columns"].loc["Columns2"]
二、如果要进行索引的数据是DataFrame类型,就用loc来索引
需要注意的是,如下的操作是去“A”这一列,并非通过索引取值。
取完后的这个值是一个Series类型。
对于DataFrame的复合索引问题,我们可以用到loc操作。
### swap方法
Demo1
使用 matplotlib 呈现出店铺总数排名前 10 的国家
使用 matplotlib 呈现出每个中国每个城市的店铺数量
from matplotlib import font_manager
my_font = font_manager.FontProperties(fname="/System/Library/Fonts/PingFang.ttc")
_x_ = data_2.index
_y_ = data_2.values
# 画图
plt.figure(figsize = (20,12),dpi = 80)
plt.barh(range(len(_x_)),_y_)
plt.yticks(range(len(_x_)),_x_,fontproperties = my_font)
plt.show()
>>>
Demo2
现在我们有全球排名靠前的10000本书的数据,那么请统计一下下面几个问题:
1.不同年份书的数量
2.不同年份书的平均评分情况
收据来源:https://www.kaggle.com/zygmunt/goodbooks-10k
import numpy as np
import pandas as pd
file_path = './books.csv'
df = pd.read_csv(file_path)
df.head(1)
# 2.不同年份书的平均评分情况
groupedd = data_1["average_rating"].groupby(by = data_1["original_publication_year"]).mean()
print(groupedd)
__x = groupedd.index
__y = groupedd.values
plt.figure(figsize = (20,8),dpi = 80)
plt.plot(range(len(__x)),__y)
plt.xticks(list(range(len(__x)))[::10],__x[::10].astype(int),rotation=45)
plt.show()
print(__y)