整理归纳在Python中使用对数据处理的常用方法,包括与HDFS文件的读写,主要是怕用的时候记不住,容易搞混,再搜也不爽,好记性不如烂笔头,写下来自己用的时候方便看,而且写一遍也加深印象。
随查随用随更新
Numpy
ndarray
ndarr1 = np.array([ [1, 2, 3], [4, 5, 6] ])
shape 大小和形状
dtype 数据类型
astype 显式的转换类型
不同大小的ndarray也可以运算,称之为广播,比如arr * 2
一维:用[x]进行索引,用[x:y]进行切片,都是原始数组的视图
多维:[x][y]或者[x,y]索引,[x1:x2, y1:y2]切片。
布尔型可以直接做索引
按轴进行布尔类型list的索引,或者对每个元素进行单个布尔索引,比如data1[data1 > 0] = 0
花式索引行和列
arr[ [行1, 行i] ][ :, [列1, 列i] ] 或者
arr[ np.ix_( [行1, 行i], [列1, 列i] ) ]
而且花式索引总是复制数据到新的数组中,不是切片那种原始数组的视图
arr.T 转置
通用函数
np.sqrt(arr) arr的开平方
np.exp(arr) arr的e的x次方
np.abs(arr) arr的绝对值
np.square(arr) arr的平方
np.log(arr) ln(arr)
np.log10(arr) log10(arr)
np.isnan arr的各个元素是否为NaN的bool数组
np.add(arr1, arr2) arr1 + arr2
np.subtract(arr1, arr2) arr1 - arr2
三元表达式
np.where(cond, xarr, yarr) 等价为 xarr if cond else yarr
统计
arr.sum(axis=?) 按照轴的方向求和
arr.mean 按照轴的方向求平均值
arr.min()和max() 最大和最小值
arr.argmin()和argmax() 最大和最小值的索引
arr.cumsum() 所有元素的累计和
arr.cumprod() 所有元素的累计积
排序
np.sort(arr)和arr.sort(),前者是顶级方法,返回对arr排序后的副本;后者则是直接对arr排序。
线性代数
包括矩阵的点乘,矩阵分解以及行列式等等。
点乘:xarr.dot(yarr)等价于np.dot(xarr, yarr)
numpy.linalg中有一组标准的矩阵分解运算以及诸如求逆和行列式之类。比如如下部分:
diag([一维数组]) 对角矩阵,对角线是输入的一维数组
dot 点乘
det 计算矩阵的行列式
inv 计算矩阵的逆
qr 计算QR分解
svd 计算奇异值分解(SVD)
Pandas
(官网)https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
官网Pandas的API汇总
官网10分钟对Pandas的简介
Series
可以把Series看成是一个有序的定长字典。
ser = Series([1, 2, 3, 4], index=['d', 'b', 'a', 'c'])
经常存在与DataFrame的行与列中。
也可以直接从字典类型的对象中构建。
idx_val_map = {'d':1, 'b':2, 'a':3, 'c':4}
ser = Series(idx_val_map, name='值的名称')
ser.index.name = '索引的名称'
ser['a']进行索引
再进一步,比如对于二维的DataFrame,取其中一列出来,就是Series,它的name就是这一列的名字,它的index就是DataFrame的index,index的name就是DataFrame中作为index的那一列的名字。
DataFrame
DataFrame的索引是不可更改的。可以添加和删除,但是不能修改。
- 构造DataFrame
1.由二维ndarray构造,可以给出行标和列标
2.由字典构成,外层字典key是列标,内层字典是行标result1 = pd.DataFrame([[1, 0.7], [0, 0.7]], index=pd.Index([135, 136], name='Number'), columns=['Gender', 'Prob'])
需要注意的是字典构成的df,需要指定column的顺序,否则默认会按照列的字符串名排序
predict_df = pd.DataFrame(data={'svc_y': svc_y, 'dnn_y': dnn_y, 'xgb_y': xgb_y, 'rf_y': rf_y}, columns=['svc_y', 'dnn_y', 'xgb_y' ,'rf_y'])
3.由另一个DataFrame构成
有时候会有一种需求是从已经有的DataFrame中创建一个空的Dataframe但是使用的是原来这个Dataframe的结构。
copy_df = pd.DataFrame.from_items([(name, pd.Series(data=None, dtype=series.dtype))
for name, series in original_df.iteritems()])
-
添加列
frame['new-col'] = value
-
添加行
s = pd.Series({'Col1':'Value1', 'Col2': 'Value2', 'Col3': Value3}) # 构造一行新数据 frame.append(s)
-
删除列
del frame['col-name']
-
删除行
# 因为DataFrame是列索引的(在frame.items()返回值中可以看出来)所以del只能删除列,删除行需要用到drop方法 frame.drop(frame.index[[1,3]])
-
索引方式
列索引:frame['col_name']或者frame.col_name
行索引:frame.ix['row_name']
点索引:frame.at[行,列] 或者frame.iat[行i,列i],只能索引具体的元素,不能切片
用整数位置索引frame.iloc[index(列表), col-index(列表)]
ix[行, 列]和iloc[行, 列]都可同时索引行与列,区别在于ix用的是名字(当DataFrame的index没有设置时,退化成iloc),iloc用的是整数位置
loc: works on labels in the index.
iloc: works on the positions in the index (so it only takes integers).
ix: usually tries to behave like loc but falls back to behaving like iloc if the label is not in the index.- 总结
['col-name']——是对列的索引
[ ['col-name1', 'col-name_i'] ]——两个[]是对多个列的索引
[bool类型list]或者[i:j]——是对行的切片索引
ix[行,列]——支持对行和列同时索引和切片(loc和iloc参考上面)NOTE:ix操作如果有左值,那么就默认会copy,如果没有左值那么就是对原始Frame进行操作,而loc和iloc默认都是对原始Frame的操作,所以推荐使用loc,如果需要复制,可以显式的调用Frame.copy(). 不过仍要注意的是frame2 = frame1.loc[a:d, :]确实不是复制是对原frame1的切片,但是frame1.loc[ [a, b, c, d], :]则是复制不是切片了。
at[行,列]和iat[i,j]——是定位到元素
所以对列的bool类型list切片应该用ix[:,bool_list],然而对行的bool类型list切片可以直接[bool_list]
- 总结
-
基本功能
-
重新索引
reindex,对索引重新排序,新加的使用缺失值填充。 -
丢弃指定轴上的项
frame.drop(['name'], axis=?) -
切片
frame[:2] 这个是直接按行切片,和常识有点不太一样,但是方便实际操作。
frame[ ['列1', '列i'] ],这个是按列切片 -
过滤
按照某一列的值过滤:frame[ frame['列'] > 5 ]
对于bool类型的list,除了上面的方法,也可以frame.loc[bool_list_X, bool_list_Y]进行过滤。 -
算术运算
frame1 + frame2 或者 frame1.add(frame2, fill_value=0)
sub做减法 div是除法 mul是乘法 -
函数应用和映射
Numpy的ufuncs也可用于操作pandas对象np.abs(frame)
还可以自定义函数(列或行的):
f = lambda x: x.max() - x.min()
frame.apply(f, axis=?)对元素的函数
format = lambda x: '%.2f' % x
frame.applymap(format)featQuant = sampleDF_feat.quantile(0.999) # 获得0.999的分位数
frameFeatUnderQuant = sampleDF_feat.where(sampleDF_feat <= featQuant, other=0) # 把异常高的数值排除掉总的来说,DataFrame可以用的是apply和applymap,Series可以用的是map:
apply:是对DataFrame的具体行或者列进行整体操作。比如可以取最大最小值
applymap:是对DataFrame的具体每个元素进行操作。
map:也是对每个元素进行操作,和applymap类似。 -
排序
frame.sort_index(by='name', axis=?, ascending=True)
-
-
汇总和计算描述
frame.sum(axis=?, skipna=True) 求和
frame.mean() 平均值
frame.count()
frame.min、max
frame.argmin、argmax 整数索引位置
frame.idmin、idmax 名字ID索引位置
frame.quantile() 计算样本的分位数(0到1),比如frame['col_name'].quantile([q/10 for q in range(1, 11, 1)])
frame.describe() # 描述各个列的详细统计信息
frame.info() # 描述各个列是否有缺失值和dtype -
处理缺失数据
frame.dropna() 丢弃NaN,可以给出阈值进行调节容忍度
df.fillna( {'列1': 值1, '列i': 值i}, inplace=True ) 默认返回一个新的对象
frame.isnull() 返回与frame一样大小的布尔值对象(值为NaN的为True,否则为False) -
其他常用
# 去重 df.duplicated(keep='first') # 这个会返回列的值重复与否的布尔值,默认对于重复值的第一个为False,其他重复的为True # 得到这个布尔后可以再进行切片去重,不过还有更便捷的方法: df['某列'].drop_duplicates(keep='first', inplace=True) # 这样直接得到去重后的结果。
-
从文件中读取
frame = pd.read_csv('file.csv', names=col_names, index_col='col_index')
除了读取本地文件还可以读取HDFS文件:首先需要安装需要的包:pip install hdfs 官网文档
然后代码中:from hdfs import Client from hdfs import HdfsError client = Client('http://name_node.url:50070') # 输入要链接的NameNode地址。如果是高可用的模式,会有多个name node,那么有个比较笨的方法就是挨个试,看看那个可用(也就是处于active状态)就用哪个。 print(client.list('/')) # 可以在这里添加try操作,catch到异常后去尝试下一个name node的链接。 try: with client.read('path/file.csv') as hdfs_in_fs: predictDF = pd.read_csv(hdfs_in_fs, names=predict_cols, index_col='Number') except HdfsError as e: print(e)
-
将Frame写入文件
frame.to_csv(path_or_buf='path/file', index=True, header=False, sep='|')
写入HDFS文件会有些麻烦,一是写的时候需要权限,把要写入的目录设置为hdfs dfs -chmod -R 777 write_Dir
;二是写入文件会遇到TypeError: a bytes-like object is required, not 'str',因为不像普通的文件可以在open的时候设置mode参数,所以还需要做如下操作:from hdfs import Client from hdfs import HdfsError client = Client('http://name_node.url:50070') # 输入要链接的NameNode地址。如果是高可用的模式,会有多个name node,那么有个比较笨的方法就是挨个试,看看那个可用(也就是处于active状态)就用哪个。 print(client.list('/')) # 可以在这里添加try操作,catch到异常后去尝试下一个name node的链接。 try: with client.write('path/file' overwrite=True, encoding='utf-8') as hdfs_out_fs: # 需要设置encoding不然遇到上面说的TypeError predictDF[['Gender', 'Prob']].to_csv(path_or_buf=hdfs_out_fs, index=True, header=False, sep='|') except HdfsError as e: print(e)
-
另外一种读写HDFS文件的方法
可以看到为了允许读写,上级目录已经将权限更改为全部可读写执行了,而且新创建的文件夹和文件都是用户dr.who
上面的代码确实可以读写HDFS文件,不过需要修改权限,这会出现下面情况:
这会给后续操作带来一定的麻烦,比如yarn用户在程序中对这个文件无法修改。因为只有读权限。
Client API介绍
不过还有一个API可以在python中使用,如果你的HDFS配置支持非安全的HttpFS的话。上面的使用Client的API是用的WebHDFS。连接的时候需要指定namenode,而在高可用的模式情况下,可能要多试几次才知道当前哪个node处于active状态。为了避免这两个问题,接下来展示另外一个API InsecureClient来实现。在接下来的代码中,使用的是HttpFS方式来读写HDFS
from hdfs import InsecureClient client = InsecureClient('http://HttpFS_node.url:14000', user='yarn') # 使用HttpFS所配置的角色主机域名及REST 端口 print(client.list('/')) # 无需像之前一样多次尝试当前输入的namenode是否可用 with client.read('path/in_file') as hdfs_in_fs: with client.write('path/out_file', overwrite=True, encoding='utf-8') as hdfs_out_fs: sampleDF = pd.read_csv(hdfs_in_fs, names=, index_col=) sampleDF.to_csv(path_or_buf=hdfs_out_fs, index=True, header=False, sep='|')
这个API的好处是:1、不需要修改权限,由参数的指定用户保证权限没问题。2、新写入的文件权限也无问题。3、可以支持远程读写,无需在集群上执行,在本地PC上运行即可。4、无需知道当前active的namenode是哪个,可以通过HttpFS主机直接连接。
新建的文件都是用户yarn的 -
合并数据集
- pandas.merge
根据一个或多个键将不同的DataFrame中的行连接起来。类似SQL的关系型数据库的连接操作
简单说就是将多个DataFrame按照指定的各自的列以某种方式(inner,left,right,outer)组合起来。
除了pd.merge还有frame_left.join(frame_right)这个方式 - pandas.concat
沿着一条轴将多个对象堆叠在一起
比如将两个frame的行数据堆叠起来。
frame_total = pd.concat([frame1, framei], axis=?, keys=['col1_name', 'coli_name']) - 举栗
合并Series到Dataframe中:purchase_1 = pd.Series({'Name': 'Chris', 'Item Purchased': 'Dog Food', 'Cost': 22.50}) purchase_2 = pd.Series({'Name': 'Kevyn', 'Item Purchased': 'Kitty Litter', 'Cost': 2.50}) purchase_3 = pd.Series({'Name': 'Vinod', 'Item Purchased': 'Bird Seed', 'Cost': 5.00}) df = pd.DataFrame([purchase_1, purchase_2, purchase_3], index=['Store 1', 'Store 1', 'Store 2']) s = pd.Series({'Name':'Kevyn', 'Item Purchased': 'Kitty Food', 'Cost': 3.00}) s.name = 'Store 2' # 合并s到df中 # 方法1,使用concat函数 df = pd.concat([df, s.to_frame().T]) # 方法2,使用append df = df.append(s)
- pandas.merge
-
打印DataFrame
打印DataFrame非常简单直接print就可以了,但是Pandas对于显示的行数、列数、宽度和长度都有默认限制,如果不期望看到...这样的省略号,或者不期望换行,那么可以如下设置:pd.set_option('display.height',1000) # 把1000换成None即为无限制,下面均是如此。 pd.set_option('display.max_rows',500) pd.set_option('display.max_columns',500) pd.set_option('display.width',1000)
上面的设置完之后会对后面的代码全部做这个配置处理,如果想在代码中动态的改变,还可以这么写:
with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.width', None, 'display.height', None): logging.info('输出打印DataFrame:\n{}'.format(data_frame))
这样在需要长显示的时候套上with就可以了,不需要的地方继续使用pandas的默认设置。
Matplotlib
常用作图方法
import matplotlib.pyplot as plt
%matplotlib inline # 在ipynb上显示图片
plt.plot([4, 3, 2, 1]) # 作图代码
plt.show() # 在python运行程序时显示
# 启动ipython作图时可以: ipython --pylab,这样就会自动import matplotlib.pyplot as plt而且不用show()就可以输出图表。
plt.plot([4, 3, 2, 1]) # 使用ipython --pylab后直接显示图图表
直接用数据作图
- 对于numpy的数组数据作图:
fig = plt.figure()
ax1 = fig.add_subplot(2, 2, 1)
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 3)
plt.plot([1.5, 3.5, -2, 1.6]) # 默认对最后创建的subplot作图
import numpy as np
from numpy.random import randn
_ = ax1.hist(randn(100), bins=20, color='k', alpha=0.3) # 直方图
ax2.scatter(np.arange(30), np.arange(30) + 3 * randn(30)) # 散列图
默认画图,直方图和散列图
- 强调数据点
plt.plot(randn(30).cumsum(), 'ko--') # 等价于下面语句
plt.plot(randn(30).cumsum(), color='k', linestyle='dashed', marker='o')
强调数据点位置
- 图中坐标和标题设置
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(randn(1000).cumsum())
ticks = ax.set_xticks([0, 250, 500, 750, 1000]) # 设置X轴的刻度
labels = ax.set_xticklabels(['one', 'two', 'three', 'four', 'five'], rotation=30, fontsize='small') # 刻度的标签
ax.set_title('My first matplotlib plot') # 图的标题
ax.set_xlabel('Stages') # X轴的名称
设置坐标轴的信息和图标题
- 添加图例
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(randn(100).cumsum(), 'k', label='one') # 创建图例
ax.plot(randn(100).cumsum(), 'g--', label='two')
ax.plot(randn(100).cumsum(), 'r.', label='three')
ax.legend(loc='best') # 把上面设置的图例(legend)创建生效
图例
- 保存图表到文件
plt.savefig('figpath.svg')
plt.savefig('figpath.png', dpi=400, bbox_inches='tight')
Pandas的绘图函数
- Series做柱状图
import pandas as pd
from pandas import Series, DataFrame
fig, axes = plt.subplots(nrows=2, ncols=1) # 获得subplot集合
data = Series(np.random.rand(16), index=list('abcdefghijklmnop'))
data.plot(kind='bar', ax=axes[0], color='k', alpha=0.7) # 竖向柱状图,不设置kind默认是线形图
data.plot(kind='barh', ax=axes[1], color='k', alpha=0.7) # 横向柱状图
Series柱状图
- DataFrame做柱状图
df = DataFrame(np.random.rand(6, 4),
index=['one', 'two', 'three', 'four', 'five', 'six'],
columns=pd.Index(['A', 'B', 'C', 'D'], name='Genus'))
print(df)
df.plot(kind='bar')
df数据打印:
Genus A B C D
one 0.017426 0.964258 0.479931 0.636357
two 0.020693 0.979753 0.846889 0.436802
three 0.650068 0.608675 0.964375 0.866141
four 0.523848 0.610598 0.296204 0.879183
five 0.419329 0.023081 0.442044 0.842727
six 0.926948 0.454734 0.436056 0.970364
- 直方图和密度图
comp1 = np.random.normal(0, 1, size=200) # N(0, 1)
comp2 = np.random.normal(10, 2, size=200) # N(10, 4)
values = Series(np.concatenate([comp1, comp2])) # 合并为一个Series
values.hist(bins=100, alpha=0.3, color = 'k', normed=True) # 直方图
values.plot(kind='kde', style='k--') # 密度图(kde表示标准混合正态分布)
直方图和密度图
- 散布图
plt.scatter(trans_data['m1'], trans_data['unemp']) # 散布图
plt.title('Changes in log %s vs. log %s' % ('m1', 'unemp'))
散布图
pd.plotting.scatter_matrix(trans_data, diagonal='kde', color='k', alpha=0.3) # 散布图矩阵
散布图矩阵
网友评论