Pandas的数据结构
导入pandas:
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
1、Series
Series是一种类似于一维数组的对象,由下面两个部分组成:
-
values:一组数据(ndarray类型)
-
index:相关的数据索引标签
- sereis对象并不是一个数组,但是它只能记录一维的数据,它类似于数组
1)Series的创建
两种创建方式:
(1) 由列表或numpy数组创建
默认索引为0到N-1的整数型索引
list1=[1,2,3,4,5,6]
arr1=np.array(list1)
arr1
------------------------
array([1,2,3,4,5,6])
# seris对象,如果没有显式指定indexs,则把数组的索引设置成index
list1 = [1,2,3,4,5,6]
s1 = Series(list1)
s1
-----------------------
0 1
1 2
2 3
3 4
4 5
5 6
dtype: int64
# 使用index参数显示设置索引
s2 = Series(data=list1, index=["name","python","c","php","java","address"])
s2
----------------
name 1
python 2
c 3
php 4
java 5
address 6
dtype: int64
# series接收的data必须是一维数组
data = np.random.randint(0,10,size=(3,3)).reshape(9)
s3 = Series(data=data)
s3
-----------------
略
还可以通过设置index参数指定索引
特别地,由ndarray创建的是引用,而不是副本。对Series元素的改变也会改变原来的ndarray对象中的元素。(列表没有这种情况)
list2=[0,1,2,3,4]
array2=np.arange(5)
display(list2,array2)
--------------------
[0, 1, 2, 3, 4]
array([0, 1, 2, 3, 4])
# 使用列表构建的是副本对象
s4 = Series(data=list2)
# 使用numpy构建的是引用对象
s5 = Series(data=array2)
# 副本对象相互独立
list2[0] = 100
s4
------------------
0 0
1 1
2 2
3 3
4 4
dtype: int64
# 引用对象会互相影响
array2[0] = 100
s5
--------------
(2) 由字典创建
dic = {
"name":"lucy",
"python":100,
"java":99,
"address":"beijing"
}
# 字典本身无需,如果使用字典构建一个sereis,将得到一个有序的series,这个sereis的顺序跟字典可能不一致
s5 = Series(data=dic)
s5
--------------------------
address beijing
java 99
name lucy
python 100
dtype: object
a = np.random.randint(0,100,size=30)
np.partition(a, -2)
--------------------------
array([28, 7, 28, 0, 2, 23, 36, 64, 66, 57, 47, 53, 51, 79, 83, 84, 72,
38, 50, 71, 43, 38, 66, 41, 85, 90, 90, 93, 97, 99])
使用多种方法创建以下Series,命名为s1:
语文 150
数学 150
英语 150
理综 300
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
dic = {
"语文":150,
"数学":150,
"英语":150,
"理综":300,
}
Series(data=dic)
-----------------------
values = [150,150,150,300]
indexes = ["语文","数学","英语","理综"]
Series(data=values, index=indexes)
-------------------------
values2 = np.array([150,150,150,300])
s1 = Series(data=values2, index=indexes)
s1
--------------------
2)Series的索引和切片
可以使用中括号取单个索引(此时返回的是元素类型),或者中括号里一个列表取多个索引(此时返回的仍然是一个Series类型)。分为显示索引和隐式索引:
(1) 显式索引:
- 使用index中的元素作为索引值
- 使用.loc[](推荐)
注意,此时是闭区间
s1["语文"]
-------------
150
s1.loc["语文"]
------------------
150
# 返回一个子series对象
s1[["语文","数学"]]
----------------
语文 150
数学 150
dtype: int32
# 不安全,不推荐使用
s1[0]
------------------
150
(2) 隐式索引:
- 使用整数作为索引值
- 使用.iloc[](推荐)
注意,此时是半开区间
# 隐士索引访问
s1.iloc[0]
------------
150
# 返回一个子series对象
s1.iloc[[0,2]]
---------------
语文 150
英语 150
dtype: int32
两种访问方式
- s.loc[key]
- s.iloc[index]
切片
s2 = Series(data=np.random.randint(0,100,size=10), index=list("ABCDEFGHIJ"))
s2
# 使用显式索引切片 左闭右闭区间
s2.loc["B":"J"]
# 使用隐式索引切片 左闭右开区间
s2.iloc[0:3]
--------------
A 69
B 75
C 86
dtype: int32
3)Series的基本概念
可以把Series看成一个定长的有序字典
可以通过shape,size,index,values等得到series的属性
s1.shape
--------
(4,)
s1.index
-----------
Index(['语文', '数学', '英语', '理综'], dtype='object')
from pandas.core.indexes.base import Index
Index(data=["语文","数学"])
-----------------
Index(['语文', '数学'], dtype='object')
s1.values
-------------------
array([150, 150, 150, 300])
s1.size
---------------
4
可以使用head(),tail()分别查看前n个和后n个值
s1.head(1)
--------------
A 150
dtype: int32
s1.tail(1)
----------------
D 300
dtype: int32
当索引没有对应的值时,可能出现缺失数据显示NaN(not a number)的情况
s1 = Series(data=np.random.randint(0,10,size=5), index=list("ABCDE"))
s2 = Series(data=np.random.randint(0,10,size=5), index=list("ABCDF"))
s3 = s1 + s2
s3
--------------------------
A 15.0
B 7.0
C 8.0
D 11.0
E NaN
F NaN
dtype: float64
# NaN就是np.nan ,Not a Number
s3.loc["E"]
------------------------
nan
可以使用pd.isnull(),pd.notnull(),或自带isnull(),notnull()函数检测缺失数据
s3
--------------
A 8.0
B 12.0
C 11.0
D 14.0
E NaN
F NaN
dtype: float64
s3.isnull()
---------------
A False
B False
C False
D False
E True
F True
dtype: bool
s3.notnull()
---------------
A True
B True
C True
D True
E False
F False
dtype: bool
# 可以使用notnull函数过滤空值
s3.loc[s3.notnull()]
----------------
A 8.0
B 12.0
C 11.0
D 14.0
dtype: float64
使用Bool_list访问数组对象
Series对象本身及其实例都有一个name属性
s3.name = "Python"
s3
----------------
A 15.0
B 7.0
C 8.0
D 11.0
E NaN
F NaN
Name: Python, dtype: float64
4)Series的运算
# 一条原则,索引对齐
s1 = Series(data = np.random.randint(0,150,size=4), index=["python","java","c","php"])
s2 = Series(data = np.random.randint(0,150,size=4), index=["python","C++","php","OC"])
s1 + s2
--------------
C++ NaN
OC NaN
c NaN
java NaN
php 208.0
python 134.0
dtype: float64
s3 = Series(data = [120], index=["python"])
s1 + s3
-----------------
c NaN
java NaN
php NaN
python 199.0
dtype: float64
# 跟一个数字运算,不会出现索引对齐的问题
s1 + 10
----------------
python 89
java 65
c 101
php 122
dtype: int32
(1) 适用于numpy的数组运算也适用于Series
(2) Series之间的运算
- 在运算中自动对齐不同索引的数据
- 如果索引不对应,则补NaN
注意:要想保留所有的index,则需要使用
- add() 加
- sub() 减
- mul() 乘
- div() 除
add函数,指定fill_value参数,当需要使用空值填充时,把空值替换成fill_value的值
s1.add(s2,fill_value=0)
--------------
C++ 53.0
OC 6.0
c 91.0
java 55.0
php 134.0
python 89.0
dtype: float64
- 想一想Series运算和ndarray运算的规则有什么不同?
- 新建另一个索引包含“文综”的Series s2,并与s2进行多种算术操作。
-
相同点
numpy运算 广播机制
sereis numpy运算都支持, 索引对齐原则 -
不同点
numpy可以处理高维数据的运算
Sereis只能处理一维数据的运算
s1 = Series(data=values2, index=indexes)
s1
-----------------
语文 150
数学 150
英语 150
理综 300
dtype: int32
s2 = Series(data = values2, index=["语文","数学","英语","文综"])
s2
-------------------
同上
s1.add(s2, fill_value=0)
-------------------
C++ 63.0
OC 124.0
c 86.0
java 90.0
php 208.0
python 134.0
dtype: float64
s1.sub(s2, fill_value=0)
--------------------
C++ -63.0
OC -124.0
c 86.0
java 90.0
php -28.0
python 118.0
dtype: float64
s1.mul(s2, fill_value=1)
----------------
C++ 63.0
OC 124.0
c 86.0
java 90.0
php 10620.0
python 1008.0
dtype: float64
s1.div(s2, fill_value=1)
--------------------
C++ 0.015873
OC 0.008065
c 86.000000
java 90.000000
php 0.762712
python 15.750000
dtype: float64
2、DataFrame
DataFrame是一个【表格型】的数据结构,可以看做是【由Series组成的字典】(共用同一个索引)。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
- 行索引:index
- 列索引:columns
- 值:values(numpy的二维数组)
1)DataFrame的创建
最常用的方法是传递一个字典来创建。DataFrame以字典的键作为每一【列】的名称,以字典的值(一个数组)作为每一列。
此外,DataFrame会自动加上每一行的索引(和Series一样)。
同Series一样,若传入的列与字典的键不匹配,则相应的值为NaN
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
# 字典的key作为列标签,字典的值是每一列数据,
# 行标签如果没有显式设置,将采用默认的数组的索引
# 行标签一般都是采用默认值0~~~n
dic = {
"name":["tome","lucy","jack","rose"],
"age":[19,20,18,20],
"score":[100,90,98,90]
}
df1 = DataFrame(data=dic)
df1
--------------------
age name score
0 19 tome 100
1 20 lucy 90
2 18 jack 98
3 20 rose 90
data = np.random.randint(0,100,size=(5,3))
columns = ["python","C","java"]
index = ["tom","lucy","mery","jack","rose"]
df2 = DataFrame(data=data, columns=columns, index=index)
df2
--------------------
python C java
tom 2 51 22
lucy 19 82 6
mery 60 6 33
jack 21 74 61
rose 99 52 7
# 构建一个空的DataFrame对象,然后使用字典赋值的方式初始化
df3 = DataFrame()
# 相当于给字典赋值
df3["python"] = [190,180,290,90]
df3["java"] = [200,180,189,100]
df3
----------------------------
python java
0 190 200
1 180 180
2 290 189
3 90 100
# 直接从文件中读取一个dataFrame
# pd.read_table()
# pd.read_csv()
# io 读取的excel文件路径
# sheet_name 设置sheet的名字,或者sheet的编号
# header 把excel的第几行读取成为列标签 默认是第一行
pd.read_excel()
pd.read_excel('students.xlsx', sheet_name='Sheet2')
2)DataFrame的索引
(1) 对列进行索引
- 通过类似字典的方式
- 通过属性的方式
可以将DataFrame的列获取为一个Series。返回的Series拥有原DataFrame相同的索引,且name属性也已经设置好了,就是相应的列名。
df = DataFrame(data=np.random.randint(0,100,size=(5,5)),
columns=["C","java","php","python","OC"],
index=["lucy","jack","tom","mery","rose"])
df
-------------------------------
C java php python OC
lucy 83 86 63 33 43
jack 74 65 21 59 7
tom 64 90 79 95 25
mery 93 51 35 53 5
rose 74 83 56 12 17
# 访问一列
# 可以把DataFrame看成是一个Series的字典,key就是每一个列标签,值就是每一列数据
df["python"]
# 可以使用列表,同时读取多列数据
df[["python","C"]]
# 通过属性方式访问
df.python
(2) 对行进行索引
- 使用.ix[]来进行行索引
- 使用.loc[]加index来进行行索引
- 使用.iloc[]加整数来进行行索引
同样返回一个Series,index为原来的columns。
df1 = DataFrame(df.python)
df1
-----------------------
python
lucy 33
jack 59
tom 95
mery 53
rose 12
df1.loc["lucy"]
---------------
python 42
Name: lucy, dtype: int32
df1.loc[["lucy","rose"]]
--------------------
python
lucy 33
rose 12
df.loc[["lucy","rose"]]
--------------------------
C java php python OC
lucy 83 86 63 33 43
rose 74 83 56 12 17
# DataFrame访问
# 列访问 直接使用[]
# 行访问 使用.loc[row_key] 使用.iloc[row_index]
# 隐式访问
df.iloc[0]
# 同时提取多行
df.iloc[[0,2]]
(3) 对元素索引的方法
- 使用列索引
- 使用行索引(iloc[3,1]相当于两个参数;iloc[[3,3]] 里面的[3,3]看做一个参数)
- 使用values属性(二维numpy数组)
# 间接访问可以读取数据,但是不建议写入数据
df["python"] .loc["lucy"]
-------------------------------------
42
C java php python OC
lucy 47 24 32 42 56
jack 48 0 93 64 22
tom 82 78 15 99 51
mery 56 2 28 8 43
rose 46 25 80 71 93
# 也属于间接访问
df.loc["lucy"].loc["python"]
---------------------------
42
直接访问
df.loc[row,column]
# 显式访问
df.loc["lucy","python"]
# 隐式访问
df.iloc[0,3]
切片
【注意】 直接用中括号时:
- 索引表示的是列索引
- 切片表示的是行切片
行切片 左闭右闭区间
df.loc[“lucy”:“tom”]
---------------------------
C java php python OC
lucy 83 86 63 33 43
jack 74 65 21 59 7
tom 64 90 79 95 25
# 隐式行切片 左闭右开
df.iloc[0:3]
---------------------------
# 列切片
df.loc[:,"C":"php"]
-------------------------
C java php
lucy 83 86 63
jack 74 65 21
tom 64 90 79
mery 93 51 35
rose 74 83 56
df.loc["lucy":"jack","C":"java"]
-----------------------------------
C java
lucy 83 86
jack 74 65
# 也可以这样访问,但是不建议
df["lucy":"jack"]
总结:
访问列 df[columnname] df[[clumnname1, columnname2…]]
访问行 df.loc[indexname] df.loc[[indexname1, indexname2]]
访问元素 df.loc[indexname, columnname]
行切片 df.loc[indexname1:indexname2]
列切片 df.loc[:,columnname1:columnname2]
局部切片 df.loc[indexname1:indexname2, columnname1:columnname2]
df.python["jack"]
--------------------------
59
df.python.loc["jack"]
----------------------
59
df.python.iloc[1]
--------------------------
59
df["python"]["jack"]
df["python"].loc["jack"]
df.loc["jack"].python
--------------------------------
3)DataFrame的运算
DataFrame和一个数运算
DataFrame和一个Series运算
DataFrame和DataFrame运算
df = DataFrame(data=np.random.randint(0,10,size=(3,3)),columns=list("ABC"))
df
-------------------------
A B C
0 6 0 0
1 4 8 8
2 9 2 0
df+10
--------------------------
A B C
0 16 10 10
1 14 18 18
2 19 12 10
(1) DataFrame之间的运算
同Series一样:
- 在运算中自动对齐相同索引的数据
- 如果索引不对应,则补NaN
df1 = DataFrame(data=np.random.randint(0,10,size=(3,3)), columns=list(“ABC”),index=list(“abc”))
s1 = Series(data=[10,20,30], index=list(“abc”))
display(df1, s1)
--------------------------
A B C
a 5 2 4
b 1 3 3
c 3 8 7
a 10
b 20
c 30
dtype: int64
df1+s1
-----------------------------
A B C a b c
a NaN NaN NaN NaN NaN NaN
b NaN NaN NaN NaN NaN NaN
c NaN NaN NaN NaN NaN NaN
DataFrame和Series相加可以设置相加的方向,必须要保证在运算的方向上的索引是对齐的,不然就会自动补充nan
df1.add(s1,axis='index')
----------------------------------------
A B C
a 15 12 14
b 21 23 23
c 33 38 37
df1 = DataFrame(data=np.random.randint(0,10,size=(3,3)), columns=list("ABC"), index=list("甲乙丙"))
df2 = DataFrame(data=np.random.randint(0,10,size=(3,3)), columns=list("ABD"), index=list("甲乙丁"))
display(df1, df2)
----------------------------------------------
A B C
甲 1 1 3
乙 6 2 1
丙 4 4 1
A B D
甲 5 9 1
乙 9 4 3
丁 2 7 0
df1 + df2
--------------------------------
A B C java python
a NaN NaN NaN NaN NaN
b NaN NaN NaN NaN NaN
c NaN NaN NaN NaN NaN
jack NaN NaN NaN NaN NaN
lucy NaN NaN NaN NaN NaN
mery NaN NaN NaN NaN NaN
rose NaN NaN NaN NaN NaN
tom NaN NaN NaN NaN NaN
df1.add(df2,fill_value=0)
----------------------------------
A B C java python
a 5.0 2.0 4.0 NaN NaN
b 1.0 3.0 3.0 NaN NaN
c 3.0 8.0 7.0 NaN NaN
jack NaN NaN 74.0 61.0 21.0
lucy NaN NaN 82.0 6.0 19.0
mery NaN NaN 6.0 33.0 60.0
rose NaN NaN 52.0 7.0 99.0
tom NaN NaN 51.0 22.0 2.0
(2) Series与DataFrame之间的运算
【重要】
- 使用Python操作符:以行为单位操作(参数必须是行),对所有行都有效。(类似于numpy中二维数组与一维数组的运算,但可能出现NaN)
- 使用pandas操作函数:
axis=0:以列为单位操作(参数必须是列),对所有列都有效。
axis=1:以行为单位操作(参数必须是行),对所有行都有效。
axis=0(0 == index 行):以列为单位操作(参数必须是列),对所有列都有效。 axis=1(1 == columns 列):以行为单位操作(参数必须是行),对所有行都有效。
【注意】fill_value在df和series之间运算时,不能使用
df1
s1.index = list("ABC")
s1
------------------
A 10
B 20
C 30
dtype: int64
#fill_value在df和series之间运算时,不能使用
df1.add(s1, fill_value=0)