数据之美与编程之美的完美结合
-------读《重构大数据统计》后记
一直以来,作为一名工科专业的在校生,都在尝试将书本上的理论知识转化为实践(对于我来说,这种实践即为编程)。但无论是简单的矩阵相乘问题还是一些统计机器学习算法都免不了较高的学习成本--通晓数学理论与程序设计。诚然,当下主流的编程语言或借助于语言标准或借助于第三方软件包对基本数学算法提供了支持(如C/C++中的math头文件和Java中的Math类库)。但此种简单调用API的方式,或者说类似于“黑盒测试”的使用手法最终会导致程序员知其然而不知其所以然。因此,一本通俗易懂的书,一份数理知识与编程技巧相结合的资料就大有裨益了。非常高兴在ChinaUnix上看到Broadview推出的这本《重构大数据统计》,从链接中分享的部分来看,理论与实践结合的风格使人耳目一新。下面简单就两个方面来谈谈我个人的阅读体会。
1.统计知识与程序设计相辅相成
毫无疑问,假如不了解数学期望、方差为何物纵然有再高深的编程技术也难有用武之地。可惜的是,市面上的大部分书籍都将两者分别著述,一边是阐述复杂证明过程的数学书,另一边是专讲编程语言语法的程序设计书,联系往往甚微。而《重构大数据统计》的风格独树一帜,果断抛却了大部分复杂推理过程的叙述,只在必要时给出简单步骤。通过数学公式与具体例子相结合的方式厘清统计学的概念,大大减轻了读者通过传统逻辑推导来理解数学理论的负担,强化了对概念“是什么,怎么用”的认识而不是纠缠在每一步推理的解释上。不仅如此,作者更从工程实际出发,将实现书中理论知识点的Java源码与读者分享,“手把手”教会读者如何利用语言工具将理论知识应用于工程实践。对于一些相对晦涩的概念(如Chapt3中的经验分布函数、QQ图)可通过先阅读源码的方式来做一些感性认识,之后再回过头来加深理解这些概念术语,方可事半功倍。当然如果遇到一些相对较容易的概念则可在参考源码的基础上进行编程实践,进一步熟悉该理论在工程当中的应用。两种阅读方式相辅相成,既深化了对知识点的理解,也加强了动手编程的能力。
2.重构代码,高效实用
“避免重复制造轮子”是一句金玉良言。的确,高效准确地使用已有的库不仅缩短了开发的周期而且提高了代码的鲁棒性。在本书公开的部分章节中,针对大部分的统计学理论都附了参考程序。作为读者(相信大部分都是IT工作者),完全可以在充分理解参考程序的基础上,将程序改写、重构并且建库,以方便以后的使用。虽然书中的代码是基于Java的,但是鉴于Java和当前主流的C/C++/C#语言在语法上的相似程度,转码工作应该是比较容易的。下面就书中Chapt2.1.4和Chapt2.2中的Java代码进行了封装打包和测试,并且将Chapt2.2中的Java代码转为C++代码,经测试可以获得相同的效果。
图1. 测试界面
图1中绘制了将Chapt2.1.4中的MeasureCalculator类和Chapt2.2中的FrequencyCalculator打包并测试的myEclipse界面。利用书中的测试数据可以获得一致的测试结果。
Java
|
C++
|
-
/*FrequencyCalculator.java*/
-
/*声明FrequencyCalculator类并且实现*/
-
-
import java.util.*;
-
import java.util.Map.Entry;
-
-
-
public class FrequencyCalculator {
-
private final int capacity;
-
private TreeMap<Object, Long> treeMap = new TreeMap<Object, Long>();
-
-
public FrequencyCalculator() {
-
this.capacity = 1000;
-
}
-
-
public FrequencyCalculator(int capacity) {
-
this.capacity = capacity;
-
}
-
-
public Object getMode() { //获取众数
-
if (null != treeMap) {
-
Object mode = null;
-
long modeCount = 0;
-
Iterator<Entry<Object, Long>> it = treeMap.entrySet().iterator();
-
while (it.hasNext()) {
-
Entry<Object, Long> e = it.next();
-
if (modeCount < e.getValue().longValue()) {
-
mode = e.getKey();
-
modeCount = e.getValue().longValue();
-
}
-
}
-
return mode;
-
} else {
-
throw new RuntimeException("不同元素个数超过" + capacity + ",本方法不能计算众数!");
-
}
-
}
-
-
-
public void calc(Object obj) { //更新treeMap中各个数字的频数
-
if (null != treeMap) {
-
if (treeMap.containsKey(obj)) {
-
treeMap.put(obj, treeMap.get(obj) + 1);
-
} else {
-
if (treeMap.size() > this.capacity) { //Too many different items!
-
treeMap = null;
-
}
-
treeMap.put(obj, new Long(1));
-
}
-
}
-
}
-
-
-
public void calc(double d) {
-
calc(new Double(d));
-
}
-
-
-
public void calc(long k) {
-
calc(new Long(k));
-
}
-
-
public void calc(int k) {
-
calc(new Integer(k));
-
}
-
-
public void calc(boolean b) {
-
calc(new Boolean(b));
-
}
-
-
public void calc(FrequencyCalculator freq2){//合并两个FrequencyCalculator中的treeMap
-
if (null != treeMap) {
-
Iterator<Entry<Object, Long>> it = freq2.treeMap.entrySet(). iterator();
-
while (it.hasNext()) {
-
Entry<Object, Long> e = it.next();
-
Object obj = e.getKey();
-
if (null != treeMap) {
-
if (treeMap.containsKey(obj)) {
-
treeMap.put(obj, treeMap.get(obj) + e.getValue().longValue());
-
} else {
-
if (treeMap.size() > this.capacity) { //Too many different items!
-
treeMap = null;
-
}
-
treeMap.put(obj, e.getValue().longValue());
-
}
-
} else {
-
break;
-
}
-
}
-
}
-
}
-
-
-
@Override
-
public String toString() {
-
java.io.CharArrayWriter cw = new java.io.CharArrayWriter();
-
java.io.PrintWriter pw = new java.io.PrintWriter(cw, true);
-
if (null != treeMap) {
-
pw.println("共有" + treeMap.size() + "个不同元素:");
-
Iterator<Entry<Object, Long>> it = treeMap.entrySet().iterator();
-
while (it.hasNext()) {
-
Entry<Object, Long> e = it.next();
-
pw.print(e.getKey());
-
pw.print(" : ");
-
pw.println(e.getValue());
-
}
-
pw.println("众数: " + getMode());
-
} else {
-
pw.println("不同元素个数超过" + capacity);
-
}
-
return cw.toString();
-
}
-
}
|
-
/*FrequencyCalculator.hpp*/
-
/*声明FrequencyCalculator类并且实现*/
-
-
#include <iostream>
-
#include <iomanip>
-
#include <map>
-
#include <algorithm>
-
#include <functional>
-
-
using namespace std;
-
-
class FrequencyCalculator{
-
private:
-
const int capacity;
-
map<double, long> treeMap;
-
-
-
public:
-
FrequencyCalculator();
-
FrequencyCalculator(int cap);
-
-
-
double getMode()const; //获取众数
-
map<double, long> getMap()const;
-
-
-
void calc(double d); //更新treeMap中各个数字的频数
-
void calc(const FrequencyCalculator& fc); //合并两个FrequencyCalculator中的treeMap
-
-
-
friend ostream& operator<<(ostream& os, const FrequencyCalculator& fc);
-
};
-
-
/*成员函数的具体实现*/
-
/*---------------------------------------------------------------------------*/
-
FrequencyCalculator::FrequencyCalculator():capacity(1000){}
-
FrequencyCalculator::FrequencyCalculator(int cap):capacity(cap){}
-
-
-
double FrequencyCalculator::getMode()const{
-
-
if(!treeMap.empty()){
-
double mode = treeMap.begin()->first;
-
long modeCount = treeMap.begin()->second;
-
//for_each(treeMap.begin(), treeMap.end(), bind2nd(mem_fun(&FrequencyCalculator::modeUpdate), mode));
-
map<double, long>::const_iterator citerEnd = treeMap.end();
-
for(map<double,long>::const_iterator citer = treeMap.begin(); citer != citerEnd; ++citer){
-
if(citer->second > modeCount){
-
mode = citer->first;
-
modeCount = citer->second;
-
}
-
}
-
return mode;
-
}else{
-
throw string("不同元素个数超过限制,本方法不能计算众数!");
-
}
-
}
-
-
-
-
-
-
-
void FrequencyCalculator::calc(double d) {
-
if (treeMap.find(d) != treeMap.end()) {
-
treeMap[d] += 1;
-
} else {
-
if (treeMap.size() > this->capacity) { //Too many different
-
cerr<<"Fail to Add New Elements as Too many different items Exists!";
-
}
-
treeMap.insert(pair<double, long>(d, 1));
-
}
-
return;
-
}
-
-
-
-
-
-
-
void FrequencyCalculator::calc(const FrequencyCalculator& fc){
-
if(treeMap.empty()){
-
treeMap = fc.getMap();
-
return;
-
}
-
-
-
map<double,long> tempMap = fc.getMap();
-
map<double, long>::const_iterator iterEnd = tempMap.end();
-
map<double, long>::const_iterator thisEnd = treeMap.end();
-
for(map<double, long>::const_iterator citer = tempMap.begin(); citer != iterEnd; ++citer){
-
if(treeMap.find(citer->first) != thisEnd){
-
treeMap[citer->first] += citer->second;
-
}else{
-
treeMap.insert(pair<double, long>(citer->first, citer->second));
-
}
-
}
-
return;
-
}
-
-
-
ostream& operator<<(ostream& os, const FrequencyCalculator& fc){
-
map<double,long> tempMap = fc.getMap();
-
-
-
os<<"共有"<<tempMap.size()<<"个不同元素"<<endl;
-
-
map<double, long>::const_iterator citerEnd = tempMap.end();
-
for(map<double, long>::const_iterator citer = tempMap.begin(); citer != citerEnd; ++citer){
-
os<<citer->first<<" : "<<citer->second<<endl;
-
}
-
os<<"众数: "<<fc.getMode()<<endl;
-
return os;
-
}
-
-
-
map<double, long> FrequencyCalculator::getMap()const{
-
return this->treeMap;
-
}
|
图2.FrequencyCalculator类的Java和C++实现
图2中给出了《重构大数据统计》一书中Chapt2.2中FrequencyCalculator类的两种编程语言的实现。其中位于屏幕左侧的是Java代码(完全参照了书中的实现),而右侧的C++代码则是由笔者进行转码实现的。两者实现了完全相同的功能:基于关联容器(Java中为TreeMap,C++中为map,均为基于红黑树的关联容器)统计目标数据集合中不同数据出现的频数,同时提供了获取众数和输出关联容器的功能。图2中两种不同语言实现的代码量基本相当,对于相同的成员函数实现方法基本一致,测试结果如图3,也是完全一致的。
FrequencyCalculator类 Java测试结果
|
FrequencyCalculator类 C++测试结果
|
|
|
图3 FrequencyCalculator类Java与C++的测试结果
统计学理论在计算机应用领域始终占有重要地位,尤其在机器学习、数据挖掘方面更是具有实际价值。随着大数据时代的进一步推进,云计算平台的逐渐成熟,统计学理论在未来必将有着广阔的应用前景。在这样的大背景下,像《重构大数据统计》这样理论与工程结合紧密的,应用性色彩鲜明的书籍与资料将使数以万计的IT工作者从中获益。
参考文献
[1]. 钱能.C++程序设计语言.清华大学出版社
[2]. Bruce.Eckel.Java编程思想机械工业出版社
[3]. 盛骤等.概率论与数理统计.高等教育出版社
阅读(756) | 评论(0) | 转发(0) |