Pandas的数据结构
导入pandas:
import pandas as pd
from pandas import Series,DataFrame
import numpy as np
Series
Series是一种类似与一维数组的对象,由下面两个部分组成:
- values:一组数据(ndarray类型)
- index:相关的数据索引标签
Series(
data=None,
index=None,
dtype=None,
name=None,
copy=False,
fastpath=False,
)
Series的创建
由列表或numpy数组创建
- 默认索引为0到N-1的整数型索引
# 使用列表创建Series
Series(data=[1,2,3,])
0 1
1 2
2 3
dtype: int64
Series(data=np.random.randint(0,100,size=(3)))
0 93
1 76
2 65
dtype: int32
- 可以通过设置index参数指定索引
Series(data=[1,2,3],index=['a','b','c']) #显式索引
a 1
b 2
c 3
dtype: int64
============================================
练习1:
使用多种方法创建以下Series,命名为s1:
语文 150
数学 150
英语 150
理综 300
============================================
Series的索引和切片
可以使用中括号取单个索引(此时返回的是元素类型),或者中括号里一个列表取多个索引(此时返回的是一个Series类型)。
(1) 显式索引:
使用index中的元素作为索引值
使用
s.loc[]
(推荐):注意,loc中括号中放置的一定是显示索引注意,此时是闭区间
s = Series([1,2,3,4],index=['a','b','c','d'])
s
a 1
b 2
c 3
d 4
dtype: int64
s[1] # 2,隐式索引:
s['a'] # 1
s.a # 1 显示索引
隐式索引:
使用整数作为索引值
使用.iloc[](推荐):iloc中的中括号中必须放置隐式索引
注意,此时是半开区间
s[1:3] # 隐式切片
s['a':'c'] # 显示切片
a 1
b 2
c 3
dtype: int64
Series的基本概念
可以使用s.head(),tail()分别查看前n个和后n个值
s.head(2)
a 1
b 2
dtype: int64
对Series元素进行去重
s = Series([1,1,1,2,3,3,3,3,2])
s.unique()
array([1, 2, 3], dtype=int64)
使得两个Series进行相加。
当索引没有对应的值时,可能出现缺失数据显示NaN(not a number)的情况
s1 = Series([1,2,3],index=['a','b','c'])
s2 = Series([1,2,3],index=['a','d','c'])
s = s1 + s2
s
a 2.0
b NaN
c 6.0
d NaN
dtype: float64
可以使用pd.isnull()
,pd.notnull()
,或s.isnull()
,notnull()
函数检测缺失数据
s[[1,2]] # 一次取多值
s[['a','b']]
s[[True,False,True,False]] # True将值保留,False:将值舍去。True:2,False:NaN,True:6,Flase:NaN。
a 2.0
c 6.0
dtype: float64
s.isnull() # 检测每个元素,NOT NaN:False;NaN:True
a False
b True
c False
d True
dtype: bool
s.notnull() # 检测每个元素,NOT NaN:True;NaN:False
a True
b False
c True
d False
dtype: bool
s[s.notnull()]
a 2.0
c 6.0
dtype: float64
Series之间的运算
- 在运算中自动对齐不同索引的数据
- 如果索引不对应,则补NaN
DataFrame
DataFrame是一个【表格型】的数据结构。DataFrame由按一定顺序排列的多列数据组成。设计初衷是将Series的使用场景从一维拓展到多维。DataFrame既有行索引,也有列索引。
- 行索引:index
- 列索引:columns
- 值:values
DataFrame的创建
最常用的方法是传递一个字典来创建。DataFrame以字典的键作为每一列的名称,以字典的值(一个数组)作为每一列。
此外,DataFrame会自动加上每一行的索引。
使用字典创建的DataFrame后,则columns参数将不可被使用。
同Series一样,若传入的列与字典的键不匹配,则相应的值为NaN。
- 使用ndarray创建DataFrame
df = DataFrame(data=np.random.randint(0,100,size=(3,4)),index=['a','b','c'],columns=['A','B','C','D'])
df
A | B | C | D | |
---|---|---|---|---|
a | 49 | 10 | 86 | 64 |
b | 6 | 74 | 62 | 13 |
c | 49 | 79 | 14 | 25 |
DataFrame属性:values、columns、index、shape
df.values
array([[49, 10, 86, 64],
[ 6, 74, 62, 13],
[49, 79, 14, 25]])
df.columns
Index(['A', 'B', 'C', 'D'], dtype='object')
df.index
Index(['a', 'b', 'c'], dtype='object')
df.shape
(3, 4)
试做:使用ndarray创建DataFrame:创建一个表格用于展示张三,李四,王五的java,python的成绩
dic = {
'张三':[150,150,150,150],
'李四':[0,0,0,0]
}
df = DataFrame(data=dic,index=['语文','数学','英语','理综'])
df
张三 | 李四 | |
---|---|---|
语文 | 150 | 0 |
数学 | 150 | 0 |
英语 | 150 | 0 |
理综 | 150 | 0 |
DataFrame的索引
(1) 对列进行索引
- 通过类似字典的方式 df['q']
- 通过属性的方式 df.q
可以将DataFrame的列获取为一个Series。返回的Series拥有原DataFrame相同的索引,且name属性也已经设置好了,就是相应的列名。
df
张三 | 李四 | |
---|---|---|
语文 | 150 | 0 |
数学 | 150 | 0 |
英语 | 150 | 0 |
理综 | 150 | 0 |
# 修改列索引
df['张三'] # 返回的是Series
语文 150
数学 150
英语 150
理综 150
Name: 张三, dtype: int64
# 获取前两列
df[['李四','张三']]
李四 | 张三 | |
---|---|---|
语文 | 0 | 150 |
数学 | 0 | 150 |
英语 | 0 | 150 |
理综 | 0 | 150 |
(2) 对行进行索引
- 使用.loc[]加index来进行行索引
- 使用.iloc[]加整数来进行行索引
同样返回一个Series,index为原来的columns。
df.loc['语文']
张三 150
李四 0
Name: 语文, dtype: int64
df.iloc[0]
张三 150
李四 0
Name: 语文, dtype: int64
df.iloc[[0,1]]
张三 | 李四 | |
---|---|---|
语文 | 150 | 0 |
数学 | 150 | 0 |
(3) 对元素索引的方法
- 使用列索引
- 使用行索引(iloc[3,1] or loc['C','q']) 行索引在前,列索引在后
df['张三']['英语']
150
df.loc['英语','张三'] # 逗号左方:行,右方:列
150
df.loc[['数学','理综'],'张三'] # 逗号左方:行,右方:列
数学 150
理综 150
Name: 张三, dtype: int64
切片:
【注意】
直接用中括号时:
- 索引表示的是列索引
- 切片表示的是行切片
df
张三 | 李四 | |
---|---|---|
语文 | 150 | 0 |
数学 | 150 | 0 |
英语 | 150 | 0 |
理综 | 150 | 0 |
df[0:2]
张三 | 李四 | |
---|---|---|
语文 | 150 | 0 |
数学 | 150 | 0 |
在loc和iloc中使用切片(切列) : df.loc['B':'C','丙':'丁']
df.iloc[:,0:1]
张三 | |
---|---|
语文 | 150 |
数学 | 150 |
英语 | 150 |
理综 | 150 |
索引:
- df[列索引]:取一列
- df[[col1,col2]]:取出两列
- df.loc[显示的行索引]:取行
- df.loc[行,列]:取元素
切片: - df[index1:index3]:切行
- df.loc[col1:col3]:切列
3)DataFrame的运算
(1) DataFrame之间的运算
同Series一样:
- 在运算中自动对齐不同索引的数据
- 如果索引不对应,则补NaN
创建DataFrame df1 不同人员的各科目成绩,月考一
创建DataFrame df2 不同人员的各科目成绩,月考二
有新学生转入
============================================
练习6:
1. 假设ddd是期中考试成绩,ddd2是期末考试成绩,请自由创建ddd2,并将其与ddd相加,求期中期末平均值。
2. 假设张三期中考试数学被发现作弊,要记为0分,如何实现?
3. 李四因为举报张三作弊立功,期中考试所有科目加100分,如何实现?
4. 后来老师发现有一道题出错了,为了安抚学生情绪,给每位学生每个科目都加10分,如何实现?
============================================
qizhong = df
qimo = df
(qizhong + qimo)/2
张三 | 李四 | |
---|---|---|
语文 | 150.0 | 0.0 |
数学 | 150.0 | 0.0 |
英语 | 150.0 | 0.0 |
理综 | 150.0 | 0.0 |
qizhong.loc['数学','张三'] = 0
qizhong
张三 | 李四 | |
---|---|---|
语文 | 150 | 0 |
数学 | 0 | 0 |
英语 | 150 | 0 |
理综 | 150 | 0 |
qizhong['李四'] += 100
qizhong
张三 | 李四 | |
---|---|---|
语文 | 150 | 100 |
数学 | 150 | 100 |
英语 | 150 | 100 |
理综 | 150 | 100 |
qizhong += 10
qizhong
张三 | 李四 | |
---|---|---|
语文 | 160 | 110 |
数学 | 160 | 110 |
英语 | 160 | 110 |
理综 | 160 | 110 |
股票项目
https://www.joinquant.com/
tushare官网tushare.org
,https://tushare.pro/
- 使用tushare包获取某股票的历史行情数据。
import tushare as ts
df = ts.get_k_data('600519',start='2020-08-10')
df.to_csv('./maotai.csv') # 持久化储存
df = pd.read_csv('./maotai.csv')
df
Unnamed: 0 | date | open | close | high | low | volume | code | |
---|---|---|---|---|---|---|---|---|
0 | 0 | 2020-08-10 | 1627.97 | 1633.99 | 1644.96 | 1608.11 | 23624.0 | 600519 |
1 | 1 | 2020-08-11 | 1640.00 | 1642.51 | 1666.43 | 1640.00 | 35818.0 | 600519 |
2 | 2 | 2020-08-12 | 1633.00 | 1626.95 | 1641.00 | 1605.25 | 27934.0 | 600519 |
3 | 3 | 2020-08-13 | 1630.00 | 1635.00 | 1638.88 | 1609.00 | 20541.0 | 600519 |
4 | 4 | 2020-08-14 | 1639.90 | 1661.00 | 1665.70 | 1631.00 | 37653.0 | 600519 |
df.drop(labels='Unnamed: 0',axis=1,inplace=True)
# drop函数中0是行。删除行,inplace=True:在原数据中删除。
# 验证data列中数据的数据类型
type(df['date'][2]) # str
df.head(2)
date | open | close | high | low | volume | code | |
---|---|---|---|---|---|---|---|
0 | 2020-08-10 | 1627.97 | 1633.99 | 1644.96 | 1608.11 | 23624.0 | 600519 |
1 | 2020-08-11 | 1640.00 | 1642.51 | 1666.43 | 1640.00 | 35818.0 | 600519 |
# 将date这一列的数据转成时间类型然后将其作为原数据的行索引
df = pd.read_csv('./maotai.csv',index_col='date',parse_dates=['date'])
# index_col:将列转为行索引,parse_dates:设置为时间类型
df.drop(labels='Unnamed: 0',axis=1,inplace=True)
df
open | close | high | low | volume | code | |
---|---|---|---|---|---|---|
date | ||||||
2020-08-10 | 1627.97 | 1633.99 | 1644.96 | 1608.11 | 23624.0 | 600519 |
2020-08-11 | 1640.00 | 1642.51 | 1666.43 | 1640.00 | 35818.0 | 600519 |
2020-08-12 | 1633.00 | 1626.95 | 1641.00 | 1605.25 | 27934.0 | 600519 |
2020-08-13 | 1630.00 | 1635.00 | 1638.88 | 1609.00 | 20541.0 | 600519 |
2020-08-14 | 1639.90 | 1661.00 | 1665.70 | 1631.00 | 37653.0 | 600519 |
输出该股票所有收盘比开盘上涨1%以上的日期
#(收盘-开盘)/开盘 > 0.01
(df['close'] - df['open']) / df['open'] > 0.01
#True:满足需求
#false:不满足
date
2020-08-10 False
2020-08-11 False
2020-08-12 False
2020-08-13 False
2020-08-14 True
dtype: bool
#返回了满足需求的行数据
df.loc[(df['close'] - df['open']) / df['open'] > 0.01]
open | close | high | low | volume | code | |
---|---|---|---|---|---|---|
date | ||||||
2020-08-14 | 1639.9 | 1661.0 | 1665.7 | 1631.0 | 37653.0 | 600519 |
#获取了满足需求的日期
df.loc[(df['close'] - df['open']) / df['open'] > 0.01].index
# 结论:如果获取了一组布尔值,接下来改组布尔值就直接作为元数据的行索引
DatetimeIndex(['2020-08-14'], dtype='datetime64[ns]', name='date', freq=None)
- 输出该股票所有开盘比前日收盘跌幅超过2%的日期。
- (开盘-前日收盘)/ 前日收盘 < -0.02
# (df['open'] - df['close'].shift(1))/df['close'].shift(1) < -0.02
# df['close'].shift(1):将close一列下移一列
#满足需求的行数据
df.loc[(df['open'] - df['close'].shift(1))/df['close'].shift(1) < -0.02].index
- 假如我从2010年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到19年9月为止,我的收益如何?
- 分析:
- 规则:基于开盘价股票的买卖
- 买:一个完整的年需要买12次股票,一次买入100只,一个完整的年需要买入1200只(单价:当天开盘价)
- 卖:一个完整的年需要卖一次股票,一次卖出1200只
- 备注:19年不是一个完整的年,该年只可以买入900只,并且卖不出去
df_new = df['2010':'2019'] # 切片,['2010-01':'2019-09']也可以
# 数据的重新取样的机制(resample):根据指定好的规则进行指定数据的提取
df_monthly = df_new.resample('M').first() # 年:A,月:M
# 计算出买股票一共花了多少钱
cost_monry = df_monthly['open'].sum()*100
cost_monry
- 卖出所有的股票一共进账多少钱
- 每年最后一个交易日以开盘价为单价进行卖出
df_yearly = df_new.resample('A').last()
df_yearly = df_yearly[:-1]
recv_monry = df_yearly['open'].sum()*1200
- 19年买入了900只股票没有卖出,剩余的股票也计算到总收益
- 剩余股票的单价应该选择使用昨日的收盘价
last_price = df.iloc[-1]['close']
cunHuo_price = last_price * 900
#计算总收益
cunHuo_price+recv_monry-cost_monry # 528908.7
处理丢失数据
有两种丢失数据:
- None
- np.nan(NaN)
None是Python自带的,其类型为python object。因此,None不能参与到任何计算中。
np.nan(NaN)是浮点类型,能参与到计算中。但计算的结果总是NaN。
pandas中None与np.nan都视作np.nan
pandas处理空值操作
isnull()
notnull()
dropna()
: 过滤丢失数据fillna()
: 填充丢失数据
创建DataFrame,给其中某些元素赋值为nan
(1)判断函数
isnull()
notnull()
df.notnull/isnull().any()/all()
过滤df中的空值(只保留没有空值的行)
df.dropna() 可以选择过滤的是行还是列(默认为行):axis中0表示行,1表示的列
(3) 填充函数 Series/DataFrame
fillna()
:value和method参数。
可以选择前向填充还是后向填充。
method 控制填充的方式 bfill ffill
练习7:
1. 简述None与NaN的区别
2. 假设张三李四参加模拟考试,但张三因为突然想明白人生放弃了英语考试,因此记为None,请据此创建一个DataFrame,命名为ddd3
3. 老师决定根据用数学的分数填充张三的英语成绩,如何实现?
用李四的英语成绩填充张三的英语成绩?