上节Pandas学会了吗?那我可教你进阶啦~

Pandas世界来去自如:pandas的I/O

老生常谈,从基础来看,我们仍然关心的是pandas对于外部数据是如何交互的。


结构化数据输入输出

  • read_csv 与 to_csv 是一对输入输出工具,read_csv 直接返回 pandas.DataFrame ,而 to_csv 只要执行命令即可写文件。
  • read_table:功能类似
  • read_fwf:操作fixed width file
  • read_excel与tp_excel方便的与Excel交互

还记得上节课的例子么?

  • header 表示数据中是否存在列名,如果在第零行就写零,并且开始读数据时跳过相应的行数,不存在可以写 none 。
  • names 表示要用给定的列名作为最终的列名。
  • encoding 表示数据集的字符编码,通常而言一份数据为了方便的进行文件传输都以utf-8作为标准。

提问:下列例子中,header = 4 , names = cnames 时,究竟能读到什么样的数据?

print(cnames)
irisdata = pd.read_csv("SIEP3_Iris.txt", header = None, name = cnames, encoding = "utf-8")
irisdata[::30]

希望了解全部参数的请移步API:
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html

这里介绍一些常用参数:

  • 读取处理:
  1. skiprows :跳过一定的行数
  2. nrows :仅读取一定的行数
  3. skipfooter :尾部有固定的行数永不读取
  4. skip_blank_lines :空行跳过
  • 内容处理:
  1. sep/delimiter :分隔符很重要,常见的有逗号、空格和tab("\t")。
  2. na_values :指定应该被当作 na_values 的数值。
  3. thousands:处理数值类型时,每千位分隔符并不统一(1.234.567.89或者1,234,567,89都有可能),此时要把字符串转换成数字需要指明千位符。
  • 收尾处理:
  1. index_col :将真实的某列(列的数目,甚至列名)当作index。
  2. squeeze :仅读到一列时,不再保存为pandas.DataFrame而是pandas.Series。

Excel。。。?

对于存储着极为规整数据的Excel而言,其实是没必要一定要用Excel来存数据,尽管pandas也非常友好的提供了I/O接口。

irisdata.to_excel("SIPE3_irisdata.xls", index = None, encoding = "utf-8")
irisdata_from_excel = pd.read_excel("SIPE3_irisdata.xls", header = 0, encoding = "utf-8")
irisdata_from_excel[::30]

唯一重要的参数是:sheetname = k, 标志着一个Excel的第k个sheet页将会被取出。(默认从0开始)


半结构化数据

JSON :网络传输中常用的一种数据格式。
仔细看一下,实际上这就是我们平时收集到异源数据的风格是一致的。

  • 列名不能完全匹配。
  • 关联键可能并不唯一。
  • 元数据被保存在数据里。
json_data = [
	{"name":"Wang","sal":50000,"job":"VP"},
	{"name":"Zhang","job":"Manager","report":"VP"},
	{"name":"Li","sal":5000,"report":"Manager"}
]
data_employee = pd.read_json(json.dumps(json_data))
data_employee_ri = data_employee.reindex(columns = ["name","job","sal","report"])
print(data_employee_ri)

数据库连接流程(Optional)

使用下列包,通过数据库配置建立Connection。

  • pymysql
  • pyODBC
  • cx_Oracle

通过pandas.read_sql_query , read_sql_table , to_sql 进行数据库操作。
Python与数据库的交互方案有很多种,从数据分析师角度来看pandas方案比较合适,之后的讲义中会结合sql语法进行讲解。
进行MySQL数据库连接:

import pymysql
import pymysql.cursors
conn = pymysql.connect(
	host = "127.0.0.1",
	user = root,
	password = "123456",
	charset = "utf8",
	cursorclass = pymysql.cursors.DictCursor
)
# pd.read_sql_query("sql",conn)
# df.to_sql("tablename",conn,flavor = "mysql")

深入Pandas数据操纵

在第一部分的基础上,数据会有更多的操纵方式:

  • 通过列名、行index来取数据,结合ix、iloc灵活的获取数据的一个子集(第一部分已经介绍)
  • 按记录拼接(就像Union All)或者关联(join)
  • 方便的自定义函数映射
  • 排序
  • 缺失值处理
  • 与Excel一样灵活的数据透视表

数据整合:方便灵活

横向拼接:直接DataFrame

pd.DataFrame([np.random.rand(2), np.random.rand(2), np.random.rand(2)],columns = ["c1", "c2"])

横向拼接:Concatenate

pd.concat([data_employee_ri, data_employee_ri, data_employee_ri])

纵向拼接:Merge

  • 根据数据列关联,使用on关键字
  1. 可以指定一列或者多列
  2. 可以使用left_on或者right_on
pd.merge(data_employee_ri, data_employee_ri, on = "name")
########
pd.merge(data_employee_ri, data_employee_ri, on = ["name","job"])
########
# 根据index关联,可以直接使用left_index或者right_index
data_emplyee_ri.index.name = "index1"
pd.merge(data_emplyee_ri,data_emplyee_ri,left_index = "index1", right_index = "index1")

TIPS:增加how关键字,并指定

  • how = “inner”
  • how = “left”
  • how = “right”
  • how = “outer”
    结合how,可看到merge基本再现了SQL应有的能力,并保持代码整洁。
DFA = pd.DataFrame(
	{	
		"name":[u"老王",u"老张",u"老李"]"sal":[5000,3000,1000]
	}
)
print(DFA)
########
DFB = pd.DataFrame(
	{
		"name":[u"老王",u"老刘"],
		"job":["VP","Manager"]
	}
)
print(DFB)
########
# how = "left" :保留左表信息
pd.marge(DFA, DFB, on = "name", how = "left")
########
# how = "right" :保留右表信息
pd.marge(DFA, DFB, on = "name", how = "right")
########
# how = "inner" :保留两表交集信息,尽量避免出现缺失值
pd.marge(DFA, DFB, on = "name", how = "inner")
########
# how = "onter" :保留两表并集信息,优点最大程度的整合了已有信息,缺点会导致出现缺失值
pd.marge(DFA, DFB, on = "name", how = "onter")

数据清洗三剑客

接下来三个功能:map,applymap,apply功能,是绝大参数数据分析师在数据清洗这一步骤的必经之路。
他们分别的回答了以下问题:

  • 我想根据一列数据新做一列数据,怎么办?(答:Series -> Series)
  • 我想根据整张表数据新作整张表,怎么办?(答:DataFrame -> DataFrame)
  • 我想根据很多列的数据新做一列数据,怎么办?(答:DataFrame -> Series)

不要再写什么for循环了!改变思维,提高编码和执行效率。

dataNumpy = np.asarray([
	("Japan","Tokyo",4000),
	("S.Korea","Seoul",1300),
	("China","Beijing",9100)
])
DF = pd.DataFrame(dataNumpy,columns = ["nation","capital","GDP"])
print(DF)
Map:以相同规则将一列数据作一个映射,也就是进行相同函数的处理。
def GDP_Factorize(v):
	fv = np.float64(v)
	if fv > 6000.0:
		return "High"
	elif fv < 2000.0:
		return "Low"
	else:
		return "Medium"
DF["GDP_Level"] = DF["GDP"].map(GDP_Factorize)
DF["NATION"] = DF.nation.map(str.upper)
print(DF)

applymap:可以对一个DataFrame里面的每一个元素像map那样全局操作。
DF.applymap(lambda x:float(x)*2 if x.isdigit() else x.upper())

apply则可以对一个DataFrame操作得到一个Series。

它会有点像我们后续介绍的agg,但是apply可以按行操作和按列操作,用axis控制即可。

DF.apply(lambda x:x["nation"] + x["capital"] + "_" + x["GDP"], axis = 1)

数据排序

  • sort:按一列或者多列的值进行行级排序
  • sort_index:根据index里的取值进行排序,而且可以根据axis决定是重排行还是列。
dataNumpy = np.asarray([
	("Japan", "Tokyo", 4000)
	("S.Korea", "Seoul", 1300)
	("China", "Beijing", 9100)
])
DF = pd.DataFrame(dataNumpy, columns = ["nation", "capital", "GDP"])
print(DF)
########
DF.sort(["capotal", "nation"])
DF.sort("GDP", ascending = False)
DF.sort("GDP").sort(ascending = False)
DF.sort_index(axis = 1, ascending = True)
print(DF)
########
DF.rank()
DF.rank(ascending = False)

注意tied data(相同值)处理:

  • method = “average”
  • method = “min”
  • method = “max”
  • method = “first”
DF = pd.DataFrame(
	{
		"name":[u"老王",u"老刘",u"老李",u"老张"],
		"sal":np.array([5000, 3000, 5000, 9000])
	}
)
print(DF)
########
# DF.rank()默认使用method = "average",两条数据相等时,处理排名时大家都用平均值。
DF.sal.rank()
# method = "min",处理排名时大家都用最小值。
DF.sal.rank(method = "min")
# method = "max",处理排名时大家都用最大值。
DF.sal.rank(method = "max")
# method = "first",处理排名时谁先出现就先给谁较小的数值。
DF.sal.rank(method = "first")

缺失数据处理

DF = data_for_multi.unstack()
print(DF)
########
# 忽略缺失值
DF.mean(skipna = True)
DF.mean(skipna = False)
# 如果不想忽略缺失值,就要祭出fillna了。
DF.finllna(0).mean(axis = 1, skipna = False)

Pandas的groupby

groupby的功能类似于SQL中的group by关键字

  • Split-Apply-Combine:
  1. split,就是按照规则排序
  2. Apply:通过一定的agg函数来获得pd.Series返回一个值的效果。
  3. Combine:把结果收集起来。
  • Pandas的groupby的灵活性:
  1. 分组的关键字可以来自于index,也可以来自于真实的列数据。
  2. 分组规则可以通过一列或者多列

from IPython.display import Image
Image(filename = "SIP3_group.png")
# 分组的具体逻辑
irisdata_group = irisdata.groupby("class")
print(irisdata_group)
for level,subsetDF in irisdata_group:
	print(level)
	print(subsetDF)

分组可以快速实现MapReduce的逻辑 :

  • Map:指定的分组的列标签,不同的值就会被扔到不同的分组处理。
  • Reduce:输入多个值,返回一个值,一般可以通过agg实现,agg能够接受一个函数。
irisdata.groupby("class").agg(\
lambda x:((x - x.mean())**3).sum()*(len(x) - 0.0 )/\
(len(x) - 1.0)/(len(x) - 2.0)/(x.std() * np.sqrt((len(x) - 0.0)/(len(x) - 1.0)))**3 if len(x)>2 else None
)
irisdata.groupby("class").agg(spstat.skew)

汇总之后的广播操作

在OLAP数据库上,为了避免groupby与join的二次操作,提出了sum()over(paritition by)的 开窗操作。
在pandas中,这波操作能够进一步呗transfrom锁取代:

pd.concat([irisdata, irisdata.groupby("class").transform("mean")], axis = 1)[::20]

产生MultiIndex(多列分组)后数据透视表操作

一般来说,多列groupby的 一个副作用就是.groupby().agg()之后你的行index就已经变成了一个多列分组的分级索引。
如果我们希望达到Excel的数据透视表效果,行和列的索引自由交换,达到统计目的,究竟应该怎么做?

factor1 = np.random.randint(0, 3, 50)
factor2 = np.random.randint(0, 2, 50)
factor3 = np.random.randint(0, 3, 50)
hierindexDF = pd.DataFrame({"F1":factor1,"F2":factor2,"F3":factor3,"F4":values})
print(hierindexDF)
##########
hierindexDF_gbsum = hierindexDF.groupby(["F1","F2","F3"]).sum()
print(hierindexDF_gbsum)
# 观察 index 值
print(hierindexDF_gbsum.index)
# unstack:
#   无参数时,把最末端index置换到column上
#   有数字参数时,把指定位置的index置换到column上
#   有列表参数时,依次把特定位置的index置换到column上
hierindexDF_gbsum.unstack()
hierindexDF_gbsum.unstack(0)
hierindexDF_gbsum.unstack([2,0])
# 更进一步的,satck功能是和unstack所对应,把column上的多级索引换到index上去。
hierindexDF_gbsum.unstack([2,0]).stack([1,2])

你可能感兴趣的:(数据分析)