Pandas基础(重庆)
pandas
pandas简介
Python Data Analysis Library
pandas是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型结构化数据集所需的工具。
pandas提供了大量能使我们快速便捷地处理数据的函数和方法。Python长期以来一直非常适合数据整理和准备,你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之 一。
pandas是python里面分析结构化数据的工具集,基础是numpy,图像库是matplotlib。
pandas学习资源
pandas官网 :https://pandas.pydata.org/
pandas核心数据结构
数据结构是计算机存储、组织数据的方式。 通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
Series
Series定义
Series可以理解为一个一维的数组,只是index名称可以自己改动。类似于定长的有序字典,有Index和 value。index赋值必须是list类型。
Series官方文档:https://pandas.pydata.org/docs/reference/series.html
# 创建函数
Series([data, index, dtype, name, copy, …])
参数名称 | 说明 |
---|---|
data | 数据源 |
index | 索引,赋值必须为列表 |
dtype | 元素数据类型 |
name | Series的名称 |
Series的创建
import pandas as pd
import numpy as np
# 创建一个空的系列
s = pd.Series()
# 从ndarray创建一个Series
data = np.array(['张三','李四','王五','赵柳'])
s = pd.Series(data)
# 增加index,index赋值必须是列表类型
s = pd.Series(data,index=['100','101','102','103'], name='series_name')
# 从字典创建一个Series,字典的键作为series的键,字典的值作为series的数据
data = {'S100': '张三', 'S101': '李四', 'S102': '王五', 'S103': '赵六', 'S104': '杨七'}
s = pd.Series(data)
# 从标量创建一个Series
s = pd.Series(5, index=[0, 1, 2, 3])
s
Series中数据的访问
# 1.使用索引检索元素
s = pd.Series([1,2,3,4,5],index = ['a','b','c','d','e'])
print(s)
print(s[0])
print(s[[0,1,2]])
print(s[s>3])
print(s[:3])
print(s[-3:])
# 2.使用(index)标签检索数据
print(s['a'])
print(s[['a','c','d']])
print(s['a':'d'])
Series的常用属性
属性名称 | 说明 |
---|---|
Series.index | 系列的索引(轴标签) |
Series.array | 支持该系列或索引的数据的ExtensionArray |
Series.values | Series的值。根据dtype将Series返回为ndarray或类似ndarray |
Series.dtype | 返回基础数据的dtype对象 |
Series.shape | 返回基础数据形状的元组 |
Series.nbytes | 返回基础数据中的字节数 |
Series.ndim | 返回基础数据的维数(轴数) |
Series.size | 返回基础数据中的元素数 |
Series.T | 返回转置 |
Series.dtypes | 返回基础数据的dtype对象 |
Series.memory_usage([index,deep]) | 返回该系列的内存使用情况 |
Series.hasnans | 如果有nans,就返回True |
Series.empty | 指示DataFrame是否为空,如果为空则返回True |
Series.name | 返回系列的名称 |
示例:
# 创建一个series
s = pd.Series([1,2,3,4,5],index = ['a','b','c','d','e'], name='series_name')
# 显示series
print(s)
print(s.index)
print(s.array)
print(s.values)
print(s.dtype)
print(s.shape)
print(s.nbytes)
print(s.ndim)
print(s.size)
print(s.T)
print(s.dtypes)
print(s.memory_usage())
print(s.hasnans)
print(s.empty)
print(s.name)
pandas日期类型数据处理
# pandas识别的日期字符串格式
dates = pd.Series(['2011', '2011-02', '2011-03-01', '2011/04/01',
'2011/05/01 01:01:01', '01 Jun 2011'])
# to_datetime() 转换日期数据类型
dates = pd.to_datetime(dates)
print(dates, dates.dtype)
print(type(dates))
0 2011-01-01 00:00:00
1 2011-02-01 00:00:00
2 2011-03-01 00:00:00
3 2011-04-01 00:00:00
4 2011-05-01 01:01:01
5 2011-06-01 00:00:00
dtype: datetime64[ns] datetime64[ns]
<class 'pandas.core.series.Series'>
# 获取时间的某个日历字段的数值
print(dates.dt.day)
0 1
1 1
2 1
3 1
4 1
5 1
dtype: int64
Series.dt提供了很多日期相关操作,如下:
Series.dt.year The year of the datetime.
Series.dt.month The month as January=1, December=12.
Series.dt.day The days of the datetime.
Series.dt.hour The hours of the datetime.
Series.dt.minute The minutes of the datetime.
Series.dt.second The seconds of the datetime.
Series.dt.microsecond The microseconds of the datetime.
Series.dt.week The week ordinal of the year.
Series.dt.weekofyear The week ordinal of the year.
Series.dt.dayofweek The day of the week with Monday=0, Sunday=6.
Series.dt.weekday The day of the week with Monday=0, Sunday=6.
Series.dt.dayofyear The ordinal day of the year.
Series.dt.quarter The quarter of the date.
Series.dt.is_month_start Indicates whether the date is the first day of the month.
Series.dt.is_month_end Indicates whether the date is the last day of the month.
Series.dt.is_quarter_start Indicator for whether the date is the first day of a quarter.
Series.dt.is_quarter_end Indicator for whether the date is the last day of a quarter.
Series.dt.is_year_start Indicate whether the date is the first day of a year.
Series.dt.is_year_end Indicate whether the date is the last day of the year.
Series.dt.is_leap_year Boolean indicator if the date belongs to a leap year.
Series.dt.days_in_month The number of days in the month.
日期运算:
# datetime日期运算
delta = dates - pd.to_datetime('1970-01-01')
print(delta, delta.dtype, type(delta))
0 14975 days 00:00:00
1 15006 days 00:00:00
2 15034 days 00:00:00
3 15065 days 00:00:00
4 15095 days 01:01:01
5 15126 days 00:00:00
dtype: timedelta64[ns] timedelta64[ns] <class 'pandas.core.series.Series'>
# 把时间偏移量换算成天数
print(delta.dt.days)
0 14975
1 15006
2 15034
3 15065
4 15095
5 15126
dtype: int64
通过指定周期和频率,使用date_range()
函数就可以创建日期序列。 默认情况下,频率是'D'。
import pandas as pd
# 以日为频率
datelist = pd.date_range('2019/08/21', periods=5)
print(datelist)
DatetimeIndex(['2019-08-21', '2019-08-22', '2019-08-23', '2019-08-24',
'2019-08-25'],
dtype='datetime64[ns]', freq='D')
# 以月为频率
datelist = pd.date_range('2019/08/21', periods=5,freq='M')
print(datelist)
DatetimeIndex(['2019-08-31', '2019-09-30', '2019-10-31', '2019-11-30',
'2019-12-31'],
dtype='datetime64[ns]', freq='M')
# 构建某个区间的时间序列
start = pd.datetime(2017, 11, 1)
end = pd.datetime(2017, 11, 5)
dates = pd.date_range(start, end)
print(dates)
DatetimeIndex(['2017-11-01', '2017-11-02', '2017-11-03', '2017-11-04',
'2017-11-05'],
dtype='datetime64[ns]', freq='D')
bdate_range()
用来表示商业日期范围,不同于date_range()
,它不包括星期六和星期天。
import pandas as pd
datelist = pd.bdate_range('2011/11/03', periods=5)
print(datelist)
DatetimeIndex(['2011-11-03', '2011-11-04', '2011-11-07', '2011-11-08',
'2011-11-09'],
dtype='datetime64[ns]', freq='B')
DataFrame
DataFrame定义
DataFrame是一个类似于表格的数据类型,可以理解为一个二维数组,索引有两个维度,可更改。DataFrame具有以下特点:
- 列可以是不同的类型
- 大小可变
- 标记轴(行和列)
- 针对行与列进行轴向统计
DataFrame创建
DataFrame统一的创建形式为:
import pandas as pd
pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
参数名称 | 说明 |
---|---|
data | 数据ndarray(结构化或同类),Iterable,dict或DataFrame |
index | 行标签,或类似数组 |
columns | 列标签,或类似数组 |
dtype | 元素数据类型 |
copy | 是否可复制 |
示例:
import pandas as pd
# 1.创建一个空的DataFrame
df = pd.DataFrame()
print(df)
# 2.从列表创建DataFrame
data = [1,2,3,4,5]
df = pd.DataFrame(data)
print(df)
# 3.从二维列表创建DataFrame
data = [['Alex',10],['Bob',12],['Clarke',13]]
df = pd.DataFrame(data,columns=['Name','Age'],dtype=float)
print(df)
# 4.从列表嵌套字典创建DataFrame
data = [{'a': 1, 'b': 2},{'a': 5, 'b': 10, 'c': 20}]
df = pd.DataFrame(data)
print(df)
# 5.从字典创建DataFrame
data = {'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]}
df = pd.DataFrame(data, index=['s1','s2','s3','s4'])
print(df)
# 6.从字典创建DataFrame
data = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),
'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(data)
print(df)
DataFrame常用属性
属性名称 | 描述 |
---|---|
DataFrame.index | DataFrame的索引(行标签) |
DataFrame.columns | DataFrame的列标签 |
DataFrame.dtypes | 返回DataFrame中元素的dtype |
DataFrame.info | 打印DataFrame的简要摘要 |
DataFrame.select_dtypes | 根据列dtypes返回DataFrame列的子集 |
DataFrame.values | 返回DataFrame的值,返回值为ndarry |
DataFrame.axes | 返回一个表示DataFrame轴的列表 |
DataFrame.ndim | 返回一个表示轴数/数组维数的整数 |
DataFrame.size | 返回表示此对象中元素数量的整数 |
DataFrame.shape | 返回一个表示DataFrame维数(形状)的元组 |
DataFrame.memory_usage([index,deep]) | 返回每列的内存使用情况(以字节为单位) |
DataFrame.empty | 指示DataFrame是否为空 |
示例:
import pandas as pd
# 创建DataFrame
data = {'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]}
df = pd.DataFrame(data, index=['s1','s2','s3','s4'])
print(df)
# 添加score列
df['score']=pd.Series([90, 80, 70, 60], index=['s1','s2','s3','s4'])
print(df)
# 轴的列表
print(df.axes)
# 数据类型
print(df['Age'].dtype)
# 是否空DataFrame
print(df.empty)
# 轴数
print(df.ndim)
# 元素个数
print(df.size)
12
# 行标签
print(df.index)
# 值
print(df.values)
# df的前三行
print(df.head(3))
# df的后三行
print(df.tail(3))
核心数据结构操作
显示索引,列
import pandas as pd
# 创建DataFrame
data = {'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]}
df = pd.DataFrame(data, index=['s1','s2','s3','s4'])
df.index
df.columns
索引选择
操作 | 语法 | 返回值 |
---|---|---|
选择列 | df[col] | Series |
按标签选择行 | df.loc[label] | Series |
按整数位置选择行 | df.iloc[loc] | Series |
切片行 | df[5:10] | DataFrame |
通过布尔向量选择行 | df[bool_vec] | DataFrame |
复合索引
DataFrame的行级索引与列级索引都可以设置为复合索引,表示从不同的角度记录数据。
data = np.floor(np.random.normal(85, 3, (6,3)))
df = pd.DataFrame(data)
index = [('classA', 'F'), ('classA', 'M'), ('classB', 'F'), ('classB', 'M'), ('classC', 'F'), ('classC', 'M')]
df.index = pd.MultiIndex.from_tuples(index)
columns = [('Age', '20+'), ('Age', '30+'), ('Age', '40+')]
df.columns = pd.MultiIndex.from_tuples(columns)
复合索引的访问:
# 访问行
df.loc['classA']
df.loc['classA', 'F']
df.loc[['classA', 'classC']]
# 访问列
df.Age
df.Age['20+']
df['Age']
df['Age', '20+']
列操作
列访问
DataFrame的单列数据为一个Series。根据DataFrame的定义可以知晓DataFrame是一个带有标签的二维数组,每个标签相当每一列的列名。
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),
'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd']),
'three' : pd.Series([1, 3, 4], index=['a', 'c', 'd'])}
df = pd.DataFrame(d)
print(df)
# 列访问
df[df.columns[:2]]
df[['one','two']]
df[df.columns[[0,2]]]
列添加
DataFrame添加一列的方法非常简单,只需要新建一个列索引。并对该索引下的数据进行赋值操作即可。
import pandas as pd
# 新建一个列索引,并赋值
# 当插入与DataFrame索引不同的Series时,它将符合DataFrame的索引
# 可以插入原始ndarray,但它们的长度必须与DataFrame索引的长度匹配,默认情况下,列会插入到末尾。
df['four']=pd.Series([90, 80, 70, 60], index=['a', 'b', 'c', 'd'])
print(df)
# insert功能可插入到列中的特定位置
df.insert(1, 'five', df['one'])
列删除
删除某列数据需要用到pandas提供的方法del、pop、drop方法,用法如下:
import pandas as pd
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),
'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd']),
'three' : pd.Series([10, 20, 30], index=['a', 'b', 'c']),
'four' : pd.Series([40, 50, 60], index=['a', 'b', 'c']),
'five' : pd.Series([70, 80, 90], index=['a', 'b', 'c'])}
df = pd.DataFrame(d)
df
# del方法删除
del(df['one'])
print(df)
# pop方法删除
df.pop('two')
print(df)
# drop方法删除
df.drop(['three'], axis=1)
drop
删除某列或某行数据 ,drop方法的用法如下。 axis为0时表示删除行,axis为1时表示删除列。
drop(labels, axis=0, level=None, inplace=False, errors='raise')。
常用参数:
参数名称 | 说明 |
---|---|
labels | 接受string或array。代表删除的行或列的标签,无默认。 |
axis | 接受0或1。代表标签所在轴。默认为None。 |
levels | 接收int或者索引名。代表标签所在级别。默认为None。 |
inplace | 接收boolean。代表操作是否对原数据生效。默认为False。 |
行操作
行访问
如果只是需要访问DataFrame某几行数据的实现方式则采用数组的选取方式,使用 ":" 即可:
import pandas as pd
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),
'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print(df[2:4])
print(df['a':'c’])
loc是针对DataFrame索引名称的切片方法。如果传入的不是索引名称,那么切片操作将无法执行,利用loc方法,能够实现所有单层索引切片操作。
loc方法使用方法如下:DataFrame.loc[行索引名称或条件, 列索引名称]
select by label : loc
标签参数查找DataFrame.loc[index:index,[‘columns’]],loc方法当中的columns可以选择多列,如果表示只按列选择的话index可以不填但是冒号(:)和逗号 (,)一定要写。
import pandas as pd
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),
'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print(df.loc['b'])
print(df.loc[['a', 'b']])
print(df.loc['a':'c'])
print(df.loc['a':'c','one':'two'])
iloc和loc区别是iloc接收的必须是行索引和列索引的位置。iloc方法的使用方法如下:
DataFrame.iloc[行索引位置, 列索引位置]。
select by position:iloc
索引查找,把标签换成绝对位置。
import pandas as pd
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),
'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print(df.iloc[2])
print(df.iloc[[2, 3]])
print(df.iloc[2:3,0:1])
行添加
import pandas as pd
df = pd.DataFrame([['zs', 12], ['ls', 4]], columns = ['Name','Age'], index=[0, 1])
df2 = pd.DataFrame([['ww', 16], ['zl', 8]], columns = ['Name','Age'], index=[2, 3])
df = df.append(df2)
print(df)
DataFrame.append(other, ignore_index=False, verify_integrity=False, sort=False)
参数 | 说明 |
---|---|
other | DataFrame或类似Series / dict的对象,要追加的数据 |
ignore_index | 如果为True,则结果轴将标记为0、1,…,n-1 |
verify_integrity | 如果为True,则在创建具有重复项的索引时引发ValueError |
sort | 布尔值,默认为False |
返回:DataFrame
行删除
使用索引标签从DataFrame中删除列或删除行。 如果标签重复,则会删除多行。
import pandas as pd
df = pd.DataFrame([['zs', 12], ['ls', 4]], columns = ['Name','Age'])
df2 = pd.DataFrame([['ww', 16], ['zl', 8]], columns = ['Name','Age'])
df = df.append(df2)
# 删除index为0的行
df = df.drop(0)
print(df)
修改DataFrame中的数据
更改DataFrame中的数据,原理是将这部分数据提取出来,重新赋值为新的数据。
import pandas as pd
df = pd.DataFrame([['zs', 12], ['ls', 4]], columns = ['Name','Age'])
df2 = pd.DataFrame([['ww', 16], ['zl', 8]], columns = ['Name','Age'])
df = df.append(df2)
df['Name'][0] = 'Tom'
print(df)
数据对齐
DataFrame对象之间的数据会自动在列和索引(行标签)上对齐。同样,结果对象将具有列标签和行标签的并集。对于缺失值将自动补全NaN。
df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])
df + df2
DataFrame的运算
add()
sub()
mul()
div()
# 二进制运算
radd()
rsub()
rmul()
rdiv()
在DataFrame和Series之间进行操作时,默认行为是在DataFrame列上对齐Series索引,从而 逐行广播。
df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
df
# 每一行都要进行计算
df - df.iloc[0]
在处理时间序列数据的特殊情况下,如果DataFrame索引包含日期,则广播将按列进行
index = pd.date_range('1/1/2000', periods=8)
df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))
df
type(df['A'])
df - df['A'] # 现在已弃用,并将在以后的版本中删除
df.sub(df['A'], axis=0)
DataFrame与数字的运算
df * 5 + 2
1 / df
df ** 4
DataFrame的布尔运算符
df1 = pd.DataFrame({'a': [1, 0, 1], 'b': [0, 1, 1]}, dtype=bool)
df2 = pd.DataFrame({'a': [0, 1, 1], 'b': [1, 1, 0]}, dtype=bool)
df1 & df2
df1 | df2
df1 ^ df2
~df1
DataFrame的转置
要进行转置,请访问T属性(也可以用transpose函数),类似于ndarray
df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
df.T
df.transpose()
apply函数
Series.apply(func,convert_dtype = True,args = (),** kwds)
DataFrame.apply(func,axis = 0,raw = False,result_type = None,args = (),** kwds)
参数说明:
参数名称 | 说明 |
---|---|
func | 需要执行的函数名称 |
convert_dtype | 尝试找到更好的dtype以获得逐元素函数结果。如果为False,则保留为dtype = object。 |
args | 在series值之后将位置参数传递给func |
**kwds | 传递给func的其他关键字参数 |
axis | {0或'index',1或'columns'},默认0 |
raw | 确定是否将行或列作为Series或ndarray对象传递,bool默认为False |
result_type | {'expand','reduce','broadcast',None},默认为None,仅在axis=1(列)时起作用 |
pandas提供了apply函数方便的处理Series与DataFrame;apply函数支持逐一处理数据集中的每个元素都会执行一次目标函数,把返回值存入结果集中。
# series.apply()
ary = np.array(['80公斤','83公斤','78公斤','74公斤','84公斤'])
s = pd.Series(ary)
def func(x):
return x[:2]
s.apply(func)
# dataframe.apply()
def func(x):
x[pd.isna(x)] = x.mean()
return x
ratings.apply(func, axis=1)
排序
Pandas有两种排序方式,它们分别是按标签与按实际值排序。
import numpy as np
d = {'Name':pd.Series(['Tom','James','Ricky','Vin','Steve','Minsu','Jack','Lee','David','Gasper','Betina','Andres']),
'Age':pd.Series([25,26,25,23,30,29,23,34,40,30,51,46]),
'Rating':pd.Series([4.23,3.24,3.98,2.56,3.20,4.6,3.8,3.78,2.98,4.80,4.10,3.65])}
unsorted_df = pd.DataFrame(d)
按标签(行)排序
使用sort_index()
方法,通过传递axis
参数和排序顺序,可以对DataFrame
进行排序。 默认情况下,按照升序对行标签进行排序。
# 按照行标进行排序
sorted_df=unsorted_df.sort_index()
print (sorted_df)
# 控制排序顺序
sorted_df = unsorted_df.sort_index(ascending=False)
print (sorted_df)
按标签(列)排序
# 按照列标签进行排序
sorted_df=unsorted_df.sort_index(axis=1)
print (sorted_df)
按某列值排序
像索引排序一样,sort_values()
是按值排序的方法。它接受一个by
参数,它将使用要与其排序值的DataFrame
的列名称。
sorted_df = unsorted_df.sort_values(by='Age')
print (sorted_df)
# 先按Age进行升序排序,然后按Rating降序排序
sorted_df = unsorted_df.sort_values(by=['Age', 'Rating'], ascending=[True, False])
print (sorted_df)
数据合并
concat
concat函数是pandas的方法,可以根据不同的轴合并数据集。
concat函数的基本语法如下:
pandas.concat(objs: Union[Iterable[‘DataFrame’], Mapping[Label, ‘DataFrame’]], axis='0', join: str = "'outer'", ignore_index: bool = 'False', keys='None', levels='None', names='None', verify_integrity: bool = 'False', sort: bool = 'False', copy: bool = 'True')
常用参数如下:
参数名称 | 说明 |
---|---|
objs | 接收多个Series,DataFrame,Panel的组合。表示参与连接的pandas对象的列表的组合。无默认。 |
axis | {0/’index’, 1/’columns’}, default 0 |
join | 接收inner或outer。表示其他轴向上的索引是按交集(inner)还是并集(outer)进行合并。默认为 outer。 |
join_axes | 接收Index对象。表示用于其他n-1条轴的索引,不执行并集/交集运算。 |
# 合并两个series
s1 = pd.Series(['a', 'b'])
s2 = pd.Series(['c', 'd'])
pd.concat([s1, s2])
# 忽略存在的index,并且重新设定index值
pd.concat([s1, s2], ignore_index=True)
# 增加复合索引
pd.concat([s1,s2],keys=['s1','s2'])
# 合并两个DataFrame
df1 = pd.DataFrame([['a', 1], ['b', 2]],columns=['letter', 'number'])
df2 = pd.DataFrame([['c', 3], ['d', 4]],columns=['letter', 'number'])
df1
df2
pd.concat([df1, df2])
# 将DataFrame具有重叠列的对象合并,缺失值将填充NaN值
df3 = pd.DataFrame([['c', 3, 'cat'], ['d', 4, 'dog']], columns=['letter', 'number', 'animal'])
pd.concat([df1,df3],sort=False)
# 合并DataFrame具有重叠列,并仅通过传递inner给join关键字参数返回那些共享的对象
pd.concat([df1, df3], join="inner")
# DataFrame传入,沿x轴水平合并对象axis=1
df4 = pd.DataFrame([['bird', 'polly'], ['monkey', 'george']],columns=['animal', 'name'])
pd.concat([df1, df4], axis=1)
纵向合并:纵向堆叠,需要区分情况。
横向合并:横向堆叠,即将两个表在X轴向拼接在一起,可以使用concat函数完成。
merge
panda具有全功能、高性能的内存连接操作,与SQL之类的关系数据库非常相似。与其他开源实现相比,这些方法的性能要好得多(在某些情况下要好一个数量级以上)
pandas提供了merge函数实现高效的内存链接操作:
pd.merge(left, right, how='inner', on=None, left_on=None,right_on=None,left_index=False, right_index=False)
参数名称 | 说明 |
---|---|
left | 接收DataFrame,左表,无默认。 |
right | 接收DataFrame或Series。右表,无默认。 |
how | {‘left’, ‘right’, ‘outer’, ‘inner’}, default ‘inner’ |
on | 接收string或sequence。表示外键字段名。 |
left_on | 关联操作时左表中的关联字段名。 |
right_on | 关联操作时右表中的关联字段名。 |
left_index | 是否将左表的index作为连接主键。(False) |
right_index | 是否将右表的index作为连接主键。(False) |
sort | 是否根据连接键对合并后的数据进行排序。(False) |
suffixes | tuple。合并后左表与右表重叠列名的别名尾缀。默认为('_x', '_y')。 |
返回值是一个DataFrame
示例:
import pandas as pd
left = pd.DataFrame({
'student_id':[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
'student_name': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung', 'Billy', 'Brian', 'Bran', 'Bryce', 'Betty', 'Emma', 'Marry', 'Allen', 'Jean', 'Rose', 'David', 'Tom', 'Jack', 'Daniel', 'Andrew'],
'class_id':[1,1,1,2,2,2,3,3,3,4,1,1,1,2,2,2,3,3,3,2],
'gender':['M', 'M', 'F', 'F', 'M', 'M', 'F', 'F', 'M', 'M', 'F', 'F', 'M', 'M', 'F', 'F', 'M', 'M', 'F', 'F'],
'age':[20,21,22,20,21,22,23,20,21,22,20,21,22,23,20,21,22,20,21,22],
'score':[98,74,67,38,65,29,32,34,85,64,52,38,26,89,68,46,32,78,79,87]})
right = pd.DataFrame(
{'class_id':[1,2,3,5],
'class_name': ['ClassA', 'ClassB', 'ClassC', 'ClassE']})
# 合并两个DataFrame
data = pd.merge(left,right)
print(data)
其他合并方法同数据库相同:
合并方法 | SQL等效 | 描述 |
---|---|---|
left | LEFT OUTER JOIN | 使用左侧对象的键 |
right | RIGHT OUTER JOIN | 使用右侧对象的键 |
outer | FULL OUTER JOIN | 使用键的联合 |
inner | INNER JOIN | 使用键的交集 |
实验:
# 合并两个DataFrame (左连接)
rs = pd.merge(left,right,on='subject_id', how='left')
print(rs)
# 合并两个DataFrame (右连接)
rs = pd.merge(left,right,on='subject_id', how='right')
print(rs)
# 合并两个DataFrame (外连接)
rs = pd.merge(left,right,on='subject_id', how='outer')
print(rs)
# 合并两个DataFrame (内连接)
rs = pd.merge(left,right,on='subject_id', how='inner')
print(rs)
join
连接另一个DataFrame的列。在索引或键列上将列与其他DataFrame连接起来。通过传递列表,一次有效地通过索引连接多个DataFrame对象
DataFrame.join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False)
参数说明:
参数名称 | 说明 |
---|---|
other | DataFrame, Series, or list of DataFrame。索引应与此列中的一列相似。如果传递了Series,则必须设置其name属性,并将其用作结果联接的DataFrame中的列名称。 |
on | str, list of str, or array-like, optional |
how | {‘left’, ‘right’, ‘outer’, ‘inner’}, default ‘left’ |
lsuffix | 在左框架的重叠列中使用的后缀 |
rsuffix | 在右框架的重叠列中使用的后缀 |
sort | 通过连接键按字典顺序对结果DataFrame进行排序 |
返回值为一个DataFrame
示例:
df = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3', 'K4', 'K5'],'A': ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']})
df
other = pd.DataFrame({'key': ['K0', 'K1', 'K2'],'B': ['B0', 'B1', 'B2']})
# 使用其索引加入DataFrames
df.join(other, lsuffix='_caller', rsuffix='_other')
# 如果要使用键列进行联接,则需要将key设置为df和other的索引
df.set_index('key').join(other.set_index('key'))
# 使用键列进行连接的另一种方法是使用on 参数。DataFrame.join始终使用其他索引,但我们可以# 使用df中的任何列
df.join(other.set_index('key'), on='key')
Pandas描述性统计分析
分组聚合
pandas提供了功能类似于数据库中group by语句的用于拆分数据组的方法pd.groupby();该方法提供的是分组聚合步骤中的拆分功能,能根据索引或字段对数据进行分组(Split) 进而针对得到的多组数据执行聚合操作(Apply),最终合并为最终结果(Combine)。
分组(gorupby)
groupby方法的参数及其说明:
DataFrame.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=<object object>, observed=False, dropna=True)
参数名称 | 说明 |
---|---|
by | 接收mapping, function, label, or list of labels。用于确定进行分组的依据。无默认。 |
axis | {0 or ‘index’, 1 or ‘columns’}, default 0 |
level | int, level name, or sequence of such, default None。代表标签所在级别。 |
as_index | 接收bool, default True。表示聚合后的聚合标签是否以DataFrame索引形式输出。 |
sort | 接收boolearn。表示是否对分组依据分组标签进行排序。默认为True。 |
group_keys | 接收boolearn。表示是否显示分组标签的名称。默认为True。 |
squeeze | 接收boolearn。表示是否在允许的情况下对返回数据进行降维。默认为False。 |
observed | bool, default False。 |
dropna | bool, default True。 |
用groupby方法分组后的结果并不能直接查看,而是被存在内存中,输出的是内存地址。实际上分组后的数据对象(Groupby对象)类似Series与DataFrame,是pandas提供的一种对象。
groupby方法的参数及其说明——by参数的特别说明:
- 如果传入的是一个数组则对其进行计算并分组。
- 如果传入的是一个字典或者Series 则字典或者Series的值用来做分组依据。
- 如果传入一个NumPy数组则数据的元素作为分组依据。
- 如果传入的是列名,字符串或者字符串列表,则使用这些字符串所代表的字段,作为分组依据。
Groupby对象的常用方法:
方法 | 说明 |
---|---|
groupObject.get_group('A') | 返回A组的详细数据 |
groupObject.size() | 返回每一组的频数 |
grouped = data.groupby(by=['class_id', 'gender'])
grouped.get_group((1, 'M'))
grouped = data.groupby(by=['class_id', 'gender'])
grouped.get_group((1, 'M'))
聚合
聚合函数为每个组返回聚合值。当创建了分组(groupby)对象,就可以对每个分组的其他字段数据执行求和、求标准差等操作。
使用agg函数和aggregate函数聚合计算
在正常使用过程中,agg函数和aggregate函数对DataFrame对象操作时功能几乎完全相 同,因此只需要掌握其中一个函数即可。
DataFrame.agg(func, axis=0, *args, **kwargs)
DataFrame.aggregate(func, axis=0, *args, **kwargs)
参数名称 | 说明 |
---|---|
func | 接收list、dict、function。表示应用于每行/每列的函数。无默认。 |
axis | 接收0或1。代表操作的轴向。默认为0。 |
使用聚合函数agg进行组内计算:
grouped = data.groupby(by='class_id')
grouped.agg({'score':np.mean})
对于某个字段希望只做求均值操作,而对另一个字段则希望只做求和操作,可以使用字典的方式,将两个字段名分别作为key:
grouped.agg({'age':np.max, 'score':np.mean})
还可以这样:
result = grouped.agg(
{'age':np.max, 'score':[np.mean, np.max]})
使用apply方法聚合数据
使用apply方法对GroupBy对象进行聚合操作其方法和agg方法也相同,不同之处在于apply方法相比agg方法传入的函数只能够作用于整个DataFrame或者Series,而无法像agg一样能够对不同字段应用不同函数获取不同结果。
DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
参数名称 | 说明 |
---|---|
func | 接收functions。表示应用于每行/列的函数。无默认。 |
axis | 接收0或1。代表操作的轴向。默认为0。 |
broadcast | 接收boolearn。表示是否进行广播。默认为False。 |
raw | 接收boolearn。表示是否直接将ndarray对象传递给函数。默认为False。 |
reduce | 接收boolearn或者None。表示返回值的格式。默认None。 |
pandas支持的聚合函数有:
方法名称 | 说明 |
---|---|
count | 计算分组的数目,包括缺失值。 |
head | 返回每组的前n个值。 |
max | 返回每组最大值。 |
mean | 返回每组的均值。 |
median | 返回每组的中位数。 |
cumcount | 对每个分组中组员的进行标记,0至n-1。 |
size | 返回每组的大小。 |
min | 返回每组最小值。 |
std | 返回每组的标准差。 |
sum | 返回每组的和。 |
透视表与交叉表
透视表
透视表(pivot table)是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。
它根据一个或多个键对数据进行分组聚合,并根据每个分组进行数据汇总。
使用povit_table函数创建透视表 pivot_table函数常用参数及其说明:
pands.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None,
margins=False, dropna=True, margins_name='All')
参数名称 | 说明 |
---|---|
data | 接收DataFrame。表示创建表的数据。无默认。 |
values | 接收字符串。用于指定想要聚合的数据字段名,默认使用全部数据。默认为None。 |
index | 接收string或list。表示行分组键。默认为None。 |
columns | 接收string或list。表示列分组键。默认为None。 |
aggfunc | 接收functions。表示聚合函数。默认为mean。 |
margins | 接收boolearn。表示汇总(Total)功能的开关,设为True后结果集中会出现名为“ALL”的 行和列。默认为True。 |
dropna | 接收boolearn。表示是否删掉全为NaN的列。默认为False。 |
pivot_table函数主要的参数调节 :
- index表示透视表的行
- columns表示透视表的列
- values 表示聚合的数据
- aggfunc表示对分析对象进行的分析,一般默认为求平均值,可以指定
- margins表示添加每行每列求和的值,默认不添加
# 以class_id与gender做分组汇总数据,默认聚合统计所有列
print(data.pivot_table(index=['class_id', 'gender']))
# 以class_id与gender做分组汇总数据,聚合统计score列
print(data.pivot_table(index=['class_id', 'gender'], values=['score']))
# 以class_id与gender做分组汇总数据,聚合统计score列,针对age的每个值列级分组统计
print(data.pivot_table(index=['class_id', 'gender'], values=['score'], columns=['age']))
# 以class_id与gender做分组汇总数据,聚合统计score列,针对age的每个值列级分组统计,添加行、列小计
print(data.pivot_table(index=['class_id', 'gender'], values=['score'], columns=['age'], margins=True))
# 以class_id与gender做分组汇总数据,聚合统计score列,针对age的每个值列级分组统计,添加行、列小计
print(data.pivot_table(index=['class_id', 'gender'], values=['score'], columns=['age'], margins=True, aggfunc='max'))
交叉表
交叉表(cross-tabulation, 简称crosstab)是一种用于计算分组频率的特殊透视表:
# 按照class_id分组,针对不同的gender,统计数量
print(pd.crosstab(data.class_id, data.gender, margins=True))
汇总数据描述
describe()函数
可以计算有关Series或DataFrame列的各种汇总统计信息(当然不包括NaN)
import numpy as np
import pandas ad pd
series = pd.Series(np.random.randn(1000))
series[::2] = np.nan
series.describe()
您可以选择特定的百分位数以包含在输出中,默认情况下,始终包含中位数。
series.describe(percentiles=[.05, .25, .75, .95])
请注意,在混合类型的DataFrame对象上,describe()会将摘要限制为仅包括数字列,如果没有,则仅包括分类列:
frame = pd.DataFrame({'a': ['Yes', 'Yes', 'No', 'No'], 'b': range(4)})
frame.describe()
可以通过提供类型列表作为include/ exclude 参数来控制此行为。
frame.describe(include=['object'])
frame.describe(include=['number'])
最小值/最大值的索引
Series和DataFrame上的idxmin()和idxmax()函数使用最小和最大对应值计算索引标签
s1 = pd.Series(np.random.randn(5))
s1.idxmin(), s1.idxmax()
df1 = pd.DataFrame(np.random.randn(5, 3), columns=['A', 'B', 'C'])
df1.idxmin(axis=0)
df1.idxmax(axis=1)
当有多个行(或列)的最小值或最大值的匹配,idxmin()并idxmax()返回第一匹配指数
df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=['A'],index=list('edcba'))
df3
df3['A'].idxmin()
idxmin和idxmax被称为NumPy里的argmin和argmax。
数值型特征的描述性统计
方法 | 描述 |
---|---|
count() |
非空值数目 |
sum() |
价值总和 |
mean() |
均值 |
median() |
算术值中位数 |
min() |
最小值 |
max() |
最大值 |
std() |
样本标准偏差 |
var() |
样本方差 |
skew() |
样品偏斜 |
kurt() |
峰度样本 |
quantile() |
样本分位数(%的值) |
cov() |
样本协方差 |
corr() |
样本相关性 |
ptp() |
极差 |
sem() |
标准误差 |
mad() |
平均绝对离差 |
类别型特征的描述性统计
描述类别型特征的分布状况,可以使用频数统计表。pandas库中实现频数统计的方法为 value_counts。
语法:
Series.value_counts(normalize = False,sort = True,升序= False,bins = None,dropna = True)
DataFrame.value_counts(subset=None, normalize=False, sort=True, ascending=False)
参数 | 说明 |
---|---|
normalize | 布尔值,默认为False。如果为True,则返回的对象将包含唯一值的相对频率。 |
sort | 布尔值,默认为True。按频率排序 |
ascending | 布尔值,默认为False。升序排列 |
bins | int,默认为None |
dropna | bool,默认为True |
subset | list-like, 计算唯一组合时要使用的列 |
data = np.random.randint(0, 7, size=50)
data
s = pd.Series(data)
s.value_counts()
pd.value_counts(data)
方法摘要
data.head()
查看数据前5行
data.tail()
查看数据倒数5行
data.info()
查看数据信息详情
pandas.Series.rename()
重命名
数据加载
pandas支持许多不同的文件格式或数据源(csv,excel,sql,json,parquet等),每种格式都有前缀[read_*
]。[read_*
]函数用来读取数据到熊猫,所述 [to_*
]方法被用于存储数据,[to_excel()
]方法将数据存储为excel文件。
读取csv文件
读取csv文件:
pandas.read_csv(filepath_or_buffer, sep=’,’, header=’infer’, names=None, index_col=None,
dtype=None, engine=None, nrows=None)
方法参数 | 参数解释 |
---|---|
filepath_or_buffer | 接收string,代表文件路径,无默认,该字符串可以是一个URL,有效的URL方案包括http, ftp,s3和file |
sep | 接收string。代表分隔符。read_csv默认为“ , ” ,read_table默认为制表符“[Tab]” 。 |
header | 接收int或sequence。表示是否将某行数据作为列名。默认为infer,表示自动识别。 |
names | header=None 时设置此字段使用列表初始化列名。 |
index_col | 接收int、sequence或False。表示选择哪列作为行索引,取值为sequence则代表多重索引。默 认为None。 |
usecols | 选择读取文件中的某些列。设置为为相应列的索引列表。 |
skiprows | 跳过行。可选择跳过前n行或给出跳过的行索引列表。 |
encoding | 编码。encoding代表文件的编码格式,常用的编码有utf-8、utf-16、gbk、gb2312、gb18030等。如果编 码指定错误数据将无法读取,IPython解释器会报解析错误。 |
写入文本:
DataFrame.to_csv(path_or_buf=None, sep=’,’, na_rep=”, columns=None, header=True,index=True,index_label=None,mode=’w’,encoding=None)
参数名称 | 说明 |
---|---|
path_or_buf | 接收string。代表文件路径。无默认。 |
sep | 接收string。代表分隔符。默认为“ , ” 。 |
na_rep | 接收string。代表缺失值。默认为“” 。 |
columns | 接收list。代表写出的列名。默认为None。 |
header | 接收boolean,代表是否将列名写出。默认为 True。 |
index | 接收boolean,代表是否将行名(索引)写 出。默认为True。 |
index_labels | 接收sequence。表示索引名。默认为None。 |
mode | 接收特定string。代表数据写入模式。默认 为w。 |
encoding | 接收特定string。代表存储文件的编码格式。 默认为None。 |
处理JSON
读取json:
pd.read_json('Filename.json')
方法参数 | 参数解释 |
---|---|
filepath_or_buffer | 文件路径 |
encoding | 编码。 |
写入json:to_json()
方法参数 | 参数解释 |
---|---|
filepath_or_buffer | 文件路径; 若设置为None,则返回json字符串 |
orient | 设置面向输出格式:['records', 'index', 'columns', 'values'] |
data = {'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]}
df = pd.DataFrame(data, index=['s1','s2','s3','s4'])
df.to_json(orient='records')
文本文件读取
使用read_table来读取文本文件
pandas.read_table(filepath_or_buffer, sep=’\t’, header=’infer’, names=None, index_col=None,
dtype=None, engine=None, nrows=None)
方法参数 | 参数解释 |
---|---|
filepath_or_buffer | 接收string,代表文件路径,无默认,该字符串可以是一个URL,有效的URL方案包括http, ftp,s3和file |
sep | 接收string。代表分隔符。read_csv默认为“ , ” ,read_table默认为制表符“[Tab]” 。 |
header | 接收int或sequence。表示是否将某行数据作为列名。默认为infer,表示自动识别。 |
names | header=None 时设置此字段使用列表初始化列名。 |
index_col | 接收int、sequence或False。表示选择哪列作为行索引,取值为sequence则代表多重索引。默 认为None。 |
usecols | 选择读取文件中的某些列。设置为为相应列的索引列表。 |
skiprows | 跳过行。可选择跳过前n行或给出跳过的行索引列表。 |
encoding | 编码。encoding代表文件的编码格式,常用的编码有utf-8、utf-16、gbk、gb2312、gb18030等。如果编 码指定错误数据将无法读取,IPython解释器会报解析错误。 |
读取Excel文件
pandas.read_excel(io, sheet_name=0, header=0, names=None, index_col=None, usecols=None, squeeze=False, dtype=None)
读取excel文件需要先安装xlrd库
pip install xlrd
数据清洗
检测与处理重复值
duplicated()函数
数据量较大,可以用duplicated()函数来查询重复的内容:
# 查询重复的内容
data[data.duplicated()]
# 统计重复行的数量
data.duplicated.sum()
# 发现有重复行时,可以用drop_duplicates()函数删除重复行
data = data.drop_duplicates()
pandas提供了一个名为drop_duplicates的去重方法。该方法只对DataFrame或者Series类型有效。这种方法不会改变数据原始排列,并且兼具代码简洁和运行稳定的特点。该方法不仅支持单一特征的数据去重,还能够依据DataFrame的其中一个或者几个特征进行去重操作。
DataFrame.drop_duplicates(subset=None, keep='first', inplace=False, ignore_index=False)
参数名 | 说明 |
---|---|
subset | 接收string或sequence。表示进行去重的列。默认为None,表示全部列。 |
keep | 接收特定string。表示重复时保留第几个数据。first:保留第一个。last:保留最后一个。false:只要有重复都不保留。默认为first。 |
inplace | 接收boolean。表示是否在原表上进行操作。默认为False。 |
示例:
import pandas as pd
data=pd.DataFrame(
{'A':[1,1,2,2],'B':['a','b','a','b']})
data.drop_duplicates('A', 'first', inplace=True)
data
检测与处理缺失值
数据中的某个或某些特征的值是不完整的,这些值称为缺失值。
pandas提供了识别缺失值的方法isnull()函数或isna()函数(两者作用类似)以及识别非缺失值的方法notnull(),这两种方法在使用时返回的都是布尔值True和False。
isnull()函数的作用是判断是否是空值,若是空值就赋予True,否则赋予False。
结合sum函数和isnull、notnull函数,可以检测数据中缺失值的分布以及数据中一共含有多少缺失值。isnull和notnull之间结果正好相反,因此使用其中任意一个都可以判断出数据中缺失值的位置。
data.isnull() # 或者写成data.isna()
data.isnull().sum() # 共有多少各缺失值
1)删除法
删除法分为删除观测记录和删除特征两种,pandas中提供了简便的删除缺失值的方法dropna,该方法既可以删除观测记录,亦可以删除特征。
pandas.DataFrame.dropna(
self, axis=0, how='any', thresh=None, subset=None, inplace=False)
参数名 | 说明 |
---|---|
axis | 接收0或1。表示轴向,0为删除记录(行),1为删除特征(列)。默认为0。 |
how | 接收特定string。表示删除的形式。any表示只要有缺失值存在就执行删除操作。all表示当且仅当全部为缺失值时执行删除操作。默认为any。 |
subset | 接收类array数据。表示进行去重的列∕行。默认为None,表示所有列/行。 |
inplace | 接收boolean。表示是否在原表上进行操作。默认为False。 |
示例:
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
df.iloc[0,1] = np.nan
df.iloc[1,2] = np.nan
# df1接收,判断是否有缺失数据 NaN, 为 True 表示缺失数据:
df1=df.isnull()
df1
#每一列的缺失数据
df2=df.isnull().sum()
df2
#整个表的缺失数据
df2.sum()
# df1接收,去掉有 NaN 的行, 可以使用 dropna 一旦出现NaN就去除整行
df3=df.dropna(axis=0,how='any')
df3
2)替换法
替换法是指用一个特定的值替换缺失值。
特征可分为离散型和连续型,两者出现缺失值时的处理方法也是不同的。
缺失值所在特征为连续型时,通常利用其均值、中位数和众数等描述其集中趋势的统计量来代替缺失值。缺失值所在特征为离散型时,则经常选择使用众数来替换缺失值。
插补方法 | 方法描述 |
---|---|
均值/中位数/众数插补 | 根据属性值的类型用该属性取值的平均数/中位数/众数进行插补 |
使用固定值 | 将缺失的属性值用一个常量替换。 |
最近临插补 | 在记录中找到与缺失样本最接近的样本的该属性值插补 |
回归方法 | 对带有缺失值的变量,根据已有数据和与其有关的其他变量(因变量)的数据建立拟合模型来预测缺失的属性值 |
插值法 | 插值法是利用已知点建立合适的插值函数f(x),未知值由对应点x求出的函数值f(x)近似代替 |
data = data.fillna(data.mean())
data = data.fillna(data.median())
pandas库中提供了缺失值替换的方法名为fillna,其基本语法如下:
pandas.DataFrame.fillna(value=None, method=None,
axis=None, inplace=False, limit=None)
常用参数及其说明如下:
参数名 | 说明 |
---|---|
value | 接收scalar,dict,Series或者DataFrame。表示用来替换缺失值的值。无默认。 |
method | 接收特定string。backfill或bfill表示使用下一个非缺失值填补缺失值。pad或ffill表示使用上一个非缺失值填补缺失值。默认为None。 |
axis | 接收0或1。表示轴向。默认为1。 |
inplace | 接收boolean。表示是否在原表上进行操作。默认为False。 |
limit | 接收int。表示填补缺失值个数上限,超过则不进行填补。默认为None。 |
案例:
# df2接收,如果是将 NaN 的值用其他值代替, 比如代替成 0:
dates = pd.date_range('20130101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6,4)),index=dates, columns=['A','B','C','D'])
df.iloc[0,1] = np.nan
df.iloc[1,2] = np.nan
# 固定值填补
df2=df.fillna(value=0)
df2
# 相似值填补
df3=df.fillna(method='pad', axis=1)
df3
# 还可以设置method='backfill'或method='bfill',表示用空值下方的值来替换空值,如果下方的值不# 存在或也为空值,则不替换。
3)插值法
删除法简单易行,但是会引起数据结构变动,样本减少;替换法使用难度较低,但是会影响数据的标准差,导致信息量变动。在面对数据缺失问题时,除了这两种方法之外,还有一种常用的方法—插值法。
scipy提供了插值算法可以通过一组散点得到一个符合一定规律插值器函数。这样当我们给插值器函数更多未知x,插值函数将会返回相应的y用于填补缺失值。
需求:统计各小区彩民买彩票的情况:
彩民数量 | 彩票购买量 |
---|---|
30 | 100注 |
40 | 120注 |
50 | 135注 |
60 | 155注 |
45 | - |
65 | 170注 |
scipy提供的插值方法如下:
import scipy.interpolate as si
func = si.interp1d(
离散水平坐标,
离散垂直坐标,
kind=插值算法(缺省为线性插值)
)
案例:
import numpy as np
import matplotlib.pyplot as mp
import scipy.interpolate as si
x = [30, 40, 50, 60, 65]
y = [100, 120, 135, 155, 170]
mp.scatter(x, y)
xs = np.linspace(min(x), max(x), 200)
# 通过这些散点,构建一个线性插值函数
linear = si.interp1d(x, y, kind='cubic')
print(linear(45))
ys = linear(xs)
mp.plot(xs, ys)
mp.show()
检测与处理异常值
异常值是远离大部分样本数据的数据值,异常值产生的原因可能是录入错误或偶然因素。通常情况下,数据样本中的异常值属于需要清理的数据,但在某些特定的情况下,异常值也有它存在的意义。
异常值:是指数据中个别值的数值明显偏离其余的数值,有时也称为离群点,检测异常值就是检验数据中是否有录入错误以及是否含有不合理的数据。
异常值是指一组数据中偏离平均值两倍标准差的数据,而偏离平均值3倍标准差以上的数据则成为高度异常值,我们通常对高度异常值更感兴趣。
异常值的存在对数据分析十分危险,如果计算分析过程的数据有异常值,那么会对结果会产生不良影响,从而导致分析结果产生偏差乃至错误。
从广义上说,不仅存在数值型异常值,某些字符型数据中也同样存在异常值,如正常邮件中夹杂的垃圾邮件或正常交易中夹杂的欺诈交易等,都是异常的。
我们发现异常值后,武断地对其进行删除是绝对不理智的,聪明的数据分析师会谨慎地考虑其背后的含义。尽管一百个数据样本中未必能有一个样本中的异常值是具有特殊意义的,但谨慎总是没错的。
字符型异常值往往比数字型异常值更重要,实际生活中有很多侦测欺诈交易或辨别流失客户的问题。但遗憾的是,发现字符型异常值要比数字型异常值困难得多。
除却其自身的特殊含义外,异常值也是数据样本中不可或缺的一部分。过多的距离数据均值、过远的异常值可能提示我们数据样本的容量太小,难以代表整体数据;极个别的异常值则可能代表的只是偶然情况,不必挂怀。
真实世界是不完美的,从真实世界中收集的数据有许多疏漏和噪声,这也正是为什么我们要花费许多精力在数据预处理上。对于数据样本中的异常值我们没必要赶尽杀绝,在清洗数据时,正确的做法是剔除那些明显错误的数据,保留那些稍有瑕疵的数据,这个度应该根据数据本身的表现去掌握。这样做的好处是让模型尽可能地贴近现实,从而提高预测的精度,毕竟数学模型还是要用来解决现实世界中的实际问题的。
常用的异常值检测主要为3σ原则和箱线图分析两种方法。
1)简单统计量分析
先对变量做一个描述性统计,找出哪些数据是不合理的,最常用的统计量是求最大值和最小值,判断变量是否在这个区间。
2)3σ原则
3σ原则又称为拉依达法则。该法则就是先假设一组检测数据只含有随机误差,对原始数据进行计算处理得到标准差,然后按一定的概率确定一个区间,认为误差超过这个区间的就属于异常值。
这种判别处理方法仅适用于对正态或近似正态分布的样本数据进行处理。如果不符合正态分布,可以用远离平均值的多少倍标准差来表示。
3)箱线图分析
箱线图提供了识别异常值的一个标准,即异常值通常被定义为小于QL-1.5IQR或大于QU+1.5IQR的值。
QL称为下四分位数,表示全部观察值中有四分之一的数据取值比它小。
QU称为上四分位数,表示全部观察值中有四分之一的数据取值比它大。
IQR称为四分位数间距,是上四分位数QU与下四分
位数QL之差,其间包含了全部观察值的一半。
箱线图依据实际数据绘制,真实、直观地表现出了数据分布的本来面貌,且没有对数据做任何限制性要求,其判断异常值的标准以四分位数和四分位数间距为基础。
四分位数给出了数据分布的中心、散布和形状的某种指示,箱形图判断异常值的标准以四分位数和四分位距为基础,箱线图识别异常值的结果比较客观,因此在识别异常值方面具有一定的优越性。
箱线共由五个数值点构成,分别是最小观察值(下边缘),25%分位数(QL),中位数,75%分位数(QU),最大观察值(上边缘)。
特别说明:箱盒图里面的极大值(上边缘值)并非最大值,极小值(下边缘值)也不是最小值。
如果数据有存在离群点即异常值,他们超出最大或者最小观察值,此时将离群点以“圆点”形式进行展示。
异常值的处理方法:
异常值处理方法 | 方法描述 |
---|---|
删除含有异常值的记录 | 直接删除含有异常值的记录 |
视为缺失值 | 将异常值视为缺失值,按照缺失值方法处理。 |
平均值修正 | 用前后两个观测值的均值进行修正。 |
不处理 | 直接在含有异常值的数据集上进行数据建模。 |
pandas可视化
基本绘图
Series数据可视化
Series提供了plot方法以index作为x,以value作为y,完成数据可视化:
ts = pd.Series(np.random.randn(10),
index=pd.date_range('1/1/2000', periods=10))
ts.plot()
DataFrame数据可视化
DataFrame提供了plot方法可以指定某一列作为x,某一列作为y,完成数据可视化:
df3 = pd.DataFrame(np.random.randn(10, 2),
columns=['B', 'C'])
df3['A'] = np.arange(len(df3))
df3.plot(x='A', y=['B', 'C'])
高级绘图
plot()方法可以通过kind关键字参数提供不同的图像类型,包括:
类型 | 说明 |
---|---|
bar or barh |
柱状图(条形图) |
hist |
直方图 |
box |
箱线图 |
scatter |
散点图 |
pie |
饼状图 |
line |
折线图 |
area |
面积图 |
kde |
密度估计图(主要对柱状图添加Kernel 概率密度线) |
相关API如下:
# 柱状图
series.plot.bar()
dataFrame.plot.bar()
dataFrame.plot.barh()
直方图
# 直方图
series.plot.hist(alpha=0.5, bins=5)
dataFrame.plot.hist(alpha=0.5, bins=5)
# 除了df.hist()函数,还可以使用pandas库中的通用绘图代码绘制图表
dataFrame.plot(kind='hist')
dataFrame['column_name'].plot(kind='hist')
散点图
# 散点图
df.plot.scatter(x='a', y='b', c=col, colormap='');
饼状图
# 饼状图
series.plot.pie(figsize=(6, 6))
dataFrame.plot.pie(subplots=True, figsize=(6, 6), layout=(2, 2))
箱线图
# 箱线图
# 先找出一组数据的上边缘、下边缘、中位数和两个四分位数;然后, 连接两个四分位数画出箱体;再将上边缘和下边缘与箱体相连接,中位数在箱体中间
df.plot.box()
# 分组箱线图
df.boxplot(by='X')
箱线图反应一组数据的集中趋势,四分位数的差可以反映一组数据的离散情况:
- 中位数高,表示平均水平较高;反之则表示平均水平较低。
- 箱子短,表示数据集中;箱子长,表示数据分散。