yiruia的个人博客分享 http://blog.sciencenet.cn/u/yiruia

博文

Day30 数据的合并和分组聚合

已有 1602 次阅读 2020-6-5 10:20 |系统分类:科研笔记

思考:

对于一组电影数据,如果我们希望统计电影分类(genre)的情况,应该如何处理数据?

思路:重新构造一个全为0的数组,列名为分类,如果某一条数据中分类出现过,就让0变为1

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
file_path = 'filepath'
df = pd.read_csv(file_path)
print(df.head(1))
print(df['Genre'])

# 统计分类的列表
temp_list = df['Genre'].str.split(',').tolist() #[[],[],[]]

genre_list = list(set(i for i in temp_list for i in j)) #去重

#构造全为0的数组,columns是每个分类
zeros_df = pd.DataFrame(np.zeros((df.shape[0],len(genre_list))),column = genre_list)
print(zeros_df)

#给每个电影出现分类的位置赋值1
for i in range(df.shape[0]):
    # zeros_df.loc[0,['sci-fi','musical']]=1,相当于取一行多列
    zeros_df.loc[i,temp_list[i]]=1 #给出现分类的地方设为1
print(zeros_df.head(3))

# 统计每个分类电影的数量和
genre_count = zeros_df.sum(axis=0)
print(genre_count)

#排序
genre_count = genre_count.sort_values()
_x = genre_count.index
_y = genre_count.values

#画图(条形图)
plt.figure(figsize=(20,8),dpi=80)
plt.bar(range(len(_x)),_y)
plt.xticks(range(len(_x)),_x)
plt.show()

一、数据合并之join

join:默认情况下他是把行索引相同的数据合并到一起

import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.ones((2,4)),index=['A','B'],columns=list('abcd'))
print(df1)
df2 = pd.DataFrame(np.zeros((3,3)),index=list('ABC'),columns=list('xyz'))
print(df2)
print(df1.join(df2))

二、数据合并之merge

merge:按照指定的列把数据按照一定的方式合并到一起

df1 = pd.DataFrame(np.ones((2,4)),index=['A','B'],columns=list('abcd'))
print(df1)
df2 = pd.DataFrame(np.zeros((3,3)),index=list('ABC'),columns=list('xyz'))
print(df2)
df3 = pd.DataFrame(np.zeros((3,3)),columns=list('fax'))
print(df1.merge(df3,on='a'))
'''
Empty DataFrame
Columns: [a, b, c, d, f, x]
Index: []
'''
df3.loc[1,'a'] = 1
print(df3)
print(df1.merge(df3,on='a'))

默认的合并方式inner,交集

merge outer,并集,NaN补全

merge left,左边为准,NaN补全

merge right,右边为准,NaN补全

df1 = pd.DataFrame(np.ones((2,4)),index=['A','B'],columns=list('abcd'))
df3 = pd.DataFrame(np.arange(9).reshape((3,3)),columns=list('fax'))
print(df1)
print(df3)
print(df1.merge(df3,on='a'))
df1.loc['A','a'] = 100
print(df1)
print(df1.merge(df3,on='a'))
print(df1.merge(df3,on='a',how='inner')) # 内连接,取交集
print(df1.merge(df3,on='a',how='outer')) #外连接,取并集

三、数据的分组聚合功能

思考:现在我们有一组关于全球星巴克店铺的统计数据,如果我想知道美国星巴克数量和中国的哪个多,或者我想知道中国每个省份星巴克的数量情况,那么应该怎么办?

传统思路:遍历一遍,每次加1?

在pandas中类似的分组操作我们有很简单的方式完成:df.groupby(by='columns_name')

那么问题来了,调用group方法后,返回的是什么内容?答:返回一个DataFrameGroupBy对象,可以遍历,调用聚合方法

import pandas as pd
import numpy as np
file_path = 'filepath'
df = pd.read_csv(file_path)
print(df.head(1))
print(df.info())

grouped = df.groupby(by='Country')
print(grouped) #返回一个DataFrameGroupBy对象,可以进行遍历,调用聚合方法
可以进行遍历,
for i,j in grouped:
    print(i)
    print('-' * 10)
    print(j,type(j))
    print('*'*10)
==
df[df['Country']='US']

调用聚合方法

print(grouped.count()) #统计每组数据的个数
country_count = grouped['Brand'].count() #统计具体哪组数据的个数
print(country_count['US']) #统计美国星巴克数量
print(country_count['CN'])

统计每个省份的店铺数量

#统计中国每个省份的店铺数量
# china_data = df[df['Country'] == 'CN']
# grouped = china_data.groupby(by = 'State/Province').count()['Brand']
# print(grouped)

grouped = df.groupby(by='columns_name')

grouped是一个DataFrameGroupBy对象,是可迭代的,grouped中的每一个元素是一个元组,元组里面是索引(分组的值),分组之后的DataFrame

那么回到之前的问题:

要统计美国和中国的星巴克数量,我们应该怎么做?

分组之后的每个DataFrame的长度?

长度是一个思路,但是我们有更多的方法(聚合方法)来解决这个问题

要统计美国和中国的星巴克数量,我们应该怎么做?

DataFrameGroupby对象有很多经过优化的方法

函数名
说明
count
分组中非NA值的数量
sum
非NA值的和
mean
非NA值得平均值
median
非NA值的算数中位数
std、var
无偏(分组为n-1)标准差和方差
min、max
非NA值的最小值和最大值


如果我们需要对国家和省份进行分组统计,应该怎么操作呢?

grouped = df.groupby(by=[df['Country'],df['State/Province']])

很多时候,我们只希望对获取分组之后的某一部分数据,或者说我们只希望对某几列数据进行分组,这时候我们应该怎么办?

获取分组之后的某一部分数据:

df.groupby(by=['Country','State/Province'])['Country'].count() #若对df内的全部信息进行分组,不需要加df
# 对某几列数据进行分组:
df['Brand'].groupby(by=[df['Country'],df['State/Province']]).count()

观察结果,由于只选择了一列数据,所以结果是Series类型,如果我想返回DataFrame类型呢?


数据按照多个条件进行分组,返回Series类型的

df['Brand'].groupby(by = [df['Country'],df['State/Province']]).count()

df['Brand']返回结果为Series类型的一列数据,前面带有两个索引(两个分组条件),所以在进行分组时需要在某列前面加上df,df[columns],数据按照多个条件进行分组,返回DataFrame类型取列的时候,列表[]中嵌套[],即[[]]

grouped1 = df['Brand'].groupby(by = [df['Country'],df['State/Province']]).count()
==
grouped2 = df.groupby(by = [df['Country'],df['State/Province']])[['Brand']].count()
==
grouped3 = df.groupby(by = [df['Country'],df['State/Province']]).count()[['Brand']]
 #[['Brand']]的[]中又取[]表示取多列,DataFrame类型

以上两条命令结果一样,和之前的结果的区别在于当前返回的是一个DataFrame类型

那么问题来了:和之前一个分组条件相比,当前返回结果的前两列是什么?


索引和复合索引

简单的索引操作

获取index:df.index

指定index:df.index=['x','y']

重新设置index:df.reindex(list('abcdef'))

指定某一列作为index:df.set_index('Country').index.unique()


假设a为一个DataFrame,那么当a.set_index(['c','d'])即设置两个索引的时候是什么样子的结果呢?

a = pd.DataFrame({'a':range(7),'b':range(7,0,-1),'c':['one','one','one','two','two','two','two'],'d':list('hjklmno')})
print(a)
'''
   a  b    c  d
0  0  7  one  h
1  1  6  one  j
2  2  5  one  k
3  3  4  two  l
4  4  3  two  m
5  5  2  two  n
6  6  1  two  o

'''


Series的复合索引取值,直接在括号中写索引就可以了

DataFrame需要用.loc取里面的列内容

df1 = pd.DataFrame(np.ones((2,4)),index=['A','B'],columns=list('abcd'))
print(df1.index) #Index(['A', 'B'], dtype='object')
df1.index=['a','b']
print(df1.index) #Index(['a', 'b'], dtype='object')
print(df1.reindex(['a','f']))
'''
a    b    c    d
a  1.0  1.0  1.0  1.0
f  NaN  NaN  NaN  NaN
'''
print(df1.set_index('a')) #将当前数据的某一列作为索引
'''
       b    c    d
a                 
1.0  1.0  1.0  1.0
1.0  1.0  1.0  1.0
'''
print(df1.set_index('a').index) #Float64Index([1.0, 1.0], dtype='float64', name='a')
print(df1.set_index('a',drop=False)) #将a列作为索引,并且不删除a列
'''
       a    b    c    d
a                      
1.0  1.0  1.0  1.0  1.0
1.0  1.0  1.0  1.0  1.0

'''
print(df1['d'].unique()) # [1.]
print(df1['a'].unique()) #[1.]
print(df1.set_index('b').index.unique()) #Float64Index([1.0], dtype='float64', name='b')
print(df1)
'''
     a    b    c    d
a  1.0  1.0  1.0  1.0
b  1.0  1.0  1.0  1.0
'''
print(df1.set_index('a','b')) #单索引
'''
       b    c    d
a                 
1.0  1.0  1.0  1.0
1.0  1.0  1.0  1.0
'''
print(df1.set_index(['a','b']))
'''
          c    d
a   b            
1.0 1.0  1.0  1.0
    1.0  1.0  1.0
'''
print(df1.set_index(['a','b']).index) #复合索引
'''
MultiIndex([(1.0, 1.0),
            (1.0, 1.0)],
           names=['a', 'b'])
'''
a = pd.DataFrame({'a':range(7),'b':range(7,0,-1),'c':['one','one','one','two','two','two','two'],'d':list('hjklmno')})
print(a)
'''
  a  b    c  d
0  0  7  one  h
1  1  6  one  j
2  2  5  one  k
3  3  4  two  l
4  4  3  two  m
5  5  2  two  n
6  6  1  two  o
'''
b = a.set_index(['c','d'])
print(b)
'''
     a  b
c   d      
one h  0  7
    j  1  6
    k  2  5
two l  3  4
    m  4  3
    n  5  2
    o  6  1
'''
print(type(b)) # <class 'pandas.core.frame.DataFrame'>
# #想取['one']['j']的值时
print(b.loc['one'].loc['j'])
'''
a    1
b    6
Name: j, dtype: int64
'''
print(b.swaplevel().loc['h']) #从内层取索引时
'''
     a  b
c        
one  0  7
'''
c = b['a']
print(c)
print(type(c))
print(c['one'])
print(c['one']['j'])
d = a.set_index(['d','c'])['a']
print(d) #此时想取'one'分组的信息不太容易,可以将双重索引的位置交换
print(d.index)
#当我们想从内层索引取值时,就用swaplevel对双重索引的位置进行转换
print(d.swaplevel())
print(d.swaplevel()['one'])

复合索引时,当我们想从内层索引取值时,就用swaplevel对双重索引的位置进行转换



https://m.sciencenet.cn/blog-3405644-1236546.html

上一篇:Day29 DataFrame续

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-6-18 03:49

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部