最近一直在忙着处理数据,以下是我作为python和数据分析的入门小白,在最近几周的工作中总结的一些经验。
高性能、简单易用的数据结构和数据分析工具
构建DataFrame
1 23456789
df = pd.DataFrame(numpy_ndarray, columns=['col1','col2',...] )df = pd.DataFrame(some_list_of_set, columns=['col1','col2',...] )df = pd.DataFrame(some_dict_of_series_or_arrays_or_list)web_stats = {'Day':[1,2,3,4,5,6], 'Visitors':[43,53,34,45,64,34], 'Bounce_Rate':[65,72,62,64,54,66]}df = pd.DataFrame(web_stats)
查看头部 df.head() 查看尾部(n个元素) df.tail(n)
df.head()
df.tail(n)
以Day字段为索引 df = df.set_index('Day')
df = df.set_index('Day')
选择某一列 df['Visitors'] 或 df.Visitors 如果有空格,使用第一种
df['Visitors']
df.Visitors
将df一列转为list(列行转) df.Visitors.tolist()
df.Visitors.tolist()
多列转数组 np.array(df[['Bounce_Rate','Visitors']])
np.array(df[['Bounce_Rate','Visitors']])
重命名列名
12
df.columns = ['Renamed_col1','Renamed_col2'] df = df.rename(columns=={oldColName: newColName})
数据的快速统计汇总 df.describe()
df.describe()
查看更多的百分位数 df.describe(percentiles=[.20,.40,.80,.90,.95])
df.describe(percentiles=[.20,.40,.80,.90,.95])
筛选:
123
df[df.A > 0] # 字段A大于0的df.query(' k == "/pay/v4/order/pay/consume" and ct > 4000')df[(df['class'] == 'Iris-virginica') & (df['petal width'] > 2.2)]
删除多列: df.drop(['col1', 'col2'], axis=1) # inplace=True指定替换原df
df.drop(['col1', 'col2'], axis=1) # inplace=True指定替换原df
删除多记录: df.drop([22,33], axis=0)
df.drop([22,33], axis=0)
分组聚合查询:
123456789101112131415161718
aggregations = { 'ct': { # work on the "duration" column 'total': 'sum', # get the sum, and call this result 'total_duration' 'average': 'mean', # get mean, call result 'average_duration' 'count': 'count', 'max': 'max', 'min': 'min', 'quantile_0.5': lambda x: x.quantile(0.5), 'quantile_0.75': lambda x: x.quantile(0.75), 'quantile_0.95': lambda x: x.quantile(0.95) },}easy_df.groupby("k").aggregate(aggregations)# 查询slow_pay_df = easy_df[ (easy_df.k == 'pay/consume' ) & ( easy_df.ct > 3000 )] # 排序easy_agg = easy_df.sort_values(by=[('ct','average')], ascending=False) # 多index的要使用tuple传入列名
选择Pclass和Survived列,按照Pclass聚合,按平均值降序排列查看:
train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
分组count: df1.groupby('p.trade_status')['p.trade_status'].value_counts()
df1.groupby('p.trade_status')['p.trade_status'].value_counts()
处理缺失数据: df = df.dropna(axis=1, thresh=100) # 删除非NaN值小于100的列
df = df.dropna(axis=1, thresh=100) # 删除非NaN值小于100的列
map: 转变数据框(df)中的某个列的列数据
将class列的数据替换为更简单的名称,并将原列替换 df['class'] = df['class'].map({'Iris-setosa': 'SET', 'Iris-virginica': 'VIR', 'Iris-versicolor': 'VER'})
df['class'] = df['class'].map({'Iris-setosa': 'SET', 'Iris-virginica': 'VIR', 'Iris-versicolor': 'VER'})
apply : 既可以在DataFrame上工作,也可在Series上工作。 df['wide petal'] = df['petal width'].apply(lambda v: 1 if v >= 1.3 else 0) axis :
df['wide petal'] = df['petal width'].apply(lambda v: 1 if v >= 1.3 else 0)
按行处理整个df df['petal area'] = df.apply(lambda r: r['petal length'] * r['petal width'], axis=1)
df['petal area'] = df.apply(lambda r: r['petal length'] * r['petal width'], axis=1)
按行更新,如果是nan就更新成另一个行
123456
def update_appkey(row, data=data): if row.app_key_x != row.app_key_x: print('update ',row['包名'], ' ',row.app_key_y) row.app_key_x = row.app_key_y return rowpdata = mdata.apply(update_appkey, axis=1).copy()
在apply的函数中,获取行号:使用row.name属性
applymap :对整个df所有数据单元执行一个函数 df.applymap(lambda v: np.log(v) if isinstance(v, float) else v)
df.applymap(lambda v: np.log(v) if isinstance(v, float) else v)
数据切片 iloc df.iloc[:3, :2]
df.iloc[:3, :2]
tip: ix vs iloc vs loc
重置索引(如截取了一个子df,索引保持原样,可以重置索引从0开始) df.reset_index(drop=True)
df.reset_index(drop=True)
检查数据相关性: df.corr() 通过传递method参数,还可以切换到Kendall’s tau或Spearman’s df.corr(method='spearman') df.corr(method='kendall')
df.corr()
df.corr(method='spearman')
df.corr(method='kendall')
unique() 相当与sql distinct df['class'].unique()
df['class'].unique()
round : 四舍五入 df.round(n) # 保留n位小数 或 df['col'].round(n)
df.round(n) # 保留n位小数
df['col'].round(n)
找重复数据: df.duplicated()
df.duplicated()
pivot_table 以电子表格形式显示数据 names.pivot_table('births',columns='sex',aggfunc=sum,index='year') # 统计对象为births列,纵坐标以sex列划分,横坐标为year,使用sum函数聚合
names.pivot_table('births',columns='sex',aggfunc=sum,index='year') # 统计对象为births列,纵坐标以sex列划分,横坐标为year,使用sum函数聚合
字符串替换: f['date'] = f['date'].str.replace('日','')
f['date'] = f['date'].str.replace('日','')
聚合Group by : 使用group by - apply 分组并加入列
1234567
def add_prop(group): # 注意变为float births = group.births.astype(float) group['prop'] = births / births.sum() return groupnames = names.groupby(['year','sex']).apply(add_prop) # 某年内某个性别分组names
分组取top 1000: top1000 = names.groupby(['year','sex']).apply(lambda g : g.sort_values(by='births', ascending=False)[:1000])
top1000 = names.groupby(['year','sex']).apply(lambda g : g.sort_values(by='births', ascending=False)[:1000])
样本值的累积和: df.cumsum()
df.cumsum()
找到已排序的series中n应该插入的位置:series.searchsorted(n)
s = pd.Series([1,3,5,7,9])s.searchsorted(2) # 1
处理时间: login_df['createtime'] = pd.to_datetime(login_df[['year','month','day','hour','minute','second']]) # login_df包含了年月日时分秒的列
login_df['createtime'] = pd.to_datetime(login_df[['year','month','day','hour','minute','second']]) # login_df包含了年月日时分秒的列
从csv文件读取到df: df = pd.read_csv('filename') 从df写入到csv: df.to_csv('newcsv.csv')
df = pd.read_csv('filename')
df.to_csv('newcsv.csv')
读取页面的tables为dataFrame list: dfs = df.read_html('https://simple.wikipedia.org/wiki/List_of_U.S._states')
dfs = df.read_html('https://simple.wikipedia.org/wiki/List_of_U.S._states')
处理utf8编码的嵌套json数据(规则化json数据):
1234567891011
import pandas as pdimport codecsimport jsonpath = 'some_nested_json_with_utf8_coded.json'with codecs.open(path, 'r', 'utf-8') as json_data: json_dicts = json.load(json_data) json_dicts = pd.io.json.json_normalize(json_dicts) # 处理嵌套json df = pd.DataFrame(json_dicts)# df = pd.read_json(path) # 这两种都一样df.head()
读取mysql数据到df(pandas处理sql要借助SQLAlchemy,一个类似java中mybatis的orm工具,具体实现还要依赖各种数据库连接驱动模块):
conn = 'mysql+pymysql://user:[email protected]:3306/dbname?charset=utf8'# pymysql也可更换为包含的库如mysqlconnectorapp_df = pd.read_sql_query(u'select col_name1 from table_name1', con=conn)
读取oracle大批数据到df:
1234567891011121314151617
import cx_Oracleip = '172.16.1.63'port = 1521SID = 'ucbi' # oracle SIDdsn_tns = cx_Oracle.makedsn(ip, port, SID)conn = cx_Oracle.connect(user='user',password='password',dsn=dsn_tns)pool_generator = pd.read_sql('select * from table_name1', con=conn, chunksize=20000) # 每次读20000条数据dfs = []for chunk in pool_generator: print('process ',len(dfs)) dfs.append(chunk)pool = pd.concat(dfs,ignore_index=True) pool
df数据写入到oracle
# 写入import cx_Oraclefrom sqlalchemy import create_enginefrom sqlalchemy import types# 指定类型,否则对df中object类型默认使用CLOB,速度慢到令人发指 https://stackoverflow.com/questions/42727990/speed-up-to-sql-when-writing-pandas-dataframe-to-oracle-database-using-sqlalchdtyp = {c:types.VARCHAR(df[c].str.len().max()) for c in df.columns[df.dtypes == 'object'].tolist()}ip = '172.16.1.63'port = 1521SID = 'ucbi'dsn_tns = cx_Oracle.makedsn(ip, port, SID)print(dsn_tns)engine = create_engine('oracle+cx_oracle://ucenter:passwd@%s' % dsn_tns)df.to_sql('table_name', engine, chunksize=20000, if_exists='replace',dtype=dtyp ) # 每次重新创建表
join: 主要用于索引上的合并 df.join(self, other, on=None, how='left', lsuffix='', rsuffix='',sort=False)
df.join(self, other, on=None, how='left', lsuffix='', rsuffix='',sort=False)
merge: 默认以重叠的列名当做连接键,默认inner连接 pd.merge(df1,df2,on='Year',how='outer') how 类似mysql join方式,有left, right, inner, outer ,连接键名称不同,可使用left_on和right_on指定;以索引当做连接键:left_index=true,right_index=True on可以指定多个col, on=['Year','Month']
pd.merge(df1,df2,on='Year',how='outer')
on=['Year','Month']
concat: 相当于DB中的全连接(union all) concat方法相当于数据库中的全连接(UNION ALL),可以指定按某个轴进行连接,也可以指定连接的方式join(outer,inner 只有这两种)。与数据库不同的时concat不会去重,要达到去重的效果可以使用drop_duplicates方法 pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False) 常用: pd.concat(some_list, ignore_index=True)
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False)
pd.concat(some_list, ignore_index=True)
python原生pickle
12345678
import pickle# 序列化pickle_out = open('picklename','wb')pickle.dump(df,pickle_out)pickle_out.close()# 反序列化pickle_in = open('picklename'),'rb')df = pickle.load(pickle_in)
pandas pickle
df.to_pickle('picklename2')df2 = pd.read_pickle('picklename2')
pd.get_dummies 属性平展
data_train['Cabin'] # 为'Yes'和'No'数据dummies_Cabin = pd.get_dummies(data_train['Cabin'], prefix= 'Cabin') # 获取Cabin_Yes 和 Cabin_No两列,并按0,1展示
pandas中判断NaN类型:
def isNaN(num): return num != num
需要筛选后处理数据时,使用copy,否则一些操作会返回一个view,这样在更新时就会有问题
多level降低:(尤其适用于在excel读入的多表头数据) frame.columns = frame.columns.droplevel()
frame.columns = frame.columns.droplevel()
bool 数组取反使用”-“ pool[ -(pool.USERID.str.contains('\|'))]
pool[ -(pool.USERID.str.contains('\|'))]
df中index不是column!
np.allclose : 检查是否近似 np.allclose(names.groupby(['year','sex']).prop.sum(),1)
np.allclose(names.groupby(['year','sex']).prop.sum(),1)
四舍五入 np.round(3.45, 0)
np.round(3.45, 0)
取随机整数 np.random.randint(50) # 0~49
np.random.randint(50) # 0~49
np.array配合查找df
mask = np.array(['lesl' in x.lower() for x in all_names])lesley_like = all_names[mask]
创建ndarray: arr1 = np.arra([...]) 查看数组维度 arr1.ndim 数组形状: arr1.shape 数据结构 arr1.dtype 使用zeros或ones创建长度或形状全0或全1的数组 np.zeros(10)或np.zeros((3,6)) 使用empty创建一个没有任何具体值的数组(并非返回全0数组,很多情况下返回未初始化的垃圾值) python内置range的数组版本: np.arange(10) 创建一个正方的N*N单位矩阵(对角线为1,其余为0) np.eye(n)
arr1 = np.arra([...])
arr1.ndim
arr1.shape
arr1.dtype
np.zeros(10)或np.zeros((3,6))
np.arange(10)
np.eye(n)
dtype是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息 np.array([1,2,3], dtype=np.float64) np.array([1,2,3], dtype=np.int32)
np.array([1,2,3], dtype=np.float64)
np.array([1,2,3], dtype=np.int32)
numpy数据类型: int8, uint8 int16, uint16 int32, uint32 int64, uint64 float16 float32 标准的单精度浮点数。与c的float兼容 float64 标准的双精度浮点数。与c的double和python的float对象兼容 float128 complex64, complex128, complex256 复数 bool object Python对象类型 string 固定长度的字符串类型(每个字符1个字节) unicode 固定长度的unicode类型(字节数由平台决定)
使用astype转换dtype(总会创建一个新数组) arr.astype(np.float64)
不同大小的数组之间的运算叫做广播。
基本的索引和切片: arr[5] arr[5:8] arr[5:8] = 12 注意numpy数组跟python列表最大的不同是:数组切片是原始数组的视图,也就是数据并不会被复制。 如果需要复制,需要显式调用copy: arr[5:8].copy() 只有冒号表示选取整个轴: arr[:]
arr[5]
arr[5:8]
arr[5:8] = 12
arr[5:8].copy()
arr[:]
数组的比较运算也是矢量化的,names == ‘Bob’将返回一个布尔型数组 “非”可以用!=或负号- 应用多个布尔条件,使用&、|
mask = (names == ‘Bob’) | (names == ‘Will’)data[mask]
通过布尔型索引选取数组中的数据,将总是创建数据的副本。
按比例生成随机数据:
12345
# 按比例生成随机日期np.random.choice(date_df['date'].astype(int),100,p=date_df['per'].tolist())# 按比例生成随机时间np.random.choice(time_df['hour'].astype(int),100,p=time_df['radio'].tolist())choice(a[, size, replace, p])
三、Matplotlib 可视化 使用绘图主要是用于特征工程,发现暂时未知的因素对最终结果的影响 中文乱码的解决:
#coding:utf-8import matplotlib.pyplot as pltplt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签plt.rcParams['axes.unicode_minus']=False #用来正常显示负号#有中文出现的情况,需要u'内容'
Python绘图鼻祖, 导入:
1234
import matplotlib.pyplot as pltplt.style.use('ggplot')%matplotlib inlineimport numpy as np
直方图:
fig, ax = plt.subplots(figsize=(6,4))ax.hist(df['petal width'], color='black');ax.set_ylabel('Count', fontsize=12)ax.set_xlabel('Width', fontsize=12)plt.title('Iris Petal Width', fontsize=14, y=1.01)
散点图:
fig, ax = plt.subplots(figsize=(6,6))ax.scatter(df['petal width'],df['petal length'], color='green')ax.set_xlabel('Petal Width')ax.set_ylabel('Petal Length')ax.set_title('Petal Scatterplot')
简单线图:
fig, ax = plt.subplots(figsize=(6,6))ax.plot(df['petal length'], color='blue')ax.set_xlabel('Specimen Number')ax.set_ylabel('Petal Length')ax.set_title('Petal Length Plot')
条形图:
1234567891011121314
fig, ax = plt.subplots(figsize=(6,6))bar_width = .8labels = [x for x in df.columns if 'length' in x or 'width' in x]ver_y = [df[df['class']=='Iris-versicolor'][x].mean() for x in labels]vir_y = [df[df['class']=='Iris-virginica'][x].mean() for x in labels]set_y = [df[df['class']=='Iris-setosa'][x].mean() for x in labels]x = np.arange(len(labels))ax.bar(x, vir_y, bar_width, bottom=set_y, color='darkgrey')ax.bar(x, set_y, bar_width, bottom=ver_y, color='white')ax.bar(x, ver_y, bar_width, color='black')ax.set_xticks(x + (bar_width/2))ax.set_xticklabels(labels, rotation=-70, fontsize=12);ax.set_title('Mean Feature Measurement By Class', y=1.01)ax.legend(['Virginica','Setosa','Versicolor'])
小提琴图:
12345678910
fig, ax = plt.subplots(2, 2, figsize=(7, 7))sns.set(style='white', palette='muted')sns.violinplot(x=df['class'], y=df['sepal length'], ax=ax[0,0])sns.violinplot(x=df['class'], y=df['sepal width'], ax=ax[0,1])sns.violinplot(x=df['class'], y=df['petal length'], ax=ax[1,0])sns.violinplot(x=df['class'], y=df['petal width'], ax=ax[1,1])fig.suptitle('Violin Plots', fontsize=16, y=1.03)for i in ax.flat: plt.setp(i.get_xticklabels(), rotation=-90)fig.tight_layout()
内省: 任何变量后输入 ? ,可以展示此对象的通用信息 变量或方法后输入 ?? ,可以展示源码 配合 * 可以实现查找IPython命名空间,如 np.*load*?
?
??
*
np.*load*?
使用 %run f.py 运行f.py脚本
%run f.py
通过 Ctrl - C 中断正在执行的代码
Ctrl - C
魔术命令: %timeit 在任何语句前,检测执行时间 使用 ? 查看魔术命令选项,如 %reset? %quickref 或 %magic ,获取魔术命令文档
%timeit
%reset?
%quickref
%magic
最近的两个输出结果分别保存在 _ 和 __ 中
_
__
使用 %cd 、 %pwd 执行常见操作
%cd
%pwd
使用 ! 执行shell命令
!
使用 %bookmark 目录书签
%bookmark
表格打印
from IPython.core.interactiveshell import InteractiveShellInteractiveShell.ast_node_interactivity = "all" # 不必使用display或print也可以打印from IPython.display import display, HTML
以下内容留待以后补充
四、Seaborn
使用pairplot查看不同特征维度pair下数据的空间分布状况(散点图和直方图):
import seaborn as snssns.pairplot(df, hue="class") # hue控制区分颜色的变量名称,这里用class列中的变量区别颜色
五、Scikit-learn
sklearn.metrics.accuracy_score(y_true, y_pred)
六、statsmodels 用于探索数据、估计模型,并运行统计校验。