python数据分析

数据处理

数据加载

首先,我们需要将收集的数据加载到内存中,才能进行进一步的操作。pandas提供了非常多的读取数据的函数,分别应用在各种数据源环境中,我们常用的函数为:

read_csv,
read_table,
read_sql,
说明:

read_csv与read_table默认使用的分隔符不同:,\t

常用参数

read_csv与read_table常用的参数:

sep / delimiter,
header,
names,
index_col,
usecols,

import numpy as np
import pandas as pd
# ?pd.read_csv
# 读取csv文件,返回一个DataFrame类型的对象。
# 在读取的时候,默认会将第一行记录当成标题。
# 如果没有标题,我们可以指定header=None。
# 如果header为None,read_csv默认会自己生成列标签。(0, 1, 2, 3……)。
# df = pd.read_csv("./data/iris.csv", header=None,skiprows=1)

# read_csv默认使用逗号作为分隔符,我们可以使用sep或delimiter来指定分隔符。
# df = pd.read_csv("./data/1.txt", sep="\t")
# df = pd.read_table("./data/1.txt",header=None)

# 我们可以通过names参数来指定列标签(标题)
# df = pd.read_table("./data/1.txt",header=None,names=["姓名", "科目", "分数"])

# 如果我们需要自己指定某列充当行索引(例如,数据库,数据表中的主键)
# 可以使用index_col参数来进行设置。
# df = pd.read_table("./data/1.txt",header=None, index_col=0)
                
# 我们可以使用usecols来控制需要哪些列。如果某列充当索引列(index_col),
# 则充当索引列的标签,也需要指定在usecols中。
df = pd.read_table("./data/1.txt", index_col=0, usecols=[0,1, 2])
display(df)

'''

subject	score
name		
huangbo	math	81
huangbo	english	87
huangbo	computer	57
xuzheng	math	89
xuzheng	english	92
xuzheng	computer	83
wangbaoqiang	math	78
wangbaoqiang	english	88
wangbaoqiang	computer	90
dengchao	math	88
dengchao	english	78
dengchao	computer	58
'''
# 读取 sqlite 数据
import sqlite3

conn = sqlite3.connect("test.db")

# 创建数据表
conn.execute("create table if not exists person (id int primary key, name varchar(30), age int)")
# 向表中插入数据
conn.execute("insert into person(id, name ,age) values(38, 'aaa', 17)")

# 注意提交事务
# conn.commit()


# 从数据库中读取数据,sql指定查询的数据(用来构造DataFrame)
t = pd.read_sql("select id, name, age from person", conn)
display(t)
conn.close()

'''
	id	name	age
0	38	aaa	17
'''
# 读取 MySQL 数据
# 先安装 pip install pymysql

import pymysql

conn = pymysql.connect(host='39.96.5.6',
                       port=3306, user='root', 
                       passwd='root', db='test', charset='utf8')
t = pd.read_sql(sql="select * from pv_province",con=conn)
display(t)

'''
	id	ds	province	pv
0	1979	2019-03-18 11:00	山东	10
1	1980	2019-03-17 12:58	广西	57
2	1981	2019-03-16 01:35	云南	8
3	1982	2019-03-18 22:25	河北	37
4	1983	2019-03-18 21:59	江西	37
5	1984	2019-03-18 08:27	云南	55
6	1985	2019-03-16 04:54	新疆	99
7	1986	2019-03-19 18:49	浙江	71

#太长,不放了
'''

写入文件

DataFrame与Series对象的to_csv方法,可以将数据写入文件或者指定的数据流中。

to_csv

常用参数

sep 分隔符,
header 是否写入标题行,默认为True。

na_rep 空值的表示,

index 是否写入索引,默认为True。

index_label 索引字段的名称,
columns 写入的字段,默认为全部写入。

df = pd.DataFrame(np.arange(15).reshape(3, 5))
df[5]=np.nan
display(df)

# 默认以逗号作为分隔符,可以使用sep来自定义分隔符。
# df.to_csv("wf.txt", sep="-")

# 默认情况会写入标题(行标签索引)。可以使用header进行设置是否写入标题。True,写入(默认),False不写入。
# df.to_csv("wf.txt", header=False,sep="&")

# 默认情况下,空值不显示,我们可以自定义空值的显式效果(内容)。
df.to_csv("wf.txt", na_rep="空",header=False)

# 行索引,默认写入,我们可以通过参数index来设置是否写入行索引。True,写入(默认), False,不写入。
#df.to_csv("wf.txt", index=False)

# 可以通过index_label来设置行索引的名称。
df.to_csv("wf.txt", index_label="index_name")

# 我们可以通过colomns列来设置那些列写入到文件中。默认为写入所有列。
# df.to_csv("wf.txt", columns=[1, 3])

'''
0	1	2	3	4	5
0	0	1	2	3	4	NaN
1	5	6	7	8	9	NaN
2	10	11	12	13	14	NaN
'''

数据清洗

我们需要对数据进行一些预处理操作,才能用到后续的数据分析与机器学习中。这是因为,无论数据的来源为何处,我们都不可能保证数据一定是准确无误的。

数据清洗可以包含以下几方面内容:

处理缺失值,
处理异常值,
处理重复值,

缺失值处理

发现缺失值,

Pandas中,会将 float 类型的 nan 与 None 视为缺失值,我们可以通过如下方法来检测缺失值:

info,
isnull,
notnull,

说明:

判断是否存在空值,可以将 isnull 与 any 或 all 结合使用。

丢弃缺失值

对于缺失值,我们可以将其进行丢弃处理(dropna)。

说明:

how:指定dropna丢弃缺失值的行为,默认为any。
axis:指定丢弃行或者丢弃列(默认为丢弃行)。
thresh:当非空数值达到该值时,保留数据,否则删除。
inplace:指定是否就地修改,默认为False。

填充缺失值

我们也可以对缺失值进行填充(fillna)。

说明:

value:填充所使用的值。可以是一个字典,这样就可以为DataFrame的不同列指定不同的填充值。

method:指定前值(上一个有效值)填充(pad / ffill),还是后值(下一个有效值)填充(backfill / bfill)。

limit:如果指定method,表示最大连续NaN的填充数量,如果没有指定method,则表示最大的NaN填充数量。

inplace:指定是否就地修改,默认为False。

df = pd.read_csv("./data/spider.csv", header=None)
display(df)

# 检测缺失值,首先可以调用info方法进行整体查看。
#info方法可以显示DataFrame中每列的相关信息。
# display(df.isnull())

# 没有空值。
df[2].isnull().any()
df[3].isnull().any()

'''
0	1	2	3	4
0	2015-4-28	http://www.apinpai.com/	采蘑菇的小姑娘;小蓓蕾组合;90;儿歌	216.0	1392.68
1	2015-8-24	http://www.apinpai.com/	我;张国荣;80;励志	273.0	1447.17
2	2015-12-14	http://www.movie.com/dor/	《恶棍天使》;2015.12.24;2016.2.13;天津橙子映像传媒有限公司、北京光线影...	NaN	NaN
3	2015-4-2	http://bj.qu114.com/	my way;张敬轩;90;励志	52.0	337.27
#不放了

1396 rows × 5 columns

True

'''
# 删除空值 dropna
# df
# df.dropna()

# 默认情况下,how的值为any,表示只要存在空值,就丢弃行(列),我们可以指定为all,表示所有值为空值时,才进行删除。
# df.dropna(how="all")

# 默认,存在空值,会丢弃行,我们可以指定丢弃列。(axis=0表示按行删除,axis=1表示按列删除。)
# df.dropna(axis=1)

# 有时候,how的any与all可能都不太合适。any的条件太宽松,all又太严格。我们可以自定义删除的门槛。
# 通过thresh来指定门槛,删除 非 NA 数低于 n 的行
# df.dropna(thresh=3)

# 可以使用inplace来设置是否进行就地修改。默认为False。
# 填充值 fillna

# 使用固定值来填充所有的列。
df.fillna(10000)

# 可以提供一个字典,这样就能够为不同的列,填充不同的值。
# 字典的key指定索引,value指定填充值。
# df.fillna({3: 5000, 4:1000})

# 我们可以使用method,来指定向前(后)填充。
# 此种情况下,主要应用是记录之间有着紧密的关联趋势,例如,房价,股票。

# 使用上一个有效值进行填充。
df.fillna(method="ffill")

# 使用下一个有效值进行填充。
# df.fillna(method="bfill")

# limit参数。如果指定method,则表示最多连续填充。
# 如果没有指定method,则表示总共填充

# df.fillna(method="bfill", limit=1)
df.fillna(value=50000, limit=2)

# 设置是否就地修改,默认为False。
# df.fillna(inplace=True)
df.describe()

# 如果DataFrame当中存在数值列,则describe只显示数值列
#df.drop([3,4],axis=1).describe()
df[[0, 1, 2]].describe()

'''
0	1	2
count	1396	1396	1396
unique	213	23	60
top	2015-12-25	http://www.movie.com/bor/	空壳;陈慧敏;90;伤感
freq	16	100	32
'''

重复值处理

在处理数据中,可能会出现重复的数据,我们通常需要将重复的记录删除。

发现重复值

我们可以通过duplicated方法来发现重复值。该方法返回Series类型的对象,值为布尔类型,表示是否与上一行重复。

参数说明:

subset:指定依据哪些列判断是否重复,默认为所有列。

keep:指定记录被标记为重复(True)的规则。默认为first。

删除重复值

通过drop_duplicates可以删除重复值。

参数说明:

subset:指定依据哪些列判断是否重复,默认为所有列。

keep:指定记录删除(或保留)的规则。默认为First。

inplace:指定是否就地修改,默认为False。

# 检测重复值
# df.duplicated()

# 查看重复记录
df[df.duplicated()]

# 如果需要查看所有重复的记录,可以使用keep参数。
# keep:
# frist:前面的记录标记为True。
# last:后面的记录标记为True。
# False:所有记录都标记为True。

# df[df.duplicated(keep=False)]

# 可以使用subset参数来指定重复的规则。默认为所有列一致才认为是重复的。
# 比如规则改为:只要第0,与第1列相同,则认为是重复的。
# df.duplicated(subset=[0, 1])

# 删除重复值
# df.drop_duplicates()

数据过滤

可以使用布尔数组或者索引数组的方式来过滤数据。

另外,也可以用DataFrame类的query方法来进行数据过滤。在query方法中也可以使用外面定义的变量,需要在变量前加上@。

# 数据过滤通常的方式:
# 通过判断条件生成一个布尔类型的数组,然后,DataFrame使用该布尔数组进行行过滤。

# display(df)
# df[0] == "2015-8-24"
# display(df[df[0] == "2015-4-28"])

# 过滤的第二种方式,通过列索引
df.columns = ["data", "url", "name", "num", "num1"]

# df.query("data == '2015-12-25'")

# 也可以在变量名称前加上@符号,进行引用变量。
s = "2015-8-24"
df.query("data == @s")

'''
data	url	name	num	num1
1	2015-8-24	http://www.apinpai.com/	我;张国荣;80;励志	273.0	1447.17
33	2015-8-24	http://www.mouxiao.com/	firework;katy perry;90;励志	201.0	1539.84
539	2015-8-24	http://www.qudee.com/	小兔子乖乖;小蓓蕾组合;90;儿歌	157.0	1246.00
816	2015-8-24	http://beijing.hand2hand.cn/	从头再来;刘欢;90;励志	25.0	648.36
1038	2015-8-24	http://www.yifawang.cn/	一起走过的日子;刘德华;80;伤感	148.0	1720.29
1176	2015-8-24	http://www.mouxiao.com/	借过;容祖儿;90;伤感	164.0	335.96
1300	2015-8-24	http://www.010y.com/	星;邓丽君;80;励志	10.0	153.95
'''

数据转换

应用与映射

Series与DataFrame对象可以进行行(列)或元素级别的映射转换操作。对于Series,可以调用apply或map方法。

对于DataFrame,可以调用apply或applymap方法。

apply:通过函数实现映射转换。【Series传递元素,DataFrame传递行或列。】

map:对当前Series的值进行映射转换。参数可以是一个Series,一个字典或者是一个函数。

applymap:通过函数实现元素级的映射转换。

替换

Series或DataFrame可以通过replace方法可以实现元素值的替换操作。

to_replace:被替换值,支持单一值,列表,字典,正则表达式。

regex:是否使用正则表达式,默认为False。

字符串矢量级运算

Series含有一个str属性,通过str能够进行字符串的矢量级运算。

import numpy as np
import pandas as pd
df = pd.read_csv("./data/spider.csv", header=None)

# 显示前n行,默认为5.
# display(df.head())

# 显示后n行
# df.tail()

# 随机采样,随机选择n行。n默认为1.
# df.sample(12)

s = df[0]
display(s)

# replace的参数,可以是单一值。
# s.replace("2015-4-28", "2015-5-28")

# replace也支持一个列表。将列表中的每一个元素都替换成2015-5-28(value参数指定的值。)
# s.replace(["2015-4-28", "2015-8-24", "2015-12-14"], "2015-5-28")

# 将多个值,每个值都替换成不同的值。
s.replace(["2015-4-28", "2015-8-24"], ["2015-4-29", "2015-8-25"])

# replace也支持字典的形式:用来将多个值,替换成不同的值。key指定要替换的值,对应的value指定要替换成什么值。
s.replace({"2015-4-28":"2015-4-29", "2018-8-24":"2015-8-25"})

# replace也支持正则表达式的形式,需要将regex参数设置为True。(默认为False)
s.replace(r"[0-9]{4}-4-28", "2017-4-28", regex=True)


'''
0        2015-4-28
1        2015-8-24
2       2015-12-14
3         2015-4-2
4       2015-12-19
5        2015-5-28
6         2015-8-1

Name: 0, Length: 1396, dtype: object
'''
# replace的操作我们也可以通过apply或map来实现。

def t(item):
    return "2034" if item == "2015-4-28" else item

# s.map({"2015-4-28":"2034"})
s.map(t)

'''
0             2034
1        2015-8-24
2       2015-12-14
3         2015-4-2
4       2015-12-19

Name: 0, Length: 1396, dtype: object
'''
# Series的str属性
# s = pd.Series([1, 2, 3])
# display(s.str) # 错误,使用Series的str属性时,需要Series元素的值是str(字符串)类型。



s = pd.Series(["abc", "def", "kefe"])
# str 能够进行字符串的矢量级运算。

display(s.str)
print("#"*20)
display(s.str.upper())
print("#"*20)
display(s.str.contains("b"))

'''
<pandas.core.strings.StringMethods at 0x28ea5e82e10>
####################
0     ABC
1     DEF
2    KEFE
dtype: object
####################
0     True
1    False
2    False
dtype: bool
'''
# df[1].str.startswith("http://www.movie.com/dor/")
# 选择(过滤)所有电影相关的记录

t = df[df[1].str.startswith("http://www.movie.com/dor/")]
# display(t)

# 一列拆分成多列,在split的同时,增加参数expand的值为True。如果没有使用expand(默认为Fasle),则使用一个列表来存放拆分之后的元素。
sp = t[2].str.split(";", expand=True)
# display(sp)


sp[7] = sp[7].str.replace("票房(万)", "")
# display(sp[7])
# display(sp.describe())
#display(sp[7].mean())

# 注意:我们执行替换之后,对象的类型是字符串。
# 对类型进行转换,转换成我们需要的类型(float)
sp[7] = sp[7].astype(np.float64)
display(sp.describe())
display(sp[7].mean())

'''
7
count	99.000000
mean	12968.721212
std	18580.349840
min	20.300000
25%	785.200000
50%	5020.300000
75%	15973.900000
max	74430.200000
12968.721212121203
'''

数据合并

concat

我们可以通过DataFrame或Series类型的concat方法,来进行连接操作,连接时,会根据索引进行对齐。

axis:指定连接轴,默认为0。

join:指定连接方式,默认为外连接。【outer:并集,inner:交集】

keys:可以用来区分不同的数据组。

join_axes:指定连接结果集中保留的索引。

ignore_index:忽略原来连接的索引,创建新的整数序列索引,默认为False。

head = df.head()
tail = df.tail()
# display(head, tail)

tail.columns = [0, 1, 2, 3, 5]
# 在进行concat拼接(堆叠),时,会根据索引进行对齐。如果无法对齐,会产生空值。(NaN)
pd.concat((head, tail))

# 通过轴axis指定堆叠方向。0竖直方向,1水平方向。
# pd.concat((head, tail), axis=0)

# 我们可以通过join指定连接方式。(outer,结果集显示并集, inner结果集显示交集。)
# pd.concat((head, tail), join="inner")

# 可以通过keys观看数据来源那一张表。(产生一个层级索引)
# pd.concat((head, tail), keys=["head", "tail"])

# 通过join_axes指定要保留的索引。
# pd.concat((head, tail), join_axes=[head.columns])
# pd.concat((head, tail), join_axes=[tail.columns])

# 可以通过ignore_index设置为True,忽略以前的索引,重新创建连续的索引。
# pd.concat((head, tail), ignore_index=True)

'''
0	1	2	3	4	5
0	2015-4-28	http://www.apinpai.com/	采蘑菇的小姑娘;小蓓蕾组合;90;儿歌	216.0	1392.68	NaN
1	2015-8-24	http://www.apinpai.com/	我;张国荣;80;励志	273.0	1447.17	NaN
2	2015-12-14	http://www.movie.com/dor/	《恶棍天使》;2015.12.24;2016.2.13;天津橙子映像传媒有限公司、北京光线影...	NaN	NaN	NaN
3	2015-4-2	http://bj.qu114.com/	my way;张敬轩;90;励志	52.0	337.27	NaN
4	2015-12-19	http://www.movie.com/dor/	《失孤》;2015.3.20;2015.5.3;华谊兄弟传媒集团、源合圣影视、映艺娱乐;彭三...	NaN	NaN	NaN
1391	2015-7-31	http://beijing.faxinxi.cn/	同道中人;张国荣;80;励志	87.0	NaN	927.30
1392	2015-4-20	http://www.denghuo.com/	忘记拥抱;a-lin;80;伤感	31.0	NaN	684.56
1393	2015-4-2	http://www.yifawang.cn/	路...一直都在;陈奕迅;90;励志	47.0	NaN	1419.74
1394	2015-4-15	http://www.wuhan58.com/index.php	像我一样骄傲;赵传;80;励志	124.0	NaN	1434.67
1395	2015-5-16	http://www.favolist.com/	最冷一天;陈奕迅;90;伤感	103.0	NaN	1020.50
'''

append

在对行进行连接时,也可以使用Series或DataFrame的append方法,不过只能竖直合并

merge

通过pandas或DataFrame的merge方法,可以进行两个DataFrame的连接,这种连接类似于SQL中对两张表进行的join连接。

how:指定连接方式。可以是inner, outer, left, right,默认为inner。

on 指定连接使用的列(该列必须同时出现在两个DataFrame中),默认使用两个DataFrame中的所有同名列进行连接。

left_on / right_on:指定左右DataFrame中连接所使用的列。

left_index / right_index:是否将左边(右边)DataFrame中的索引作为连接列,默认为False。

suffixes:当两个DataFrame列名相同时,指定每个列名的后缀(用来区分),默认为x与y。

df = pd.DataFrame([[100, 2, 3], [3, 4, 5], [7, 8, 9]])
df2 = pd.DataFrame([[1, 2, 4], [3, 4, 6], [10, 11, 12]], columns=[0, 1, 3])
display(df, df2)
# 根据所有同名字段(标签名)进行等值连接。
df.merge(df2)
# 可以通过how指定连接方式。默认为内连接。
# df.merge(df2, how="left")
# df.merge(df2, how="right")
# df.merge(df2, how="outer")

# 我们还可以通过on来指定连接的列(on指定的列必须同时出现在两个表之中)。(默认使用所有同名的列进行等值连接)
# display(df.merge(df2))
# display(df.merge(df2, on=1))

# 如果连接的列名不同,则我们可以使用left_on与right_on参数分别指定左,右两张表用来进行等值连接的索引名。
# df.merge(df2, left_on=1, right_on=3)

# 我们可以通过left_index,right_index来指定,是否使用索引来充当连接条件。True,是,False,不是。
# 注意:left_index(right_index)与left_on(right_on)不能同时指定。
# df.merge(df2, left_index=True, right_index=True)
# df.merge(df2, left_index=True, right_on=1)

# 我们也可以自定义连接的后缀。(两张表存在同名字段时,可以使用。默认为_x,_y)
# df.merge(df2, left_index=True, right_index=True, suffixes=["_table1", "_table2"])

'''
	0	1	2
0	100	2	3
1	3	4	5
2	7	8	9
0	1	3
0	1	2	4
1	3	4	6
2	10	11	12
0	1	2	3
0	3	4	5	6
'''

join

与merge方法类似,但是默认使用索引进行连接。

how:指定连接方式。可以是inner, outer, left, right,默认为left。

on:设置当前DataFrame对象使用哪个列与参数对象的索引进行连接。

lsuffix / rsuffix:当两个DataFrame列名相同时,指定每个列名的后缀(用来区分),如果不指定,列名相同会产生错误。

# df = pd.DataFrame([[100, 2, 3], [3, 4, 5], [7, 8, 9]])
# df2 = pd.DataFrame([[1, 2, 4], [3, 4, 6], [10, 11, 12]], columns=[0, 1, 3], index=[0, 1, 3])
df = pd.DataFrame([["a1", 2, 3],["a1", 2, 5], ["a2", 4, 5], ["b", 8, 9]],columns=["marea","aa","bb"])
df2 = pd.DataFrame([["a1", "A", 4], ["a2", "A", 6], ["c", "B", 12]], columns=["marea", "area", "count"])
df=df.set_index("marea")
df2=df2.set_index("marea")
display(df, df2)
df=df.join(df2)
display(df)
# join与merge类似,都是进行两张表的连接。
# 不同:
#1 merge默认进行的内连接(inner),join默认进行的左外连接(left)。
#2 当出现同名字段(列索引)时,merge可以自动补后缀(_x, _y),但是join不会自动补后缀,而是会产生错误。
#3 merge默认使用同名的列进行等值连接。join默认使用左右两表的索引进行连接。
#4 merge中on参数,指定两张表中共同的字段,而join中on参数,仅指定左表中的字段(右表依然使用索引)。

# 如果没有指定连接方式,默认为左外连接(left)
df.join(df2,lsuffix="_x", rsuffix="_y")
# 我们可以通过how指定连接方式。
# df.join(df2, lsuffix="_x", rsuffix="_y", how="outer")

# on参数指定当前的表中使用哪个列与参数表(右侧表)的索引进行连接。
# df.join(df2, lsuffix="_x", rsuffix="_y", on=0)

'''
	aa	bb
marea		
a1	2	3
a1	2	5
a2	4	5
b	8	9
area	count
marea		
a1	A	4
a2	A	6
c	B	12
aa	bb	area	count
marea				
a1	2	3	A	4.0
a1	2	5	A	4.0
a2	4	5	A	6.0
b	8	9	NaN	NaN
aa	bb	area_x	count_x	area_y	count_y
marea						
a1	2	3	A	4.0	A	4.0
a1	2	5	A	4.0	A	4.0
a2	4	5	A	6.0	A	6.0
b	8	9	NaN	NaN	NaN	NaN
'''
posted @ 2019-07-11 10:15  Erio  阅读(478)  评论(0编辑  收藏  举报