Skip to content

about

有了numpy,为啥还要学习pandas?

numpy已经可以帮助我们进行数据的处理了,那么学习pandas的目的是什么呢?

numpy能够帮助我们处理的是数值型的数据,当然在数据分析中除了数值型的数据还有好多其他类型的数据(字符串,时间序列),那么pandas就可以帮我们很好的处理除了数值型的其他数据!

什么是pandas?

学习pandas的重点

主要学习pandas中两个常用的类:

  • Series:是一种类似于一维数组的对象,由两个部分组成:
    • values:一组数据(ndarray类型)。
    • index:相关数据索引标签。
  • DataFrame:由Series组成的更复杂的数据结构。

Series

我们可以暂时将Series理解为是一种类似于一维(不能是多维)数组的对象,由两个部分组成:

  • values:一组数据(ndarray类型)。
  • index:相关数据索引标签。

Series的创建

列表/数组充当数据源创建Series

python
# 用前先导入
from pandas import Series
import numpy as np

# 使用列表充当数据源
s1 = Series(data=[1,2,3,'a', 'b', 'c'])
s1
"""
0    1
1    2
2    3
3    a
4    b
5    c
dtype: object
"""

# 使用数组充当数据源
s2 = Series(data=np.random.randint(0, 10, size=(3)))
s2
"""
0    8
1    3
2    6
dtype: int32
"""

# 数组只能是一维的,不能是多维的
s3 = Series(data=np.random.randint(0, 10, size=(3, 3)))
# ValueError: Data must be 1-dimensional, got ndarray of shape (3, 3) instead

显式索引和隐式索引

注意,就算有了显式的索引,但隐式索引仍然存在。

python
# 隐式索引不在多表,从索引0开始的
s1 = Series(data=[1,2,3,'a', 'b', 'c'])
s1
"""
0    1
1    2
2    3
3    a
4    b
5    c
dtype: object
"""


# 显示索引可以增强Series的可读性
# 使用index参数添加显式索引
s2 = Series(data=[1, 2, 3],index=['a','b','c'])
s2
"""
a    1
b    2
c    3
dtype: int64
"""

字典充当数据源创建Series

python
dic = {
    '语文':100,
    '数学':99,
    '理综':250
}
s = Series(data=dic)
s
"""
语文    100
数学     99
理综    250
dtype: int64
"""

Series的索引和切片

因为Serise的数据源是一维数组,所以,其索引和切片跟一维数组差不多,非常好理解。

python
s1 = Series(data=[1, 2, 3], index=['a','b','c'])

# 按照索引取值
# s1[0]  # 1

# 最后一个
# s1[2]  # 'c'


# s1[4]  # IndexError: index 4 is out of bounds for axis 0 with size 3


# 倒着取
# s1[-1], s1[-2]  # (3, 2)

# 上面这几种方式都可以,但如果你的pandas版本高的话,会有FutureWarning,说这种方式即将弃用
# 你想要索引取值,应该这么来
# s1.iloc[0], s1.iloc[2], s1.iloc[-1]  # (1, 3, 3)


# 如果有显式索引的话,也可以通过显式索引来索引取值
# s1.a, s1.b, s1.c  # (1, 2, 3)


# 全切
# s1[:]

# 按照范围切
# s1[0:3], s1[1:], s1[:2], s1[:-1]

# 加步长的切
# s1[::-1], s1[::2]

Series的常用属性

注意,如果数据源中的数据类型不统一,Series内部会转为统一类型,这个思想跟numpy一样,都是为了方便运算。

python
s = Series(data=[1,2,3,'four'],index=['a','b','c','d'])

s.shape  # 形状
s.size   # 长度
s.index  # 返回索引
s.values # 返回值  
s.dtype  # 元素的类型,数据类型O表示的是Object(字符串类型)
"""
(4,)  # 有4个元素的一维数组
4
Index(['a', 'b', 'c', 'd'], dtype='object')
array([1, 2, 3, 'four'], dtype=object)
dtype('O')
"""

Series的常用方法

python
s = Series(data=np.random.randint(60,100,size=(10,)))
s.head(3) # 显示前n个数据
s.tail(3) # 显示后n个元素
s.unique() # 去重的结果
python
s = Series(data=np.array([1, 2, np.nan], dtype=np.float32))
s
"""
0    1.0
1    2.0
2    NaN
dtype: float32
"""

s.isnull() #用于判断每一个元素是否为空,为空返回True,否则返回False
"""
0    False
1    False
2     True
dtype: bool
"""

# 用于判断每个元素是否为空,不为空的返回True,否则返回False
s.notnull()  
"""
0     True
1     True
2    False
dtype: bool
"""
python
"""
法则:索引一致的元素进行算数运算否则补空
算术运算时,可以使用运算符 + - * /
也可以使用封装好的add() sub() mul() div()这几个方法
"""
s1 = Series(data=[1,2,3],index=['a','b','c'])
s2 = Series(data=[1,2,3],index=['a','d','c'])
# s = s1 + s2
"""
a    2.0
b    NaN  # 索引不一致,为空
c    6.0
d    NaN  # 索引不一致,为空
dtype: float64
"""

# s1.add(s2)
# 对于索引位置有空缺的,一律用NaN表示
"""
a    2.0
b    NaN
c    6.0
d    NaN
dtype: float64
"""

# 对于空值可以通过fill_value进行替换
# 注意,一定是相同索引位置的进行运算
s1.add(s2, fill_value=9)
"""
a     2.0  
b    11.0  # 只有s1中有索引b,s2没有索引b,被fill_value替换为9,然后参与与s1的b索引位置的值运算
c     6.0
d    11.0
dtype: float64
"""
# 参考:https://pandas.pydata.org/docs/reference/api/pandas.Series.add.html

DataFrame

DataFrame是一个"表格型"的数据结构。

DataFrame由按一定顺序排列的多列数据组成,每一列的数据可能不同。

设计初衷是将Series的使用场景从一维拓展到多维。

DataFrame既有行索引,也有列索引。

  • 行索引:index
  • 列索引:columns
  • 值:values,numpy的二维数组。
显式索引col1col2col3
显式索引隐式索引nameageaddr
r10张开18北京
r21李开19上海
r32王开29广州
r43赵开30深圳

DataFrame的创建

可以通过numpy来创建,也可以通过字典来创建。

df对象的创建

python
# 行列都使用默认的索引
# df = DataFrame(data=np.random.randint(0, 100, size=(3, 4)))
# df

# 自定义行列索引的,你可以按需指定某一个都行
# df = DataFrame(data=np.random.randint(0, 100, size=(3, 4)), index=['i1', 'i2', 'i3'], columns=["a", "b", "c", "d"])
# df


# 字典作为数据源
dic = {
    "name": ["张开", "李开", "赵开"],
    "salary": [9999, 9998, 0]
}

# 默认的,字典的key会作为df的列索引,你可以按需指定行索引
df = DataFrame(data=dic, index=["a", "b", "c"])
df

df对象的属性

python
dic = {
    'name':['zhangsan','lisi','wanglaowu'],
    'salary':[1000,2000,3000]
}
df = DataFrame(data=dic,index=['a','b','c'])
# df.values
"""
array([['张开', 9999],
       ['李开', 9998],
       ['赵开', 0]], dtype=object)
"""
# df.columns  # Index(['name', 'salary'], dtype='object')
# df.index  # Index(['a', 'b', 'c'], dtype='object')
# df.shape  # (3, 2)

小练习来了

python
"""
根据以下考试成绩表,创建一个DataFrame,命名为df:

    张三  李四  
语文 150  25
数学 150  20
英语 150  30
理综 300  50
"""
python
dic = {
    '张三':[150,150,150,150],
    '李四':[25,20,30,50]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])
df

df对象的索引操作

python
df = DataFrame(data=np.random.randint(60,100,size=(8,4)),columns=['a','b','c','d'])
df

# 传一个值,默认是取第一列
# 如果df有显式的索引,通过索引机制去行或者列的时候只可以使用显示索引
# df[0]  # 这个肯定就报错了,列有显式索引
df["a"]  # 这个可以

# 取多列
df[['a','c']] 

"""
iloc: 通过隐式索引取行
loc: 通过显示索引取行
无论是索引还是切片的时候,用这俩方法时,一定要注意是隐式还是显式索引,不能瞎用,否则报错
"""
#取单行
df.loc[0]

#取多行
df.iloc[[0,3,5]]

#取单个元素,第几行第几列的元素
df.iloc[0, 2]
# df.iloc[0, "a"]  # 报错,因为iloc只能隐式取索引
df.loc[0, 'a']

#取多个元素,取哪几行的第几列的元素
df.iloc[[1,3,5],2]
python
df = DataFrame(data=np.random.randint(60,100,size=(8,4)),columns=['a','b','c','d'])
df

#切行,切前两行
# df[0:2]

#切行,切后两行
# df[-2:]

#切行,按需切
# df[2:5]

#切列,所有行的前两列
# df.iloc[:,0:2]

#切列,所有行的后两列
# df.iloc[:,-2:]

#切列,所有行,按需切列
# df.iloc[:,1:3]

#切列,指定行,指定列
# df.iloc[1:3,1:3]

df索引和切片操作小结:

  • 索引:

    • df[col]:取列
    • df.loc[index]:取行
    • df.iloc[[0,3,5]]:取多行
    • df.iloc[index,col]:取元素
  • 切片:

    • df[index1:index3]:切行
    • df.iloc[:,col1:col3]:切列

df对象的运算

df对象的运算规则参考Series的运算规则。

这里以算术运算为例:

python
"""
法则:索引一致的元素进行算数运算否则补空
算术运算时,可以使用运算符 + - * /
也可以使用封装好的add() sub() mul() div()这几个方法
"""
df1 = DataFrame(data=np.random.randint(0,10,size=(8,4)),columns=['a','b','c','d'])
df2 = DataFrame(data=np.random.randint(0,10,size=(8,4)),columns=['a','b','c','d'])
display(df1, df2)

# df1 + df2
df1.add(df2)

------- 继续从这开始搞运算 -------------

小练习来喽

bash
1.  假设midterm是期中考试成绩,final是期末考试成绩,
	请自由创建final,并将其与midterm相加,求期中期末平均值。
dic = {
    '张三':[52, 52, 15, 46, 22],
    '李四':[31, 24, 78, 57,  9],
    '王五':[59, 47, 90, 50, 56],
    '赵六':[27, 48, 92, 20, 71],
}

midterm = DataFrame(data=dic,index=['语文','数学','英语','理综', "政治"])
midterm

2.  假设张三期中考试数学被发现作弊,要记为0分,如何实现?
3.  李四因为举报张三作弊立功,期中考试所有科目加10分,如何实现?
4.  后来老师发现期中考试中有一道题出错了,
	为了安抚学生情绪,给每位学生每个科目都加10分,如何实现?
python
dic = {
    '张三':[52, 52, 15, 46, 22],
    '李四':[31, 24, 78, 57,  9],
    '王五':[59, 47, 90, 50, 56],
    '赵六':[27, 48, 92, 20, 71],
}
dic2 = {
    '张三':[0, 88, 19, 37, 38],
    '李四':[67, 16, 24, 11, 42],
    '王五':[33, 29, 10, 50,  6],
    '赵六':[6, 67, 59, 89, 67],
}

midterm = DataFrame(data=dic,index=['语文','数学','英语','理综', "政治"])
midterm
final = DataFrame(data=dic2,index=['语文','数学','英语','理综', "政治"])


# 1. 请自由创建final,并将其与midterm相加,求期中期末平均值。
# 思路就是每个学生的每科的期中期末加一起除以2就行了
medin = (midterm + final) / 2
medin

# 2. 假设张三期中考试数学被发现作弊,要记为0分,如何实现?
# 思路是,先根据索引定位到该数学成绩,再重新赋值
# 因为有显式索引,所以,可以用loc按照显式索引取值
midterm.loc['数学', '张三']  
# 隐式方式也行
# midterm.iloc[1, 0]
midterm.loc['数学', '张三'] = 0
midterm

# 3. 李四因为举报张三作弊立功,期中考试所有科目加10分,如何实现?
# 思路就是为李四的所有列加10操作就行了
# midterm["李四"]  # 这么着取值也行
midterm.iloc[:, 1]
midterm.iloc[:, 1] += 10
midterm

# 4. 后来老师发现期中考试中有一道题出错了,为了安抚学生情绪,给每位学生每个科目都加10分,如何实现?
# 思路就是df中的所有元素值都加10就完了
midterm += 10
midterm

时间序列

时间序列内容有很多,这里我们只讲用到的。

将字符串转为时间序列:

python
import numpy as np
import pandas as pd
from pandas import DataFrame


dic = {
    'time':['2010-10-10','2011-11-20','2020-01-10'],
    'temp':[33,31,30]
}
df = DataFrame(data=dic)
df

# 查看time列的类型
df['time'].dtype

# 将time列的数据类型转换成时间序列类型
df['time'] = pd.to_datetime(df['time'])
df


# 将time列作为源数据的行索引
# inplace参数决定是否将变更应用到原数据中
df.set_index('time')
# df.set_index('time',inplace=True)

数据清洗

Python中空值用None表示,类型是NoneType。 而numpy中空值用np.nan表示,类型是float,为什么是float类型,为了运算时保证类型统一,所以这点和Python不太一样。 np.nan的另一个特点是它和任何数进行算术运算都得空。

在pandas中,遇到None的情况,pandas会自动将None强制转为nan。

pandas处理空值操作

方式1:过滤出来空值的行,然后删除空值所在的行

相关方法:df.isnull, df.notnull, df.any, df.all,df.dropna

python