Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。上次pandas基础的学习笔记主要记录pandas的数据结构和面向对象的基本数组操作,这次主要记录一下pandas进阶内容的学习。
pandas拥有特殊的Categorical类型,用于承载基于整数的类别和类别的相关数据。Categorical类型的优势在于能将数据进行分类,和之前提到的unique函数可以计算唯一值元素有点像,但Categorical类型功能更加强悍
import numpy as np
import pandas as pd
data = {
'livestock':['pig','cow','sheep','horse','cow','sheep'],
'room_id':np.arange(6),
'count':np.random.randint(5,10,size=6),
'weight':np.random.uniform(10,20,size=6)
}
arr1 = pd.DataFrame(data)
print(arr1)
print('\n')
arr1_cat = arr1['livestock'].astype('category')
print(arr1_cat)
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code5.py
livestock room_id count weight
0 pig 0 9 16.759821
1 cow 1 7 14.388205
2 sheep 2 6 10.310637
3 horse 3 8 16.575770
4 cow 4 7 16.155055
5 sheep 5 9 10.287664
0 pig
1 cow
2 sheep
3 horse
4 cow
5 sheep
Name: livestock, dtype: category
Categories (4, object): ['cow', 'horse', 'pig', 'sheep']
#Process finished with exit code 0
通过上面一个模拟养殖场牲畜统计的程序可以看出,我们利用astype(‘category’)方法将牲畜一列的数据转换为Categorical对象进行分类统计,这种操作方便用于统计重复数据。
我们也可以从其他Python序列类型直接生成pandas.Categorical,比如可以结合编码数据和类别元组,使用from_codes构造函数来搭建一个Categorical对象,Categorical对象拥有categories和codes属性
import numpy as np
import pandas as pd
fruits = ['apple','orange','banana','pear']
codes = [1,3,2,3,0,2,1,0,0,2]
arr1 = pd.Categorical.from_codes(codes,fruits)
print(arr1)
print('\n')
print(arr1.categories)
print('\n')
print(arr1.codes)
print('\n')
print(arr1.as_ordered())
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code2.py
['orange', 'pear', 'banana', 'pear', 'apple', 'banana', 'orange', 'apple', 'apple', 'banana']
Categories (4, object): ['apple', 'orange', 'banana', 'pear']
Index(['apple', 'orange', 'banana', 'pear'], dtype='object')
[1 3 2 3 0 2 1 0 0 2]
['orange', 'pear', 'banana', 'pear', 'apple', 'banana', 'orange', 'apple', 'apple', 'banana']
Categories (4, object): ['apple' < 'orange' < 'banana' < 'pear']
#Process finished with exit code 0
利用as_ordered的方法我们可以对未排序的分类实例进行排序,排序后输出的[‘apple’ < ‘orange’ < ‘banana’ < ‘pear’]表示元素的先后次序。
pandas中的某些函数,比如groupby函数与Categorical对象协同工作时性能更好,还有一些函数可以使用ordered标识。
下面的案例我们会生成一些随机数据,并使用分箱函数进行处理。在pandas中,cut和qcut函数都可以进行分箱处理操作,就像把数据包装到不同的集装箱里。其中cut函数是按照数据的值进行分割,而qcut函数则是根据数据本身的数量来对数据进行分割。
import numpy as np
import pandas as pd
arr1 = np.random.randn(1000)
print(pd.qcut(arr1,4))
print('\n')
print(pd.qcut(arr1,4,labels=['a','b','c','d']))
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code2.py
[(-0.65, 0.0158], (0.0158, 0.678], (-0.65, 0.0158], (0.678, 3.025], (0.678, 3.025], ..., (0.678, 3.025], (0.678, 3.025], (-3.51, -0.65], (-3.51, -0.65], (0.0158, 0.678]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.51, -0.65] < (-0.65, 0.0158] < (0.0158, 0.678] < (0.678, 3.025]]
['b', 'c', 'b', 'd', 'd', ..., 'd', 'd', 'a', 'a', 'c']
Length: 1000
Categories (4, object): ['a' < 'b' < 'c' < 'd']
#Process finished with exit code 0
上述程序使用qcut函数将数据进行四分位分箱,返回的四个分箱类别表示数据按数据量四等分后的四个分箱区间,通过传递labels参数可以实现对分箱区间的标签可视化,清楚地观察每个数组的区间落点。
import numpy as np
import pandas as pd
arr1 = np.random.randn(1000)
print( pd.qcut(arr1,4))
print('\n')
arr2 = pd.qcut(arr1,4,labels=['S1','S2','S3','S4'])
print(arr2)
print('\n')
arr3 = pd.Series(arr2,name='section')
print(arr3)
print('\n')
print(pd.Series(np.random.randn(1000)).groupby(arr3).agg(['count','min','max']).reset_index())
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code2.py
[(-0.738, 0.00521], (0.643, 3.61], (0.00521, 0.643], (0.643, 3.61], (-3.203, -0.738], ..., (0.00521, 0.643], (0.643, 3.61], (-3.203, -0.738], (-0.738, 0.00521], (0.643, 3.61]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.203, -0.738] < (-0.738, 0.00521] < (0.00521, 0.643] <
(0.643, 3.61]]
['S2', 'S4', 'S3', 'S4', 'S1', ..., 'S3', 'S4', 'S1', 'S2', 'S4']
Length: 1000
Categories (4, object): ['S1' < 'S2' < 'S3' < 'S4']
0 S2
1 S4
2 S3
3 S4
4 S1
..
995 S3
996 S4
997 S1
998 S2
999 S4
Name: section, Length: 1000, dtype: category
Categories (4, object): ['S1' < 'S2' < 'S3' < 'S4']
section count min max
0 S1 250 -2.890994 2.046019
1 S2 250 -2.419307 2.949340
2 S3 250 -3.240744 3.064904
3 S4 250 -2.551531 2.535125
#Process finished with exit code 0
上述程序中的arr2虽然实现了数据的等长区间分类和标签可视化,但仍然没有将各个箱体区间的具体参数汇总统计出来,于是我们利用groupby方法群组化提取数据的汇总统计值,其中包括箱体名称,箱体数据量统计,箱体区间端点值。
Series对象包含的分类数据有一些特殊的方法,这些方法类似于Series.str的特殊字符串方法,给我们提供了快捷访问类别和代码的方式。其中常用的方法cat,在使用codes属性下会返回从数字编码,使用categories属性会返回元组对象的内容
import numpy as np
import pandas as pd
arr1 = pd.Series(['apple','banana','orange','pear']*3)
cat_arr1 = arr1.astype('category')
print(cat_arr1.cat.codes)
print('\n')
print(cat_arr1.cat.categories)
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code4.py
0 0
1 1
2 2
3 3
4 0
5 1
6 2
7 3
8 0
9 1
10 2
11 3
dtype: int8
Index(['apple', 'banana', 'orange', 'pear'], dtype='object')
#Process finished with exit code 0
假设实际需要的数据类别集合超过了数据中出现的值,我们可以使用set_categories方法来改变类别,再利用value_counts方法可以呈现所有存在的数据类别,利用remove_unused_categories方法则可以去除未呈现的类别
import numpy as np
import pandas as pd
arr1 = pd.Series(['apple','banana','orange','pear']*3)
cat_arr1 = arr1.astype('category')
arr2 = ['apple','banana','orange','pear','strawberry']
cat_arr2 = cat_arr1.cat.set_categories(arr2)
print(cat_arr2)
print('\n')
print(cat_arr2.value_counts())
print('\n')
cat_arr3 = cat_arr2[cat_arr2.isin(['banana','orange','pear'])]
print(cat_arr3)
print('\n')
print(cat_arr3.cat.remove_unused_categories())
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code4.py
0 apple
1 banana
2 orange
3 pear
4 apple
5 banana
6 orange
7 pear
8 apple
9 banana
10 orange
11 pear
dtype: category
Categories (5, object): ['apple', 'banana', 'orange', 'pear', 'strawberry']
apple 3
banana 3
orange 3
pear 3
strawberry 0
dtype: int64
1 banana
2 orange
3 pear
5 banana
6 orange
7 pear
9 banana
10 orange
11 pear
dtype: category
Categories (5, object): ['apple', 'banana', 'orange', 'pear', 'strawberry']
1 banana
2 orange
3 pear
5 banana
6 orange
7 pear
9 banana
10 orange
11 pear
dtype: category
Categories (3, object): ['banana', 'orange', 'pear']
#Process finished with exit code 0
以下是Series对象的分类方法汇总
方法 | 描述 |
---|---|
add_categories | 添加新的类别到已有类别的尾部 |
as_ordered | 对类别排序 |
as_unordered | 使类别无序 |
remove_categories | 去除类别,将被移除的值置null |
remove_unused_categories | 去除没有出现在数据中的类别 |
rename_categories | 使用新的类别名称代替现有类别 |
reorder_categories | 对类别重新排序 |
set_categories | 用指定的新类别替换现有类别 |
分组操作给我们处理数据提供更高的自由度,在之前的分组操作中,我们用过apply方法来实现转换和函数调用,现在可以通过内建方法transform实现相似的效果。
import pandas as pd
arr1 = pd.DataFrame({'fruit':['apple','banana','orange']*3,
'kilograms':np.arange(9.0)})
arr1 = arr1.groupby('fruit').kilograms
print(arr1.mean())
print('\n')
print(arr1.transform(lambda x: x.mean()))
print('\n')
print(arr1.transform(lambda x: x.std()))
print('\n')
print(arr1.transform(lambda x: x.rank(ascending=False)))
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code4.py
fruit
apple 3.0
banana 4.0
orange 5.0
Name: kilograms, dtype: float64
0 3.0
1 4.0
2 5.0
3 3.0
4 4.0
5 5.0
6 3.0
7 4.0
8 5.0
Name: kilograms, dtype: float64
0 3.0
1 3.0
2 3.0
3 3.0
4 3.0
5 3.0
6 3.0
7 3.0
8 3.0
Name: kilograms, dtype: float64
0 3.0
1 3.0
2 3.0
3 2.0
4 2.0
5 2.0
6 1.0
7 1.0
8 1.0
Name: kilograms, dtype: float64
#Process finished with exit code 0
transform方法下可以定义和运用很多函数,比如上面案例中的mean函数求平均值和std函数求标准差,也可以进行降序排名,因为使用GroupBy方法后每个组就成为一个整体,所以排名后每个组的数据次序号相同。
import numpy as np
import pandas as pd
arr1 = pd.DataFrame({'fruit':['apple','banana','orange']*3,
'kilograms':np.arange(9.0)})
arr2 = arr1.groupby('fruit').kilograms
def normalize(x):
return (x-x.mean()) / x.std()
print(arr2.transform(normalize))
print('\n')
print((arr1['kilograms']-arr2.transform('mean'))/arr2.transform('std'))
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code4.py
0 -1.0
1 -1.0
2 -1.0
3 0.0
4 0.0
5 0.0
6 1.0
7 1.0
8 1.0
Name: kilograms, dtype: float64
0 -1.0
1 -1.0
2 -1.0
3 0.0
4 0.0
5 0.0
6 1.0
7 1.0
8 1.0
Name: kilograms, dtype: float64
#Process finished with exit code 0
上述案例中结合利用平均值函数和标准差函数对DataFrame对象中的元素进行计算操作的两种方式最后得到的结果都一样,后一种transform的展开式分组操作有时也更加快捷。
我们经常会遇到时间序列的数据,resample方法在语义上是一种基于时间分段的分组操作,它让我们可以利用时间作为索引操作数据
import numpy as np
import pandas as pd
arr1 = pd.DataFrame({'time':pd.date_range('2023-01-08 20:00',freq='1min',periods=9),
'value':np.arange(9.0)})
print(arr1)
print('\n')
print(arr1.set_index('time').resample('3min').sum())
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code4.py
time value
0 2023-01-08 20:00:00 0.0
1 2023-01-08 20:01:00 1.0
2 2023-01-08 20:02:00 2.0
3 2023-01-08 20:03:00 3.0
4 2023-01-08 20:04:00 4.0
5 2023-01-08 20:05:00 5.0
6 2023-01-08 20:06:00 6.0
7 2023-01-08 20:07:00 7.0
8 2023-01-08 20:08:00 8.0
value
time
2023-01-08 20:00:00 3.0
2023-01-08 20:03:00 12.0
2023-01-08 20:06:00 21.0
#Process finished with exit code 0
当我们想按照索引来对数据进行相同重新采样以实现数据的堆叠效果,我们可以使用pd.Grouper对象,然后按照时间为索引并根据’key’和’time_key’作为分组索引,然后分组聚合进行重复采样
import numpy as np
import pandas as pd
times = pd.date_range('2023-01-08 20:00',freq='1min',periods=3)
arr1 = pd.DataFrame({'time':times.repeat(6),
'value':np.arange(18.0),
'key':np.tile(['a','b','c'],6)})
time_key = pd.Grouper()
print(arr1)
print('\n')
print(arr1.groupby(['key',pd.Grouper(key='time',freq='1T')]).sum())
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code4.py
time value key
0 2023-01-08 20:00:00 0.0 a
1 2023-01-08 20:00:00 1.0 b
2 2023-01-08 20:00:00 2.0 c
3 2023-01-08 20:00:00 3.0 a
4 2023-01-08 20:00:00 4.0 b
5 2023-01-08 20:00:00 5.0 c
6 2023-01-08 20:01:00 6.0 a
7 2023-01-08 20:01:00 7.0 b
8 2023-01-08 20:01:00 8.0 c
9 2023-01-08 20:01:00 9.0 a
10 2023-01-08 20:01:00 10.0 b
11 2023-01-08 20:01:00 11.0 c
12 2023-01-08 20:02:00 12.0 a
13 2023-01-08 20:02:00 13.0 b
14 2023-01-08 20:02:00 14.0 c
15 2023-01-08 20:02:00 15.0 a
16 2023-01-08 20:02:00 16.0 b
17 2023-01-08 20:02:00 17.0 c
value
key time
a 2023-01-08 20:00:00 3.0
2023-01-08 20:01:00 15.0
2023-01-08 20:02:00 27.0
b 2023-01-08 20:00:00 5.0
2023-01-08 20:01:00 17.0
2023-01-08 20:02:00 29.0
c 2023-01-08 20:00:00 7.0
2023-01-08 20:01:00 19.0
2023-01-08 20:02:00 31.0
#Process finished with exit code 0
注意使用Grouper的一个限制是时间必须是Series和DataFrame对象的索引。
我们知道现实中的数据通常是杂乱无章的,需要大量的预处理才能使用。pandas中的pipe是一种更优雅的数据预处理方法,该方法好处就是可以链式编程,使得我们处理数据的过程更加高效集中
import numpy as np
import pandas as pd
arr1 = pd.DataFrame(np.random.randint(2,200,9).reshape(3,3))
def add_mean_n(df,n):
df2=df.applymap(lambda x:(x+n)/2)
return df2
def substract_mean_n(df,n):
df2=df.applymap(lambda x:(x-n)/2)
return df2
print(arr1.pipe(add_mean_n,6).pipe(substract_mean_n,66))
#/usr/bin/python3.8 /home/ljm/PycharmProject/pythoncode1/code2.py
0 1 2
0 17.25 -22.25 -4.00
1 -27.75 -19.25 -14.00
2 -26.50 -30.25 -20.25
#Process finished with exit code 0
pipe方法执行是就是像链条一样按照先后环节次序来对数据进行操作,pipe方法在数据预处理应用广泛,比如可以同时处理缺失值、删除重复的值、消除异常值等。
以上就是今天pandas进阶笔记的内容,本次笔记相对较短,简单介绍了pandas的进阶使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法,有些方法值得我们深入探究和应用,继续学习matplotlib库。