Archive for the ‘python’ Category
python 三剑客
嗯,今天终于让我的 matplotlib 正常的画出来图了,看来后 matlab 时代又多了一个可以依赖的数值计算工具,那就是 python 的数值计算三剑客 numpy、scipy 和 matplotlib。从某个角度来说,其实除了 matlab 以外的确有不少选择,如 octave、GNU R 或者 scilab 之类的。每个软件都有自己的一些优点,当然也会有某些缺点。那么 python 系的优点自然就是作为一个 general purpose 的 programming language 和大量的标准库 module 让你能对很多问题给出快速的解决方案,最后这三个 module 详尽的 docs 绝对和 Matlab 自己的帮助有的一比。缺点包括没有 matlab 那么多牛 x 的 toolbox(依照这三个 module 顶多分别实现了 matlab 矩阵计算和一些基本数据结构,最常用的几个 toolbox 如信号处理、统计和优化的最简单的功能,可视化的部分功能)。这里有一个与 matlab 的比较。
我们首先看看 numpy 这玩意究竟给我们了一些什么功能。编译 numpy 需要的一些库如 ATLAS 或者 umfpack 之类的时候第一点发现是 numpy 和 matlab 的思路差不多,底层的运算打包给了优化的 C/FORTRAN 实现的 BLAS,并且利用这些数值包里面给的 linear system solver 和 eigenvalue solver(如 LAPACK)实现基本的矩阵操作。numpy 向 matlab 学习了不少很便捷的函数,如 linspace、eye 等方便用户创建需要的矩阵。
scipy 是一些科学计算中常用的操作的集合,包括
- cluster,聚类算法包括 vq 下面的 vq(vector quantization)、whiten(数据进行白化)、kmeans(2)(这个太有名了);hierarchy 下面的提供几个层次聚类算法(linkage、single、complete 和 average),以及将层次聚类的结果转换成 flat cluster 的方法(fcluster、fclusterdata 和 leaders),绘制 dendrogram 的功能
- constants 包括常用常数,比如常用的光速 c、普朗克常数等
- fftpack 进行快速 Fourier 变换,这个跟 matlab 的类似,(i)fft(2n) 以及 (i)dct
- integrate 包括积分和 ODE 的 solver,提供了类似 Matlab 的 quad、Gauss 方法的和 Romberg 方法、Simpson 公式等,ode 和 odeint 求解常微分方程
- interpolation 进行插值的函数:与 Matlab 类似的 interp1d,主要是 FITPACK 相关的 port
- io 进行 IO 的函数,对 matlab 的 save/load 的实现为 savemat 与 loadmat,另外一些格式不是很熟悉,也提供了对音频文件的支持(如 wav)
- linalg 线性代数部分:如线性方程组的解(solve),逆和伪逆(inv、pinv),行列式的值(det)、范数(norm),kron(Kronecker 乘积);特征值问题的求解如 eig;各种分解(如 lu、svd、cholesky、schur、qr 等);创建特殊矩阵(hankel、hilbert、leslie 等)
- misc 里面包含求导(derivative)、阶乘(factorial)、图像方面的(imread 等 matlab 等价)、fromimage(与 PIL 交互)
- ndimage 做 digital image processing 的部分:filters 提供了常见的 image filtering 的滤波器和卷积函数(类似 matlab),fourier 下的一些 fourier 滤波,interpolation 下对矩阵(图片)进行仿射变换等,measurements 提供了一些统计相关的信息(如 histogram、最大最小),morphology 提供了一些形态学方面的东西
- odr 包括 orthogonal distance regression
- optimize 包括一些 generic optimizer (类似 fmin、提供了 CG 和 l-BFGS 等,甚至还有一些 anneal、brute)和 root solver(二分法、Newton 法等、fsolve)、拟合如 curve_fit,utility 函数(line_search 与 check_grad)
- signal 包括信号处理的函数,也提供了 fft 和 convolution 相关的函数、B 样条、滤波器以及滤波器设计相关函数(似乎跟 matlab 信号处理的工具箱类似?)、小波
- sparse 对稀疏矩阵求解的函数,如稀疏矩阵的容器,以及相关的一些成员函数能完成基本的操作(转置),在 linalg 里面提供了稀疏矩阵相关的线性代数的操作(如 spsolve、eigs)
- spatial 包括空间数据的处理,如建立 kdtree,另外有 distance 下几种距离,还有 pdist(pairwise 距离)
- special 包括特殊函数:Bessel 函数(jn、jn_zero)、airy、ellipj 等椭圆积分相关的,等等
- stats 包含常用分布函数和随机数产生,rv_continuous 提供了 pdf、logpdf、cdf、logcdf、以及许多求相关的 moment、entropy 等信息的函数,对应有 rv_discrete 提供的一族,只是很多名字不这么叫;另外有一些 test 的实现;mstats 提供了带 mask 的统计量的计算,kde 提供了密度估计,
- weave 用于 C/C++ 接口,据说可以直接编译某些 c/c++ 函数直接运行…
matplotlib 实现了一个基本的 visualization 界面,其中 pyplot 是主要的画图命令部分。另外它还提供了一个 pylab 环境,这是模仿 matlab 提供的一个交互式环境。
下面是一个使用这些东西实现的 PCA 以及例子。
#!/usr/bin/env python
import numpy as np
import scipy as sp
def pca (X, dim):
mu = np.reshape (np.mean (X, 1), [X.shape[0], 1])
X -= np.repeat (mu, X.shape[1], axis=1)
U, S, V = sp.linalg.svd (X, full_matrices = False)
return mu, U[:, :dim], S[:dim]
if __name__ == '__main__':
import matplotlib.pyplot as plt
import scipy.stats as stats
X = stats.norm.rvs (size=[2, 100])
A = stats.norm.rvs (size=[2, 2])
X = np.dot (A, X)
mu, U, v = pca (X.copy (), 1)
plt.figure (1)
plt.scatter (X[0, :], X[1, :])
plt.grid (True)
plt.plot (mu[0], mu[1], 'ro')
ed = mu + v * U * 0.3
plt.plot ([mu[0], ed[0]], [mu[1], ed[1]], 'k-')
plt.show ()
为了让 matplotlib 能正常工作,一般需要设置一个合适的 backend,我们应该编辑 ~/.matplotlib/matplotlibrc,我用的 Mac 需要设置
backend: MacOSX
其他的平台可以设置 Qt 或者 GtkCairo 等。之后我们就能生成类似如下图片的 figure 了。
值得一提的是,现在不少项目也开始采用这三个库提供的开发环境,比如 DBN。看来以后有必要积累一些 python 这方面的东西,在没有 Matlab 的日子里面,估计 python 这个还是能比较通用的。
———————-
And said, I pray you, brothers, do not so wickedly.
PIG 的 UDF 之深入研究
既然接了这么个活,就好好的研究一下 hadoop 系的东西了。大致的打算是从最近需要的 PIG UDF 开始,后面会重新弄一下 hadoop,另外比较感兴趣的是能做 serving 的 HBase。之后有空再看看 zookeeper。
PIG 方面的文档最近似乎变多了一些,这里主要参考其 API 文档和这个 tutorial。
环境
首先要下载 PIG 的源文件和二进制文件(见这里),然后如果你需要的话可以开始编译,PIG 自己使用的是 ant。如果想直接使用,可以直接把对应的 jar 文件、lib 之类的 copy 出去。PIG 的 jar 分两个,一个是自带 hadoop 的,一个是单独的。如果我们想写点 UDF 之类的,应该指定其一就行了。传统上会指定一个 PIG_HOME,不过好像新的不要求了。
UDF 的分类
我们的 UDF 继承下面四个类之一(在org.apache.pig 下):
- EvalFunc,用于求值或者数据聚合
- FilterFunc 用于过滤,本质上就是 EvalFunc<Boolean>
- LoadFunc 用于读取特殊的数据类型
- StoreFunc 用于存储特殊的数据类型
特别的 EvalFunc 在实现了 Algebraic 接口时可以在 reducer/combiner 里面进行 reduce 方面的工作。对于不使用 combiner 的 UDF,可以使用 Accumulator 接口来实现。
EvalFunc 以及 Algebraic、Accumulator 接口
EvalFunc 是一个 generic 类,类型指定的是返回类型,PIG 里面使用的类型一般放在 org.apache.pig.data 下面。EvalFunc 需要实现的是 exec 函数,它返回的就是 generic 指定的类型,且输入是 org.apache.pig.data.Tuple。
一个 Algebraic 接口包含三个接口函数,getInitial、getIntermed 与 getFinal,它们的分别是 mapper、combiner 和 reducer 侧调用,返回的是一个 EvalFunc 的类名,这样我们实际上需要提供额外的三个 EvalFunc 的实现,它们对应的输入也是 Tuple,一般在 getInitial 返回的类对应的 exec 需要处理一个 tuple(可以是 bag 的一部分),而在 getIntermed 和 getFinal 里面需要将这些值组合起来。比如我们如果需要实现 COUNT,我们就需要在 getInitial 时返回一个能计算 bag 或者 map 大小的 EvalFunc(比如多个 mapper 每个 mapper 处理这个 bag 的一部分都会调用这个函数获得当前 bag 的大小),然后无论是 combiner 还是 reducer 都是将这些大小相加并返回和。
一个 Accumulator 的接口包含 accumulate、getValue 和 cleanup 三个函数,这三个函数将在每碰到同一个 key 对应的 value 是进行 accumulate,在该 key 对应 tuple 遍历完后调用 getValue 获得 accumulation 的结果,cleanup 用于初始化这个 accumulator 内部的结构。比如计算 MAX,我们需要保留一个当前最大值,cleanup 的时候将该值设为负无穷大(Java 可以用 null),然后每 accumulate 时更新该值或者 skip,最后 getValue 的时候返回。
EvalFunc 可以用 reporter 对象的 progress() 方法更新进度。重写 getCacheFiles 能够将某些 cache 文件传到 UDF 执行的本地,并且就可以在需要的时候读取。UDF 的构造函数可以在 define 里面调用。使用 -Dudf.import.list 可以将给定的几个 package 里面的类全部 import,这样就不需要写 canonical name 了。可以用
Schema
EvalFunc 可以使用 outputSchema 函数进行 org.apache.pig.impl.logicLayer.schema.Schema 的运算,从这点来说能够帮助 PIG 了解一个运行时多态返回的不同 Schema。outputSchema 函数输入是 input 的 Schema,我们可以用 getField 获得这个 tuple 每个部分的 Schema,并返回一个 Schema,用来表达返回类型。还是用最简单的例子来说明一下,比如我们写了一个求平方的东西,如何能为 Integer、Float 或者 Double 返回需要的类型呢?
import java.io.IOException ;
import org.apache.pig.EvalFunc ;
import org.apache.pig.data.Tuple ;
import org.apache.pig.data.TupleFactory ;
import org.apache.pig.impl.logicalLayer.schema.Schema ;
class Square extends EvalFunc<Tuple> {
public Tuple exec (Tuple t) throws IOException {
if (t == null || t.size () != 1)
return null ;
try {
Tuple output = TupleFactory.getInstance ().newTuple (1) ;
Object o = t.get (0) ;
if (o instanceof Integer) {
int l = ((Integer) o).intValue () ;
Integer r = new Integer (l*l) ;
output.set (0, r) ;
} else if (o instanceof Double) {
double l = ((Double) o).intValue () ;
Double r = new Double (l*l) ;
output.set (0, r) ;
} else if (o instanceof Float) {
float l = ((Float) o).intValue () ;
Float r = new Float (l*l) ;
output.set (0, r) ;
} else {
return null ;
}
return output ;
} catch (Exception e) {
System.err.println (e.getMessage ()) ;
return null ;
}
}
public Schema outputSchema (Schema input) {
return new Schema (input) ;
}
}
Java 似乎不支持直接写个 * 在 exec 里面,我们必须根据每种类型重写其实现,而且 ms 由于包裹过的 Double、Integer 之类也没法使用方法进行计算,最后就很挫… 不知道有啥优美的解决方案不?另外可以使用 getArgToFuncMapping 将对应的 schema 交给不同的实现,这个返回的是一个 List<FuncSpec>。
LoadFunc 与 StoreFunc
需要搞清楚的是 LoadFunc 和 StoreFunc 是建立在 InputFormat/OutputFormat 之上的一层封装,通常由 RecordReader 负责将 record 取出来,LoadFunc 将其转换成为 PIG 的数据类型,如 Tuple、Map 等,而 StoreFunc 恰好相反,将 PIG 类型通过 RecordWriter 写入。
LoadFunc 需要重写 getNext 函数:首先调用 RecordReader 的 nextKeyValue() 检查是否有下一个,然后使用 currentKey/Value 获得需要的内容。最后返回 Tuple;StoreFunc 重写 putNext 函数,根据类型写到 RecordWriter 里面。
如何传递 configuration
如果我们需要通过 XML 或者 -D 参数传递一些东西给 UDF,可以使用 org.apache.pig.impl.util.UDFContext 这个类,这是一个 singleton,我们用 getJobConf 就能拿到当前 job 的 Configuration 对象,这样就能获得传递进来的参数了。
Jython 一个 BT 的功能
如果你只想写一个 python,而不把 python 实现的 UDF 与 PIG 分离,可以利用 python UDF 中的 __main__ ,下面是一个来自以上所说 tutorial 的例子,
#!/usr/bin/jython
from org.apache.pig.scripting import *
@outputSchema("word:chararray")
def helloworld():
return 'Hello, World'
if __name__ == '__main__':
P = Pig.compile("""a = load '1.txt' as (a0, a1);
b = foreach a generate helloworld();
store b into 'myoutput'; """)
result = P.bind().runSingle();
还能有 JS
比较奇葩的是 PIG 现在也支持 JS 作为脚本写 UDF(不知道能不能用 jQuery/YUI 之类的东西啊…),不过鉴于 python 已经能搞定很多东西了,就不仔细研究这部分了。
——————
I will go down now, and see whether they have done altogether according to the cry of it, which is come to me; and if not, I will know.
创建一个虚拟 python 环境
介绍给 mm 一个小工具。
这个工具叫 virtualenv,用起来很简单,适合需要自己定制 python 环境的 heavy 用户。先装个 virtualenv,然后创建一个虚拟环境,我比较喜欢的是 –relocatable 和 –no-site-packages,这样能够将此目录移动,如果某些 package 还是希望与系统共享(比如 cluster 上面),可以去掉第二个选项,让 package 稍微小点。
这样获得的目录下有 bin/python,我们可以通过 source bin/activate 激活这个环境,这表示我们可以简单的将提交的 shell 脚本里面嵌入这行,之后的 python 系列的 job 就会自动的使用这个虚拟环境里面的东西了。为此,我们可以先切换到此目录里面使用 pip 等工具安装我们需要的各种 package。值得注意的是尽管这个环境看起来非常独立,但是它并不是一个完整的 python,它的解释器其实是指向系统的 python,因此如果系统没有某个版本的 python 它是没法创建的;它的运行环境依赖于系统,这意味着链接到的 so 仍然是系统上的。它的好处是能将用户 package 里面的 so 正确的放置。
退出可以用 deactivate。
比如要搞个 fftw3,可以在这个环境里面把 fftw3 的 so 之类的 setup 好,然后 pip install fftw3 就可以找到了。似乎比较容易。
这与 Windows 下面打包程序还是非常不同的,似乎有 PyInstaller 之类的可以帮我们做 install package。这个差不多就是为了 deployment 用的了。而 virtualenv 适合不同版本 package 的环境进行比较什么的。
——————
And the men rose up from there, and looked toward Sodom: and Abraham went with them to bring them on the way.
