pandas
pandas
Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。
Series 是一种类似于一维数组的对 象,它由一组数据(各种 Numpy 数据类型)以及一组与之相关的数据标签(即索引)组成。
DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共同用一个索引)。
pandas 数据存取-csv/xlsx/xls
import pandas as pd
data = [['Google',10],['Runoob',12],['Wiki',13]]
df = pd.DataFrame(data,columns=['Site','Age'],dtype=float)
df.to_csv('file1.csv',index=False)
df = pd.read_csv('file1.csv')
df.to_excel('file1.xlsx',index=False)
df = pd.read_excel('file1.xlsx')
df.to_excel('file1.xls',index=False)
df = pd.read_excel('file1.xls')
df
'''
Site Age
0 Google 10
1 Runoob 12
2 Wiki 13
'''
pandas 数据存取-json
data =[
{
"id": "A001",
"name": "菜鸟教程",
"url": "www.runoob.com",
"likes": 61
},
{
"id": "A002",
"name": "Google",
"url": "www.google.com",
"likes": 124
},
{
"id": "A003",
"name": "淘宝",
"url": "www.taobao.com",
"likes": 45
}
]
df = pd.DataFrame(data)
df.to_json('sites.json')
df = pd.read_json('sites.json')
print(df.to_string())
'''
id name url likes
0 A001 菜鸟教程 www.runoob.com 61
1 A002 Google www.google.com 124
2 A003 淘宝 www.taobao.com 45
'''
pandas 数据存取-sql
from sqlalchemy import create_engine,text
import pandas as pd
MYSQL_HOST = '*'
MYSQL_PORT = 3306
MYSQL_USER = 'employee_u'
MYSQL_PASSWORD = 'employee_s'
MYSQL_DB = 'employee'
engine = create_engine('mysql+pymysql://{}:{}@{}:{}/{}'.format(MYSQL_USER,MYSQL_PASSWORD,MYSQL_HOST,MYSQL_PORT,MYSQL_DB),
echo = False)
dataset = pd.DataFrame({'Names':['Abhinav','Aryan',
'Manthan'],
'DOB' : ['10/01/2009','24/03/2009',
'28/02/2009']})
dataset.to_sql('Employee_Data',con = engine,index=False)
#附加到以前创建的数据库中
dataset.to_sql('Employee_Data',con = engine,index=False,if_exists = 'append')
# 查看是否写入成功
with engine.connect() as conn:
df1 = pd.read_sql('Employee_Data',con = conn,columns = ["Names"])
df2 = pd.read_sql('Employee_Data',con = conn,index_col = 'Names',columns = ["Names"])
df3 = pd.read_sql_table("Employee_Data", conn)
df4 = pd.read_sql_query(text("select * from Employee_Data"),con = conn)
df5 = conn.execute(text("select MAX(`index`) from Employee_Data")).fetchall()
df6 = conn.execute(text("select `index` from Employee_Data WHERE `index` > :index"),{"index":1}).fetchall()
print(df1)
print(df2)
print(df3)
print(df4)
print(df5)
print(df6)
快速的识别结构化数据
import numpy as np
import scipy as sp
import pandas as pd
iris_file = './iris.data'
data = pd.read_csv(iris_file, header=None, encoding='utf-8')
data
快速的操作元数据
cnames = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
data.columns = cnames
data
快速过滤
data[data['petal_width'] == data.petal_width.max()]
快速切片
data.iloc[::30, :2]
快速统计
data['class'].value_counts()
for x in range(4):
s = data.iloc[:,x]
print('{0:<12}'.format(s.name), " Statistics: ",
'{0:>5} {1:>5} {2:>5} {3:>5}'.format(s.max(), s.min(), round(s.mean(),2),round(s.std(),2)))
快速“MapReduce”
slogs = lambda x:np.log(x)*x
entpy = lambda x:np.exp((slogs(x.sum())-x.map(slogs).sum())/x.sum())
data.groupby('class').agg(entpy)
Pandas 的重要数据类型
- DataFrame(二维表)
- Series(一维序列)
- Index(行索引,行级元数据)
1.1 Series:pandas 的长枪(数据表中的一列或一行,观测向量,一维数组...)
数据世界中对于任意一个个体的全面观测,或者对于任意一组个体某一属性的观测,全部可以抽象为 Series 的概念。
用值构建一个 Series:
由默认 index 和 values 组成。
series1 = pd.Series(np.random.randn(4))
series1
print(type(series1))
print(series1.index)
print(series1.values)
Series 支持过滤的原理就如同 NumPy
series1 > 0
series1[series1 > 0]
当然也支持 Broadcasting
series1*2
series1+5
以及 Universal Function
print(series1)
print(np.exp(series1))
#NumPy Universal Function
f_np = np.frompyfunc(lambda x:np.exp(x*2 + 5), 1, 1)
f_np(series1)
在序列上就使用行标,而不是创建一个 2 列的数据表,能够轻松辨别哪里是数据,哪里是元数据:
series2 = pd.Series(series1.values, index=['norm_' + str(i) for i in range(4)])
print(series2, type(series2))
print(series2.index)
print(type(series2.index))
print(series2.values)
series2
虽然行是有顺序的,但是仍然能够通过行级的 index 来访问到数据:
(当然也不尽然像 Ordered Dict,因为行索引甚至可以重复,不推荐重复的行索引不代表不能用)
series2[['norm_0','norm_3']]
'norm_0' in series2
'norm_6' in series2
默认行索引就像行号一样:
series1.index
从 Key 不重复的 Ordered Dict 或者从 Dict 来定义 Series 就不需要担心行索引重复:
s_dict = {"Japan":"Tokyo", "Korea":"Seoul", "China":"Beijing"}
series3 = pd.Series(s_dict)
print(series3.index)
print(series3.values)
series3
与 Dict 区别一: 有序
lst = ["Japan", "China", "Singapore", "Korea"]
series4 = pd.Series(s_dict, index =lst)
series4
print(series4.values)
print(series4.index)
print(series4.isnull())
print(series4.notnull())
与 Dict 区别二: index 内值可以重复,尽管不推荐。
lst = ['A', 'B', 'B', 'C']
series5 = pd.Series(series1.values, index=lst)
series5
series5[['B', 'A']]
整个序列级别的元数据信息:name
当数据序列以及 index 本身有了名字,就可以更方便的进行后续的数据关联啦!
series4.name
series4.index.name
series4.name = "Capital Series"
series4.index.name = "Nation"
series4
pd.DataFrame(series4)
1.2 DataFrame:pandas 的战锤(数据表,二维数组)
Series 的有序集合,就像 R 的 DataFrame 一样方便。
仔细想想,绝大部分的数据形式都可以表现为 DataFrame。
从 Numpy 二维数组、从文件或者从数据库定义:数据虽好,勿忘列名
data_np = np.asarray([('Japan', 'Tokyo', 4000),
('Korea', 'Seoul', 1300),
('China', 'Beijing', 9100)])
df1 = pd.DataFrame(data_np, columns=['nation','capital','GDP'])
df1
等长的列数据保存在一个字典里(JSON):很不幸,字典 key 是无序的
data_dict = {'nation': ['Japan', 'Korea', 'China'],
'capital': ['Tokyo', 'Seoul', 'Beijing'],
'GDP': [4900, 1300, 9100]}
df2 = pd.DataFrame(data_dict)
df2
从另一个 DataFrame 定义 DataFrame:啊,强迫症犯了
df21 = pd.DataFrame(df2, columns=['nation', 'capital', 'GDP'])
df21
df22 = pd.DataFrame(df2, columns=['nation', 'capital', 'GDP'], index = [2, 0, 1])
df22
从 DataFrame 中取出列?两种方法(与 JavaScript 完全一致!)
- '.'的写法容易与其他预留关键字产生冲突
- '[ ]'的写法最安全。
print(df22.nation)
print(df22.capital)
print(df22['GDP'])
df22['capital']
从 DataFrame 中取出行?(至少)两种方法
df22[0:1] # 给出的实际是DataFrame
df22.iloc[0] # 通过对应Index给出行
像 Numpy 切片一样的终极招式:iloc
df22.iloc[0,:]
df22.iloc[:,0]
听说你从 Table 地狱来,大熊猫笑了
然而动态增加列无法用"."的方式完成,只能用"[ ]"
df22['population'] = [1600, 130, 55]
df22['region'] = 'East_Asian'
df22
1.3 Index:pandas 进行数据操作的鬼牌(行级索引)
行级索引是
- 元数据
- 可能由真实数据产生,因此可以视作数据
- 可以由多重索引也就是多个列组合而成
- 可以和列名进行交换,也可以进行堆叠和展开,达到 Excel 透视表效果
Index 有四种...哦不,很多种写法,一些重要的索引类型包括
- pd.Index(普通)
- Int64Index(数值型索引)
- MultiIndex(多重索引,在数据操纵中更详细描述)
- DatetimeIndex(以时间格式作为索引)
- PeriodIndex (含周期的时间格式作为索引)
直接定义普通索引,长得就和普通的 Series 一样
index_names = ['a','b','c']
s = pd.Series(index_names)
print(pd.Index(index_names))
print(pd.Index(s))
s
Immutable,牢记
pd.Index 是不可变的
index_names = ['a', 'b', 'c']
index0 = pd.Index(index_names)
print(index0.values)
# index0[2] = 'd' # 改变值会出错
扔进去一个含有多元组的 List,就有了 MultiIndex
MyltiIndex 也是 immutable 的。
multi1 = pd.Index([['Row_' + str(x + 1), 'Col_' + str(y + 1)] for x in range(4) for y in range(4)])
multi1.name = ('index1', 'index2')
multi1
multi2 = pd.Index([('Row_' + str(x + 1), 'Col_' + str(y + 1)) for x in range(4) for y in range(4)])
multi2
# multi2.name = ['index1', 'index2'] # 出错
对于 Series 来说,如果拥有了多重 Index,数据,变形
下列代码说明:
- 二重 MultiIndex 的 Series 可以 unstack()成 DataFrame
- DataFrame 可以 stack 成拥有二重 MultiIndex 的 Series
data_for_multi1 = pd.Series(range(0, 16), index=multi2)
data_for_multi1
data_for_multi1.unstack()
data_for_multi1.unstack().stack()
我们来看一下非平衡数据的例子:
Row_1,2,3,4 和 Col_1,2,3,4 并不是全组合的。
multi2 = pd.Index([('Row_' + str(x), 'Col_' + str(y + 1)) for x in range(5) for y in range(x)])
multi2
data_for_multi2 = pd.Series(np.arange(10), index=multi2)
data_for_multi2
data_for_multi2.unstack()
data_for_multi2.unstack().stack()
DateTime 标准库如此好用,你值得拥有
import datetime
dates = [datetime.datetime(2021, 1, 1), datetime.datetime(2021, 1, 8), datetime.datetime(2021, 1, 30)]
pd.DatetimeIndex(dates)
如果你不仅需要时间格式统一,时间频率也要统一的话
periodindex1 = pd.period_range('2021-01', '2021-04', freq='M')
periodindex1
月级精度和日级精度如何转换?
有的公司统一以 1 号代表当月,有的公司统一以最后一天代表当月,转化起来很麻烦,可以 asfreq
periodindex1.asfreq('D', how='start')
periodindex1.asfreq('D', how='end')
最后的最后,我要真正把两种频率的时间精度匹配上?
periodindex_mon = pd.period_range('2021-01', '2021-03', freq='M').asfreq('D', how='start')
periodindex_day = pd.period_range('2021-01-01', '2021-03-01', freq='D')
periodindex_mon
periodindex_day
粗粒度数据+ reindex + ffill/bfill
full_ts = pd.Series(periodindex_mon, index=periodindex_mon).reindex(periodindex_day)
full_ts.head()
full_ts = pd.Series(periodindex_mon, index=periodindex_mon).reindex(periodindex_day, method='ffill')
full_ts.head()
关于索引,方便的操作有?
前面描述过了,索引有序,重复,但一定程度上又能通过 key 来访问,也就是说,某些集合操作都是可以支持的。
index1 = pd.Index(['A', 'B', 'B', 'C', 'C'])
index2 = pd.Index(['C', 'D', 'E', 'E', 'F'])
index3 = pd.Index(['B', 'C', 'A'])
print(index1.append(index2))
print(index1.difference(index2))
print(index1.intersection(index2))
print(index1.union(index2)) # Support unique-value Index well
print(index1.isin(index2))
print(index1.delete(2))
print(index1.insert(0, 'K')) # Not suggested
print(index3.drop('A')) # Support unique-value Index well
print(index1.is_monotonic, index2.is_monotonic, index3.is_monotonic)
print(index1.is_unique, index2.is_unique, index3.is_unique)
老生常谈,从基础来看,我们仍然关心 pandas 对于与外部数据是如何交互的。
2.1 结构化数据输入输出
- read_csv 与 to_csv 是一对输入输出的工具,read_csv 直接返回 pandas.DataFrame,而 to_csv 只要执行命令即可写文件
- read_table:功能类似
- read_fwf:操作 fixed width file
- read_excel 与 to_excel 方便的与 excel 交互
还记得刚开始的例子吗?
- header 表示数据中是否存在列名,如果在第 0 行就写就写 0,并且开始读数据时跳过相应的行数,不存在可以写 none
- names 表示要用给定的列名来作为最终的列名
- encoding 表示数据集的字符编码,通常而言一份数据为了方便的进行文件传输都以 utf-8 作为标准
提问:下列例子中,header=4,names=cnames 时,究竟会读到怎样的数据?
print('cnames:', cnames)
irisdata = pd.read_csv(iris_file, header=None, names=cnames, encoding='utf-8')
irisdata[::30]
希望了解全部参数的请移步 API:
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html#pandas.read_csv
这里介绍一些常用的参数:
读取处理:
- skiprows:跳过一定的行数
- nrows:仅读取一定的行数
- skipfooter:尾部有固定的行数永不读取
- skip_blank_lines:空行跳过
内容处理:
- sep/delimiter:分隔符很重要,常见的有逗号,空格和 Tab('\t')
- na_values:指定应该被当作 na_values 的数值
- thousands:处理数值类型时,每千位分隔符并不统一 (1.234.567,89 或者 1,234,567.89 都可能),此时要把字符串转化为数字需要指明千位分隔符
收尾处理:
- index_col:将真实的某列(列的数目,甚至列名)当作 index
- squeeze:仅读到一列时,不再保存为 pandas.DataFrame 而是 pandas.Series
2.1.x Excel ... ?
对于存储着极为规整数据的 Excel 而言,其实是没必要一定用 Excel 来存,尽管 Pandas 也十分友好的提供了 I/O 接口。
iris_excel_file = 'irisdata.xls'
irisdata.to_excel(iris_excel_file, index=None)
irisdata_from_excel = pd.read_excel(iris_excel_file, header=0)
irisdata_from_excel[::30]
import os
os.remove(iris_excel_file)
唯一重要的参数:sheetname=k,标志着一个 excel 的第 k 个 sheet 页将会被取出。(从 0 开始)