pandas is a Python package providing fast, flexible, and expressive data structures designed to make working with “relational” or “labeled” data both easy and intuitive. It aims to be the fundamental high-level building block for doing practical, real world data analysis in Python. Additionally, it has the broader goal of becoming the most powerful and flexible open source data analysis / manipulation tool available in any language. It is already well on its way toward this goal.
panda 与SQL在很多地方都很相似,具体的对比可以参考该链接
PANDAS的功能
- 能够很好的处理missing value(NaN)
- 可以对二维甚至高维的数据对象进行插入和删除
- 支持将其它python数据结构简单快捷的转变为DataFrame
- 支持分组计算group by
- 支出数据重塑与数据透视表
- 支持智能的基于标签的切片,索引选取等数据操作
- 支持多个数据集的组合操作:join与merge
- 支持从多个渠道读取文本数据
- 支持时间序列time-series操作
- 支持可视化数据
文件读取
在之前的I/O章节中我们学习了使用open函数来打开文件,read函数来读取数据。但是读取进来的数据都是str的格式,非常不方便我们进行分析。 pandas提供了read_csv函数可以将文件按照固定的格式进行读取,函数能够自动解析数据类型,添加列名与索引等很多功能,能够以结构化的dataframe形式存储数据。
一些注意点:
- 尽量避免去读取excel文件,使用通用的csv或者txt格式(excel并不是全平台通用的,它是微软家的)
- 注意编码问题,使用encoding参数
- 注意处理报错行
import pandas as pd
import numpy as np
print(pandas.__version__) # 查看版本 0.24.2
读文件
# 使用read_csv方式,数据文件和代码文件位于同一目录下
df = pd.read_csv("Restaurant.csv", sep="\t")
# 查看数据文件的头部,默认输出前五行数据
df.head()
# 使用read_excel方式
movie = pd.read_excel("movie.xlsx", sheet_name=0) # 不推荐
df.head()
写文件
# index表示是否要保存索引
df.to_csv("Restaurant_copy.csv", sep="\t", index=False)
DataFrame 与 Series
Series的创建
Series is a one-dimensional labeled array capable of holding any data type (integers, strings, floating point numbers, Python objects, etc.). The axis labels are collectively referred to as the index.
Series是一维的、带标签的、能够存储任意数据类型、的数组
s = pd.Series(data, index=index)
Here, data can be many different things:
- a Python dict
- an ndarray (like list)
- a scalar value (like 5)
The passed index is a list of axis labels. Thus, this separates into a few cases depending on what data is:
# From dict
pd.Series({"name":"jeff", "age":18})
# from array
pd.Series([1,2], index=list("ab"))
# From scala
pd.Series(5, index=list("abcdef"))
DataFrame的创建
DataFrame is a 2-dimensional labeled data structure with columns of potentially different types. You can think of it like a spreadsheet or SQL table, or a dict of Series objects. It is generally the most commonly used pandas object. Like Series, DataFrame accepts many different kinds of input:
DataFrame是二维的、带标签的、不同类型列的、数据结构。你可以看作类似于EXCEL工作簿或者SQL表或者是由Series组成的字典。
DataFrame由一个或者多个Series组成,DataFrame的一行或者一列就是一个Series。
pd.Dataframe(data, index=index, columns=columns)
Here, data can be many different things:
- Dict of 1D ndarrays, lists, dicts, or Series
- 2-D numpy.ndarray
- Structured or record ndarray
- A Series
- Another DataFrame
Along with the data, you can optionally pass index (row labels) and columns (column labels) arguments. If you pass an index and / or columns, you are guaranteeing the index and / or columns of the resulting DataFrame.
# 通过字典方式创建
df = pd.DataFrame({"name":["Jeff", "Tom", "Peter"], "age": [24, 25, 20], "height": [180, 165, 179]})
df
执行
name | age | height | |
---|---|---|---|
0 | Jeff | 24 | 180 |
1 | Tom | 25 | 165 |
2 | Peter | 20 | 179 |
# 通过二维数组方式创建
pd.DataFrame([[1,2,3], [4,5,6]], index=list("ab"), columns=list("fgh"))
# 通过Series方式创建
s1 = pd.Series({'name': 'jeff', 'age': 25})
pd.DataFrame(s1, columns=['V'])
pd.DataFrame(df['name']) # DataFrame每一列每一行都是一个Series
pd.DataFrame(df.name)
常用操作
Pandas对DataFrame与Series提供了丰富的操作方法,在此列出最为常用的一些。
查看属性
- columns
- index
- dtypes
- shape
- size
df.columns # Index(['name', 'age', 'height'], dtype='object')
df.index # RangeIndex(start=0, stop=3, step=1)
df.dtypes # 返回每一列的数据类型
df.shape # (3, 3) # 返回元祖类型
df.size # 9
len(df) # 3
df.shape[1] # 3
方法使用
- unique()
- value_counts()
- max/min/sum/mean
- describe
- head
- tail
- rename
- replace
- sort_values
- apply
s1.unique() # array(['jeff', 25], dtype=object)
s1.nunique() # 2 # 返回不同值的个数
s1.value_counts() # 按照value值统计数量
# 常见的数学运算
df['height'].max() # 180 # min/max/sum/mean
df['height'].describe() # 统计某一列的最大值,最小值,百分位数,平均数,标准差
df.head(2)
df.tail(4)
# 重名了index和column,并且使它在原数据上生效
df.rename(index={0: 'Hello', 1: 'World'}, columns={"name":"NAME","age":"AGE"}, inplace=True)
# 将NAME这一列的Tom替换成Rehan,并且在原数据上生效
df.replace({"NAME": {'Tom': 'Rehan'}}, inplace=True)
# 将整个DataFrame中的数字165替换成185,并且在原数据上生效
df.replace({165: 185}, inplace=True)
执行看下结果
NAME | AGE | height | |
---|---|---|---|
Hello | Jeff | 24 | 180 |
World | Rehan | 25 | 185 |
2 | Peter | 20 | 179 |
# 依照height和AGE这两列的数据降序排列,之后我们会学习sort_index函数
df.sort_values(by=["height", "AGE"], ascending=False)
这里着重介绍下apply函数,其中有个参数叫axis(轴),返回的数据类型为Series。
# 将每个数字加1了
pd.Series([1,2,3]).apply(lambda s: s + 1)
# 生成一个3行4列的二维数组
d = np.array(range(12)).reshape(3, 4)
dd = pd.DataFrame(d, columns=["a", "b", "c", "d"])
# 纵向 每一列被当成一个Series传入
dd.apply(lambda s: s[0] + 10000, axis=0) # 10000 10001 10002 10003
# 横向 每一行被当成一个Series传入
dd.apply(lambda s: s[0] + 10000, axis=1) # 10000 10004 10008
a | b | c | d | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
axis为0,表示index,纵向计算
axis为1,表示column,横向计算
dd.max(axis=0) # 8 9 10 11 # 每一列的最大值
dd.max(axis=1) # 3 7 11 # 每一行的最大值
数据选取/添加/删除
示例
这里简单介绍一下数据的基本操作,更加灵活的操作在介绍loc和iloc时会详细说明
# 列数据的选取
df = pd.DataFrame({"name":["Jeff", "Tom", "Peter"], "age": [24, 25, 20], "height": [180, 165, 179]})
df['name'] # 选取一列
df[['name', 'age']] # 选取多列
df.name # 不推荐这种写法
# 增加一列
df["class"] = 1
执行查看结果
name | age | height | class | |
---|---|---|---|---|
0 | Jeff | 24 | 180 | 1 |
1 | Tom | 25 | 165 | 1 |
2 | Peter | 20 | 179 | 1 |
# 删除某一列
del df["class"]
条件筛选
a = (df["age"] > 22) & (df["age"] < 25)
print(a) # True False False(布尔型的Series)
# 下面这两条语句的执行结果是相同的
df[df["age"] == 24]
df[(df["age"] > 22) & (df["age"] < 25)]
执行看下结果
name | age | height | |
---|---|---|---|
0 | Jeff | 24 | 180 |
Missing value
pandas使用numpy.nan来代表缺失值。缺失值不代表没有值,它本身就是某种类型的值。PYTHON中一般用None代表没有值,这与nan是两回事。缺失值不会被程序计算。处理的方式:
- 删除含有缺失值的行
- 填充缺失值
检测缺失值
这些常用方法,返回的都是布尔值
- isna
- notna
- isnull
- notnull
import numpy as np
a = pd.Series([1,3,np.nan,10,20])
pd.isna(a) # False False True False False
pd.notna(a) # True True False True True
pd.isnull(a) # False False True False False
pd.notnull(a) # True True False True True
# 注意:也可以这样用,这些方法对于Series和DataFrame是通用的
a.isna()
df['name'].isna()
df.isna() # 返回的是布尔型的二维数组
删除与填充
dropna函数,着重说明一下几个重要的参数。
axis为0时,按行删除,axis为1时,按列删除;
how为any时,表示该行/列只要有一个nan值,则删除;为all时,该行/列必须全部是nan值,才删除;
subset子集,可以用它来限定我们选定哪几行,或者哪几列,而不是应用到整个DataFrame;(axis=0时,subset指定列的集合,axis=1时,subset指定行的集合)
df_test = pd.DataFrame([[np.nan, 2, np.nan, 0], [3, 4, np.nan, 1],
[np.nan, np.nan, np.nan, 5]],
columns=list('ABCD'))
df_test.dropna(axis = 0)
df_test.dropna(axis = 1)
df_test.dropna(axis = 1,how = "all")
df_test.dropna(axis = 0,subset=['B'])
fillna函数,着重说明一下几个重要的参数。
axis为0时,按行来填充,axis为1时,按列来填充;
method参数,取值 bfill / backfill 时,用后一个值填充,取值ffill / pad 时,用前一个值填充
value参数,表示具体要填充的值
df.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
# 表示age列的缺失值填充为18,name列的缺失值填充为Jeff
df.fillna({"age": 18, "name": "Jeff"})
Missing value的计算
要千万注意,缺失值是不参与计算的,所以清洗数据要仔细严谨一些
None == None # True
np.nan == np.nan # False
np.nan + 3 # nan
文本数据
DataFrame与Series中经常有文本格式的数据存在,pandas提供了良好的工具用来处理这些文本
str方法
str方法返回的对象才能够调用字符串处理函数
# 基本用法
a = pd.Series([' ABC', 'a ', 'xxx', 'fg'])
a.str.endswith('a') # False False False False
# 链式操作,原因在于每次计算结果返回的都是Series对象
a.str.strip().str.endswith('a') # False True False False
# 去除index或者column名称前后的空格(很常见的一种用法)
df.columns = df.columns.str.strip()
# 注意,如果是应用到df.index,要确保index为字符串类型,否则会报错
# 截取/提取字符串,使用[]对字符串的位置进行索引选取
a = a.str.strip()
# 作用到了每一个字符串上
a.str[:1] # A a x f
Splitting and Replacing String
split方法用于根据某个分隔符对字符进行分割,返回一个列表
# 为了做分割练习,我们先修改下原数据
df['name'] = df['name'] + ' Wang'
执行结果
name | age | height | |
---|---|---|---|
0 | Jeff Wang | 24 | 180 |
1 | Tom Wang | 25 | 165 |
2 | Peter Wang | 20 | 179 |
# 注意:这里的split返回的也是Series对象
df['name'].str.split(' ') # [Jeff, Wang], [Tom, Wang], [Peter, Wang]
# 使用get去获取分割后的第一个元素
df['name'].str.split(' ').str.get(0) # Jeff Tom Peter
# 将name这一列分割成姓和名两列,结果是DataFrame类型了
df['name'].str.split(' ', expand=True)
上面单元格第三行代码的执行结果
0 | 1 | |
---|---|---|
0 | Jeff | Wang |
1 | Tom | Wang |
2 | Peter | Wang |
# 获取分割后的第1列元素,并赋值给first name列
df['first name'] = df['name'].str.split(' ', expand=True)[0]
df
执行结果
name | age | height | first name | |
---|---|---|---|---|
0 | Jeff Wang | 24 | 180 | Jeff |
1 | Tom Wang | 25 | 165 | Tom |
2 | Peter Wang | 20 | 179 | Peter |
replace同Python中replace的用法大致相同,就不详细举例了
分类数据
分类数据对应的是统计学中的分类变量:拥有一些有限的值。比如说性别,iphone的类型(i5, i6, i7)等等。 分类的数据可以有序列性属性(即排序),但是不支持数值类型的操作。
常用的场景如下:
- 将一列拥有不是很多值的字符串变量转换成分类变量,可以节省内存
- 分类变量可以让数据有逻辑排序而不是词汇(词典)的排序,比如 one/two/three
- 和其它Python库交互时,它会被当做分类变量处理
创建分类特征
# 1. 创建Series时指定dtype
s = pd.Series(['i5', 'i6', 'i7', 'i5'], dtype='category')
# 2. astype强转
s = pd.Series(['excellent', 'great', 'good', 'excellent'])
s.astype('category')
df_test = pd.DataFrame({'A': ['a', 1, 2, 'a', 1, 'a']})
df_test['B'] = df_test['A'].astype('category')
# 3. 使用pd.Categorical创建
raw_cat = pd.Categorical(values=['apple', 'banana', 'apple', 'fruit', 'fruit', 'fruit'],
categories=['apple', 'banana'], ordered=True)
print(raw_cat)
# 在赋值给DataFrame中的列时,要注意数量是要匹配的
df_test['C'] = raw_cat
执行看一下输出
[apple, banana, apple, NaN, NaN, NaN]
Categories (2, object): [apple < banana]
这里有几个要注意的点
categories指定要把哪些值设为分类值,而ordered为True时,则按照categories中给的顺序排序
不在categories中的变量会被置为NaN
# 4. 创建DataFrame时指定dtype,批量地将列转换成了分类数据
df_ = pd.DataFrame({"a": list("1234"), "b": list("5678")}, dtype='category')
控制分类值行为:CategoricalDtype
注意:前面创建分类数据的四种方式中,只有第三种方式显示的指明了有哪些类别,并且设置了排序;其余三种方式,都是由程序自动推断有哪些类别的,(也就是将数据中所有的唯一值都视为一种类别),也没有排序
“category”默认有两种分类值的行为:
- 类别从数据中自行推断
- 没有排序的性质
下面我们通过CategoricalDtype来控制分类值的这两类行为
categories参数,一个唯一值的序列并且不能有缺失值
ordered参数,是否排序,布尔类型
from pandas.api.types import CategoricalDtype
t = CategoricalDtype(categories=['b', 'a'], ordered=True)
pd.Series(['a', 'b', 'a', 'c'], dtype=t)
pd.Series(['a', 'b', 'a', 'c']).astype(t)
执行
0 a
1 b
2 a
3 NaN
dtype: category
Categories (2, object): [b < a]
注意:不在类别当中的数据会被视为NaN
分类值的描述
我们可以通过describe方法来查看分类数据的一些统计,比如count / unique / top / freq
先看一下我们之前的测试数据集
df_test['B']
执行
0 a
1 1
2 2
3 a
4 1
5 a
Name: B, dtype: category
Categories (3, object): [1, 2, a]
我们想看下该列数据调用describe方法的情况
df_test['B'].describe()
执行
count 6
unique 3
top a
freq 3
Name: B, dtype: object
处理分类变量(属性和方法)
分类数据拥有分类与排序(唯二性)的特征:包含了分类值展示以及值之间是否是有序的。
我们可以通过 s.cat.categories 与 s.cat.ordered (这里的s是Series类型)进行操作。如果你没有人为的设定类别与排序信息,它们会被自动推断
# 返回分类类型
df_test['B'].cat.categories # Index([1, 2, 'a'], dtype='object')
# 是否排序
df_test['B'].cat.ordered # False
注意:categories 和方法 unique()返回的值可能是不一样的
from pandas.api.types import CategoricalDtype
s = pd.Series(list('babc')).astype(CategoricalDtype(list('abcd')))
s.cat.categories # 有a, b, c, d四个类别
s.unique() # 只有b, a, c三个不同值
下面是一些分类变量涉及到的方法,增加类别、删除类别、移除无用类别、重新设置类别
单元格中的每行代码可逐行执行,查看结果
要注意的一点是:当添加已经存在的类别时会报错;删除不存在的类别时也会报错;(未更改源数据)
# 添加
df_test['B'].cat.add_categories(['b'])
# 删除
df_test['B'].cat.remove_categories(['a', 'b'])
# 移除没用的类别值
df_test['B'].cat.remove_unused_categories()
# 重新设定类别值,赋值的原因是我们后面会再次用到
df_test['B'] = df['B'].cat.set_categories([1, 2, 'a', 'b'], ordered=True)
严格意义上讲min和max并不算是数学运算,它只是获取了有序列表的头部和尾部
# 和我们前面给定的排序顺序一致
df_test['B'].min() # 1
df_test['B'].max() # a
df_test['B'].mean() # 报错,分类变量不参与数学运算