2013年2月3日 星期日

Map Reduce in MongoDB

1. 在程式內呼叫 mongodb 內建之 map reduce 的方法
 
from bson.code import Code 
# main logic
log_collection = getReadCollection('LOG')
mapper = Code(mapper_code)
reducer = Code(reducer_code)
output_collection = getReadCollection('PAGEVIEW')
results = hfd_collection.map_reduce(mapper, reducer, output_collection)
 
ps: mapper_code 及 reducer_code 是由 javascript 作為語言的程式碼片段字串, 例如
 
# mapper_code
function() {
 if (this.country=="US"){ 
         emit(this.number , {freq: this.freq})
  }
}
 
# reducer_code 
function reduce(key, values) {
     var total = 0;
     for (var i=0;  i< values.length ; i++ ) {
          total += values[i].freq;
     }
     return {freq: total}
}
 
2. 注意事項
 
最後產生的結果會如下格式:
{
   "_id" : key,
   "value" : { value }
}
 
而 key 即是 mapper_code 中 emit 函式的第一個參數 , 上例中最後運算的結果格式如下
{
  "_id" : u223686999,
  "value" : { "freq" : 30 }
}
 
然而若要讓 map reduce 以多個 key 為條件去做 aggregation ( google 關鍵字可下 : compound key + mongodb ) , 則可修正如下:
 
# mapper_code
function() {
 if (this.country=="US"){ 
         emit( { number: this.number ,date: this.date } , {freq: this.freq})
  }
}
則會產生
{
  "_id" : { 
     "number" : u862236699,
     "date" : ISODate("2013-01-31T00:00:00.0Z")
   }
  "value" : { "freq" : 30 }
}
 
Reference : http://cookbook.mongodb.org/patterns/unique_items_map_reduce/

2012年9月30日 星期日

HTML 內文擷取

各式各樣網頁文字內容的掘取是做各種後續處理 (例如撰寫 search engine 的 crawler) , 分析的第一步, 一般人想到掘取一個網頁內容最直覺的想法就是發送 HTTP GET, 再透過一些 meta tag , DOM processing library 來將回傳的 html 中之標籤移除 (透過 beautifulsoup 之類的 html cleaning tool); 然而, 在近代的網頁中, 例如論壇或部落格, 真正重要/有意義的內容往往只佔整個頁面的一小部分 (只保留 user generate article, 而不是廣告 , 留言, social widget,  tags , button 等與主要文章不相關的 text)

另外, 除了便於分析研究等相關工作外, 網頁內容掘取的技術也被 apply 到許多應用上 , 例如可以將網頁文字內容擷取並美觀的應用 Readability , 可以讓使用者將有興趣閱讀的網頁內容 "稍後再看" 的 pocket app ; rss/news reader app 如 pulse , feedly , 甚至是為了讓現有網頁能夠在 mobile 端被適當的呈現給使用者

網路上有人整理了幾種目前解決 html content extraction 演算法的比較 : http://tomazkovacic.com/blog/56/list-of-resources-article-text-extraction-from-html-documents/ 裡頭整理了目前學界在此問題上的 state-of-arts solutions.

而對於開發者來說, 其實大可不必要花太多時間研讀上述所提及的演算法 ; 開發出 readability 的 arc90 公司除了提供 API 之外也將其 source code 開放源碼, 目前更陸續被 port 在許多不同的語言 , 例如 php , ruby 與 python , java 等等版本 , 讓使用者可以自行選擇適合的

除了 readability 的 solution 外, 以下整理出一些其他的 solutions :

Google mobilizer : http://www.google.com/gwt/n
Pocket API 提供的 parser : http://getpocket.com/api/
fivefilters  : http://fivefilters.org/content-only/
viewtext : http://viewtext.org/
Goose : http://jimplush.com/blog/goose





2012年8月29日 星期三

redis in python



Redis 為 Remote Dictionary Server 的縮寫, 是一款 In-Memory 的 NoSQL 資料庫, 因為是 In-Memory DB , 在資料的 access 上會比其他任何形式的資料庫都來的快速, 但是在規劃 data model 時必須注意資料的容量受限於實體記憶體的大小 ; 另外值得一提的是, redis 相對於其他的 NoSQL database , 提供了更為多元的 high-level data structure , 讓開發者在規劃 與開發時更為彈性與便捷


(1) 安裝 redis database

在 http://redis.io/download 中可以選擇欲使用的版本, 網頁下方也清楚展示了安裝的方式 ; redis-cli 是一個互動式的操作環境, 可以在此程序中直接輸入指令來操作資料庫

(2) 安裝 redis python client

由於 redis 的便利性, 使其廣為各個語言所支援 , 目前多數主流程式語言的 redis client 皆已 被開發完成 ; 而在 python 中, 目前多用 redis-py 來開發

只要在 .py script 檔中 import redis 即可再該檔案內操作 redis database
redis-py 進一步使用方式可以參考 http://degizmo.com/2010/03/22/getting-started-redis-and-python/

(3) 注意事項

以下列出一些 redis 原生指令與在 redis-py 中幾個差異處

a. select db : 在 redis-cli 環境中可以透過 select n 來切換目前的 key-space ( 例如可以用來區分 testing 與 deploy 的 db 版本 ) , 然而在 python 中只能透過一開始在初始 redis 物件時來選擇 key space , 例如 r = redis.Redis("localhost",db=1) 即為選擇第一號 data space

b. 刪除某個 key : 在 redis-cli 中欲刪除某個 key 的指令為 del key_name , 然而 del 在 python script 中是被視作保留字 (reserved word) , 因此在 redis-py 中要使用 r.delete(key_name)


2012年4月28日 星期六

在 ec2 (ubuntu)上使用 web.py 透過 nginx 與 fastcgi 佈署 web application


最近要在 amazon ec2 (ubuntu 11) 上使用 python + Redis 開發一套簡單的 api , 因此選擇了一套 lightweight 的 http framework 來 handle REST http requests , 因為也是初學 python , 因此首先選擇嘗試較輕量化的 web.py 而不是大多數人使用的 django 

在 web.py 的官方網站 中提到了三種主要 deployment 的方式 , 分別是透過 apache , lighttpd 與 nginx , 分別試過這三種途徑以及自身需求後[1] , 覺得透過 nginx + fastcgi 的方式算是相對簡潔與高效的作法 , 在此簡單記錄步驟並且補充官網上沒提到的一些值得注意的細節

step 1: 下載必要的套件

sudo apt-get install python-flup
sudo apt-get install nginx
sudo apt-get install fast-cgi

step 2: 創建佈署 app 時對應的 server 設定檔

將原始設定檔拷貝一份, 並且在 HTTP { /.../ } 中加入以下 

location / {
        fastcgi_param REQUEST_METHOD $request_method;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param CONTENT_TYPE $content_type;
        fastcgi_param CONTENT_LENGTH $content_length;
        fastcgi_param GATEWAY_INTERFACE CGI/1.1;
        fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
        fastcgi_param REMOTE_ADDR $remote_addr;
        fastcgi_param REMOTE_PORT $remote_port;
        fastcgi_param SERVER_ADDR $server_addr;
        fastcgi_param SERVER_PORT $server_port;
        fastcgi_param SERVER_NAME $server_name;
        fastcgi_param SERVER_PROTOCOL $server_protocol;
        fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9002;
}


step 3 : 修改 python script 加入 fast-cgi 的接口

例如:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import web

urls = ("/.*", "hello")
app = web.application(urls, globals())

class hello:
    def GET(self):
        return 'Hello, world!'

if __name__ == "__main__":
    web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr)
    app.run()


主要是在 app.run() 前加入 web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr) 


修改前可以先試試 #python your_script.py , 沒特別指定 port 的話預設是跑在 port 8080 , 看 script 是否有誤 , 這裡常會碰到的問題是 8080 被佔住的問題 , 如果被佔住用指令檢查一下並且 kill 掉該 process 即可

step 4 : start & stop script

假設我們的 web.py 主程式名為 code.py , 並且放在 /home/ubuntu/www 下
則 :

# sudo spawn-fcgi -d /hom/ubuntu/www -f /home/ubuntu/www/code.py -a 127.0.0.1 -p 9002

如果成功的話 , fcgi 會給出一個 pid , 之後關閉要透過這個 pid , 或者 # pgrep -f "python /home/ubuntu/www/code.py" 找回 pid , 並且透過 sudo kill -9 pid 來關閉 fcgi

常見的怪問題都是發生在這個步驟中, 例如 502 bad gateway 等, 一開始會以為是 nginx 的問題 , 但經過自己的經驗, 502 一般都是 fast-cgi 沒有成功啟動所造成 (即使如此 , 系統還是有可能會給出成功啟動的訊息! ), 可以在 spawn-fcgi 的指令中 , 加入 -n 參數來看詳細的資訊 或者是在執行指令後, 透過 sudo nentstat -antup 來看是否有成功 spawn fcgi process


step 5 : start & stop nginx service

使用 # sudo nginx -c /home/ubuntu/www/my_nginx.conf 來啟動 , 並且可以透過 sudo service nginx stop 來停止


[1] 由於會搭配 redis 作為後台資料庫 ,在記憶體的資源取捨下放棄 apache 這個較為廣為使用的但較吃記憶體的套件

2012年3月27日 星期二

關於 Hidden Markov Model 的一些整理


  • Markov Property: 如果一個過程(process)的"將來"僅依賴於"現在"而不依賴過去, 則這個過程具有Markov Property, 或稱此過程為馬科夫過程, 即 X(t+1) = f(X(t))
  • Markov Chain: 時間和狀態都離散的馬科夫過程稱為 Markov Chain
  • Hidden Markov Model 與 Markov Model 的差別: 除了 State 之間的 transition, 每個 State 還有自己對於各 Observable symbol 的產生機率
  • "Hidden" of Hidden Markov Model: 在大部分的情況下, 我們無法直接得知真正的"狀態"是什麼, 能夠直接觀測到的往往是一些"表徵", 例如頻率的高低
  • HMM 能夠解決的三個問題
    • (1) 給定一經觀察得到的 sequence 及 model , 評估此序列發生的可能性
      • 即 P(觀察到的現象|模型)     
      • Solution : Forward Algorithm
    • (2) 給定觀察序列以及 model , 找出一個最合理能夠解釋觀察率列的 State 序列    
      • 即 P(狀態序列|觀察到的現象,模型)    
      • Solution : Viterbi Algorithm
    • (3) 經由觀察序列學習出模型的參數    
      • 即 怎樣的 model 可以使 P(觀察到的現象|模型)最大化    
      • Solution : Baum-Welch Algorithm

2012年3月11日 星期日

存取 android market 資料的方法 (二) 第一次爬 Android App Market 就上手 ! Head first crawling Android app market


廢話不多說, 照做以下步驟就可以把整個 market 上的 app 資料都般回家!

開始!


1. 前置準備 

    1.1 一台裝有 Ubuntu 的電腦 

   1.2 並準備好 Ruby on Rails 的環境
          ps. 可參考 http://blog.longwin.com.tw/2008/11/ruby-on-rails-linux-environment-build-2008/ 或網路上任何相關文章 , 此文章是用 Ubuntu 10.04 版

    1.3 安裝 hg
           $sudo add-apt-repository ppa:tortoisehg-ppa/releases
           $sudo add-apt-repository ppa:mercurial-ppa/releases
           $sudo apt-get update
           $sudo apt-get install mercurial python-nautilus tortoisehg

     1.4 下載 source code
           $hg clone https://code.google.com/p/android-marketplace-crawler
           裡頭主要有兩個部分 , 分別是 crawler 以及 server           

2. 準備 crawler 
 
     2.0 安裝 ANT $sudo apt-get install ant
  
     2.1 準備 market-id : 最簡單的方式是在手機上撥打 *#*#8255#*#* 
           會出現如圖畫面 



           請記住紅線部分的字!


     3.1 填入 /crawler/src/com/marketplace/io/Secure.java 中 Secure() , getUsername() , getPassword() 中填妥你的 google 帳號 , 例如 : preferences.put("username_key","你的google帳號");   

     3.2  修改 /crawler/src/com/marketplace/util/Device.java. 中 Device() 的 market id 例如 this.marketId = "你的marketId";

     3.3  修改 /crawler/build.xml 第 38 行
               <fileset dir="/home/你的帳號/android-marketplace-crawler/crawler/bin"/>

     3.3 編譯程式 : $ant          

     3.4 $mv /crawler/permission /path_to_jar_file
           一般情形下 /path_to_jar_file 會在 /crawler/dist/lib 下

4. 準備 server

     4.0 修改 /android-marketplace-crawler/server/Gemfile 如下
           gem 'rake' , '>= 0.9.2' 
           gem 'mysql2', '0.2.18'
           存檔後 , $bundle update
     
    4.1 修改 /config/database.yaml
          增加 socket: /var/run/mysqld/mysqld.sock
          填上 root 之 密碼欄位 
          存檔後 $bundle check,  $bundle install

    4.2 建立資料庫 
          $rake db:create
          $rake db:seed
          $rake db:migrate

    4.3 啟動 Rails server     
          $rails s

5. 開始 crawling! 把資料都搬回家!
    
    切換到剛編譯完的.jar檔所在目錄 
    $java -jar crawler.jar -c


最後, 可以透過瀏覽 http://你的伺服器IP位置:3000 瀏覽目前已經被你爬下來的app資訊!



2011年11月5日 星期六

Installing XMPP Server on Ubuntu Linux

Instant messaging is a popular application in our life ,  most of today's smartphone IM apps are build upon a protocol named XMPP like google talk , Whatsapp , skype ,etc., It it useful to learn how to setup your own XMPP server when we consider to make our app user can chat with some other users on the move (for example: mobile chat room or a mobile MMORPG ) or the real-time server push function (like the Google's C2DM with app engine).

To apply XMPP functionalities to our app , we need to have a server side for handling the account management and message forwarding first.


Here are the steps to install an XMPP server (on Ubuntu Linux):

    and extract :
         #tar -xzvf openfire_3_0_0.tar.gz
           #mv openfire /opt
2. Start Installation Process
    
    a.  #bin/Openfire start
    b.  Insert http://localhost:9090 in your browser to start the web-based wizard
    c.  You will need to have a database system (ex: mysql) on your server that can be accessed by Openfire. Create a table for Openfire when or before you are in the database setup step.   
    d. Now you can access your own XMPP backend server through http://your-domain:9090

We will have our XMPP client in Android platform in the next post !! Have fun !