2010年12月17日金曜日

気象庁全国合成レーダー降雨



気象庁の全国合成レーダー降雨データがこちらで配布されている。
データは10分ごとにある。
データはtarでアーカイブされており、その中に含まれるデータはgrib2形式である。
清水慎吾さんがこちらで、データをgrads形式に変換するプログラムを公開している(全国合成気象庁レーダ雨量,GRIB2形式)。

以下のプログラムはファイルをネットから読み込んで、清水さんのツールでgrads形式に変換するpythonスクリプトである。

RadarToGradsはファイル(tarファイル)をネットから呼んで、tarファイルから降雨データのバイナリファイル(grib2)を取り出し、gradsファイルを作る。
grib2からgradsへのファイル変換は清水さんのjmaradar2binで行っている。gradsコントロールファイル("view_radar.ctl")が自動的にできるようにmain.cで"gradsout=1"としている。

PlotByGradsは作ったgradsデータを使って、gradsでプロットしている。gradsを操作するのに、pythonインターフェイスを使っている。

メインでは、これらを使って、2010年10月20日午前8時00分世界標準時(日本時間午後5時)の降水を東経125-135度北緯24-35度でgradsデータに変換して、それをプロットしている。これは奄美大島で豪雨が発生したころにあたる。

import_jmaradar.py
def RadarToGrads(dir=".",gradsbinname="temp.bin",year=2010,mon=1,day=1,hour=0,mi
n=0,slon=118.,elon=150.,slat=20.,elat=48.):
    """
   Import JMA rain radar data (every 10min) from http://database.rish.kyoto-u.ac
.jp/arch/jmadata/synthetic-original.html
   Then convert data to grads file using tool by Dr. Shingo Shimizu (http://shim
izus.hustle.ne.jp/wiki/wiki.cgi?page=%A5%EC%A1%BC%A5%C0%B4%D8%CF%A2%A5%E1%A5%E2)
 

   usage:
       RadarToGrads(dir=".",gradsbinname="temp.bin",year=2010,mon=1,day=1,hour=0
,min=0,slon=118.,elon=150.,slat=20.,elat=48.):
     
   input
      dir: directory to import tar file
      gradsbinname: file name for grads binary
      year, mon, day, hour, min  : time for data (every 10 min) in UTC
      slon,elon  Western,East bounday
      slat,elat  South,North bounday


   output files
      grads binary: gradsbinname
      grads control: view_radar.ctl        
         
    """


    import os
    import tarfile
    import urllib

    # filenames
    syear="%04d" % year
    smon="%02d" % mon
    sday="%02d" % day
    shour="%02d" % hour
    smin="%02d" % min
    date=syear+smon+sday+shour+smin
    tarname="Z__C_RJTD_%(date)s00_RDR_JMAGPV__grib2.tar" %locals()
    urldir="http://database.rish.kyoto-u.ac.jp/arch/jmadata/data/jma-radar/synth
etic/original/%(syear)s/%(smon)s/%(sday)s/" %locals()
    filename="Z__C_RJTD_%(date)s00_RDR_JMAGPV_Ggis1km_Prr10lv_ANAL_grib2.bin" %l
ocals()

    # get tar file
    if not os.path.exists(dir+"/"+tarname):
        if not os.path.exists(dir): os.mkdir(dir) 
        # Download the data
        print 'Downloading data, please wait'
        opener = urllib.urlopen(urldir+tarname )
        open(dir+"/"+tarname, 'w').write(opener.read())
        print 'Downloaded'

    # extract radar precipitation file
    tarfile.open(dir+"/"+tarname, 'r').extract(filename)

    # convert to grads file
    os.system("jmaradar2bin %(filename)s %(gradsbinname)s %(slon)s %(elon)s %(sl
at)s %(elat)s" %locals())
    os.system("rm %(filename)s" %locals())

####################################################################

def PlotByGrads(pngname):
    """
      plot converted JMA radar rain data
    """
    from grads.galab import GaLab  # for python interface for grads
    ga = GaLab(Bin='grads',Window=False,Echo=False,Verb=0,Port=False)
    ga.open("view_radar.ctl")
    script="""
    set grads off
    set mpdset hires
    set gxout shaded
    set clevs 0 1 5 10 20 30 50 80
    d rr
    cbarn
    printim %(pngname)s white
"""
    ga(script %locals())


#####################################################################

if __name__ == '__main__':

# radar file to grads 
    RadarToGrads(dir="DATA",gradsbinname="jmaradar.bin",year=2010,mon=10,day=20,
hour=8,min=00,slon=125.,elon=135.,slat=24.,elat=35.)

# plot by grads
    PlotByGrads("jmaradar.png")


2010年11月7日日曜日

Google Graphで棒グラフ



GE-GraphというGoogle Graph上に棒グラフをプロットできるフリーソフトがある。 http://www.sgrillo.net/googleearth/gegraph.htm
これを使ってみた。

結果から。下の図は正味海洋-大気熱フラックス(海から大気)の1月長期平均をGoogle Earth上で棒グラフにしたものである。正値のみ使っている。
このkmzファイルはこちら

GE-Graphに入力するデータとして
場所名,緯度,経度,値
で構成されるcsvファイルを用意する必要がある。
上の例のcsvファイルはコレ(その1,その2
GE-Graphが5000地点以上を受け付けないのでファイルを2つに分けている。場所名は特に意味は無いので全て0を入れている。

上のデータをGE-Graphに読み込ませる。設定は以下の図のようにした。
それぞれのデータで"Run"を行ってkmzファイルを作った。Google Earth上で二つのファイルをまとめて、saveしたのが上のkmzファイルである。

最後にcsvファイルを作るpythonプログラムを添付する。
NCEP/NCARのデータをESRLのサイトからPython Interface to GrADS を使って読み込む。
latent/sensible heat flux, longwave/shortwave radiationを全て足して正味の熱フラックスを計算する。
陸・海マスクを読み、海のデータだけを使う。また正(海から大気)のデータだけ残している。データは4000点づつセーブする。
from grads.ganum import GaNum  # for python interface for grads
import numpy as np

#====== DATA read
ga = GaNum(Bin='grads')
ga("sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis.derived/surface_gauss/lhtfl.mon.ltm.nc") #net latent heat flux
ga("sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis.derived/surface_gauss/shtfl.mon.ltm.nc") #net sensible heart flux
ga("sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis.derived/surface_gauss/nswrs.mon.ltm.nc") #net shortwave radiation
ga("sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis.derived/surface_gauss/nlwrs.mon.ltm.nc") #net longwave radiation
ga("set t 1")  # January
ga("netheat=lhtfl.1+shtfl.2+ nswrs.3+nlwrs.4") # all net heat
netheat=ga.exp("netheat")
ga("close 4")
ga("close 3")
ga("close 2")
ga("close 1")
ga("sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis/surface_gauss/land.sfc.gauss.nc")  #land sea mask
land=ga.exp("land")
del ga

lon=netheat.grid.lon
lat=netheat.grid.lat

#===== DATA manupilation
data=netheat.data
data[land>0]=-999.  # remove values on land

x,y=np.meshgrid(lon,lat)

vl=0.               # data criteria 

xout=x[data>vl].flatten()      # only  values >vl  are used
xout[xout>180]=xout[xout>180]-360. #   -180.=<lon<=180.  
yout=y[data>vl].flatten()      # only  values >vl  are used
zout=data[data>vl].flatten()   # only  values >vl  are used
points=np.zeros(data[data>vl].size) # position number (anythin is ok)

#==== DATA output
nn=zout.size/4000+1
for n in range(nn):
    st=4000*n
    en=4000*(n+1)
    np.savetxt("data%(n)s.txt" %locals(),np.transpose((points[st:en],yout[st:en],xout[st:en],zout[st:en])),fmt='%4i,%7.3f,%7.3f,%7.3f')


2010年10月29日金曜日

Google Earthでアニメーション



以前、Google Earthで図を描いたが、今回はアニメーションを作ってみる。図が3枚だけのアニメーションだ。

手順1
Gradsで図を3枚つくる。GradsでGoogle Earth向きの図をつくるやり方は以前の記事を参照のこと。下の例はNCEP/NCARの最解析データを使って、2008年から2010年の8月の表面大気温度のアノマリをプロットしている。colorbarはxcbarを使って図の上に一緒に乗せた。
"reinit"
"sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC//Datasets/ncep.reanalysis.de
rived/surface/air.mon.mean.nc"
"sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC//Datasets/ncep.reanalysis.de
rived/surface/air.mon.ltm.nc"
year=2008
while (year<=2010)
"set time aug"year
"set x 1 144"
"set y 1 73"
"set parea 0 11 0 8.5"
"set grid off"
"set grads off"
"set mproj scaled"
"set mpdraw off"
"set gxout shaded"
"color -5 5 0.5 -kind blue->gray->red"
"d air.1-air.2(t=8)"
"xcbar  4.5 6.5 4 4.2 -edge triangle -fstep 10 "
"printim  airtemp_"year"aug.png x2048 y1024 -t 0"
"c"
year=year+1
endwhile

こんな感じの図ができる。


手順2
基になるkmlファイルをGradsで作成する。やり方は以前の記事と同じだ。
*************KML
"reinit"
"sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC//Datasets/ncep.reanalysis.de
rived/surface/air.mon.mean.nc"
"set time aug2010"
"set x 1 144"
"set y 1 73"
"set kml airtemp"
"set gxout  kml"
"d air"

以下のようなkmlファイルが出来る。
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <GroundOverlay>
    <name>Monthly Mean Air Temperature</name>
    <Icon>
      <href>airtemp.tif</href>
    </Icon>
    <LatLonBox>
      <west>     -1.25</west>
      <east>    358.75</east>
      <south>    -91.25</south>
      <north>     91.25</north>
      <rotation>0.0</rotation>
    </LatLonBox>
  </GroundOverlay>
</kml>

手順3
kmlファイルを以下の様に書き換える。
ポイントは

GroundOverlayから/GraoundOverlayの部分を図の数だけ繰り返す。
・それぞれ図の名前を指定する。
・図がどの時間を占めるべきかTimeSpan /TimeSpanの部分で指定する。なお、今の場合、図は8月だけの図であるが、アニメーションで図が続く様に1年中同じ図が表示されるよう、1月1日から12月31日をTimeSpanで設定している。
・繰り返しの全体をFolder/Folderではさむ。 

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Folder>
  <GroundOverlay>
    <name>Monthly Longterm Mean of Air Temperature</name>
    <TimeSpan>
      <begin>2008-01-01</begin>
      <end>  2008-12-31</end>
    </TimeSpan>
    <Icon>
      <href>airtemp_2008aug.png</href>
    </Icon>
    <LatLonBox>
      <west>     -1.25</west>
      <east>    358.75</east>
      <south>    -91.25</south>
      <north>     91.25</north>
      <rotation>0.0</rotation>
    </LatLonBox>
  </GroundOverlay>

  <GroundOverlay>
    <name>Monthly Longterm Mean of Air Temperature</name>
    <TimeSpan>
      <begin>2009-01-01</begin>
      <end>  2009-12-31</end>
    </TimeSpan>
    <Icon>
      <href>airtemp_2009aug.png</href>
    </Icon>
    <LatLonBox>
      <west>     -1.25</west>
      <east>    358.75</east>
      <south>    -91.25</south>
      <north>     91.25</north>
      <rotation>0.0</rotation>
    </LatLonBox>
  </GroundOverlay>

  <GroundOverlay>
    <name>Monthly Longterm Mean of Air Temperature</name>
    <TimeSpan>
      <begin>2010-01-01</begin>
      <end>  2010-12-31</end>
    </TimeSpan>
    <Icon>
      <href>airtemp_2010aug.png</href>
    </Icon>
    <LatLonBox>
      <west>     -1.25</west>
      <east>    358.75</east>
      <south>    -91.25</south>
      <north>     91.25</north>
      <rotation>0.0</rotation>
    </LatLonBox>
  </GroundOverlay>
</Folder>
</kml>

出来たファイルをkmzにまとめたものをこちらに置いた。kmzファイルはkmlと図をまとめたもので、Google Earthで一度kmlファイルを開いて、「名前をつけて保存」で作成できる。

Google Earthで開くとこんな感じ。


2010年10月25日月曜日

Ubuntu 10.10にアップグレードメモ



Ubuntuを10.10にアップグレードした。
順調にアップグレードしてくれると思ったらあまかった。
再起動後GUIの画面が始まらず、CUIでログインできるだけ。
リカバリーモードさえうまくいかない。
その夜はそれ以上進まず、諦めて寝ることにした。
次の日ネットで調べたら以下の情報を見つけた。

After upgrade to 10.10 no GUI just command line
http://ubuntuforums.org/showthread.php?t=1575262

これに書いてある中で、 fglrxを消すというのが効果があった。
これで、リカバリーモードのSafe Graphics ModeでGUIが出てくるようになった。
あとは再起動やアップグレードをしているうちに、ちゃんと正常に動くようになった。
良かった、良かった。

2010年10月7日木曜日

富士山の初冠雪日(2) 続・トレンド



前回の続きとして、私自身あまりよく理解しているとは言えないが、ココを参考にして、ロバスト推定でも線形回帰をとってみた。

参考:
ロバスト推定(統計数理研究所)
http://www.ism.ac.jp/~fujisawa/research/robust.html
ロバスト推定(朱鷺の社)
http://ibisforest.org/index.php?%E3%83%AD%E3%83%90%E3%82%B9%E3%83%88%E6%8E%A8%E5%AE%9A

Rのパッケージrobustbaseを使うために
install.packages("robustbase")
を行っておく。
以下がプログラム。
import numpy as np
import scikits.timeseries as ts
#module
from read_FirstSnowFuji import *

#for R
import rpy2.robjects as robjects

ayear,amonth,aday= read_FirstSnowFuji()
days=np.array([],dtype="i4")
for  n in range(ayear.size):
    td=ts.Date("D",year=ayear[n],month=amonth[n],day=aday[n])
    days=np.append(days,td.day_of_year)


#for R
ryear=robjects.FloatVector(ayear)
rdays=robjects.FloatVector(days)
robjects.globalEnv["ryear"] = ryear
robjects.globalEnv["rdays"] = rdays

r=robjects.r

scripts="""
library("robustbase")   #import robustbase
png('FujiSnow_trend_robust.png')
plot(ryear,rdays,xlab="Year",ylab="Day of year") #scatter plot 
fm<-lmrob(rdays ~ ryear)  # robust linear model
abline(fm)  #plot regression line
abline(lm(rdays ~ ryear),lty=2) # usual linear model
dev.off()
"""
r(scripts)

# summary
summary=r('sm<-summary(fm)')
print summary


以下が図。点線が前回の回帰直線、実線がrobustbaseで得られた直線である。


出力(print summary)は


Call:
lmrob(formula = rdays ~ ryear)

Weighted Residuals:
    Min      1Q  Median      3Q     Max
-57.989  -7.872  -0.125   5.986  25.291

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept) 76.10515   51.85081   1.468 0.144896   
ryear        0.10154    0.02673   3.798 0.000234 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Robust residual standard error: 10.03
Convergence in 12 IRWLS iterations

Robustness weights:
 2 observations c(21,115) are outliers with |weight| <= 0.00057 ( < 0.00085);
 13 weights are ~= 1. The remaining 102 ones are summarized as
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
0.00365 0.88600 0.94480 0.88410 0.98680 0.99880
Algorithmic parameters:
tuning.chi         bb tuning.psi refine.tol    rel.tol
 1.5476400  0.5000000  4.6850610  0.0000001  0.0000001
 nResample     max.it     groups    n.group   best.r.s   k.fast.s      k.max
       500         50          5        400          2          1        200
 trace.lev compute.rd
         0          0
seed : int(0) 

これによると回帰係数は0.10154(100年で約10日冠雪日が遅くなる)で、しかもp値が非常に小さく統計的に有意である。
ただし、c(21,115)が外れ値、すなわち1914年(ayear[21-1]=1914)と2008年(ayear[115-1]=2008)が外れ値になるのは良いとして、weightが1に近い点は13だけで、あとの外れ値を除く102点はweightが1以下に落とされている。

これを見るために、回帰直線の係数、weightなどの数値をpythonに戻し、プロットしてみる(同じことをRでもできるはずだが、pythonのmatplotlibのほうがなれているので)。
以下プログラム。
robust推定で得られた回帰直線を緑線で、
weightが1に近い点を青○で、
weightが0.5から0.9988の間にある点を黒点、
weightが0.00085から0.5の間にある点を赤点、
外れ値を赤xでプロットした。
#weights
weights=r('weights<-sm$weights')
weight=np.array(weights)

#plot
import matplotlib.pyplot as plt
plt.plot(ayear,ayear*a+b,'g-')
range=(weight>0.9988)
plt.plot(ayear[range],days[range],"bo")
range=np.logical_and(weight<=0.9988,weight>=0.5)
plt.plot(ayear[range],days[range],"k.")
range=np.logical_and(weight<0.5,weight>=0.00085)
plt.plot(ayear[range],days[range],"r.")
range=(weight<0.00085)
plt.plot(ayear[range],days[range],"rx")
plt.xlim(ayear[0],ayear[-1]) 
plt.xlabel("year")
plt.ylabel("day of year")
plt.savefig("FujiSnow_trend_robust_2.png")
plt.show()
ちなみに今年2010年の重みはweight[-1]=0.86999219949571671
このように多数の点の重みを落とすのは統計的に良くても、物理的な理由は自明ではない。
今回、この方法を用いたのは適当ではなかったかもしれない。

前回と今回の解析で、初冠雪日にトレンドがあるかは統計的にははっきりしなかったが、初冠雪日が遅くなっていっているかもしれないという作業仮説自体は興味深いものがある。これから、将来、どのような傾向があらわれてくるだろうか?

参考
異常気象を追う 初冠雪
http://www.bioweather.net/column/essay2/aw15.htm

縮小する富士山の永久凍土
http://www.fujisan-net.jp/data/article/1583.html

2010年10月6日水曜日

富士山の初冠雪日(2) トレンド



前回作ったモジュール(read_FirstSnowFuji)で初冠雪日を読み、年初からの日にち(1月1日を1とする)に変換したうえで、線形回帰を当てはめてみる。
以前やったように、rpy2を用いて、Rにデータを渡してやる。

import numpy as np
import matplotlib.pyplot as plt
import scikits.timeseries as ts
#module
from read_FirstSnowFuji import *

#for R
import rpy2.robjects as robjects

ayear,amonth,aday= read_FirstSnowFuji()
days=np.array([],dtype="i4")
for  n in range(ayear.size):
    td=ts.Date("D",year=ayear[n],month=amonth[n],day=aday[n])
    days=np.append(days,td.day_of_year)


#for R
ryear=robjects.FloatVector(ayear)
rdays=robjects.FloatVector(days)
robjects.globalEnv["ryear"] = ryear
robjects.globalEnv["rdays"] = rdays
r=robjects.r

scripts="""
png('FujiSnow_trend.png')
plot(ryear,rdays,xlab="Year",ylab="Day of year") # scatter plot
fm<-lm(rdays ~ ryear) # linear model
abline(fm) # plot regression line                 
dev.off()             
"""
r(scripts)
summary=r('sm<-summary(fm)')
print summary
出力(print summary)は
Call:
lm(formula = rdays ~ ryear)

Residuals:
     Min       1Q   Median       3Q      Max
-54.2679  -5.8555   0.9491   8.1001  27.3063

Coefficients:
             Estimate Std. Error t value Pr(>|t|) 
(Intercept) 138.25153   71.89542   1.923   0.0570 .
ryear         0.06873    0.03683   1.866   0.0645 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 13.45 on 115 degrees of freedom
Multiple R-squared: 0.0294,    Adjusted R-squared: 0.02096
F-statistic: 3.484 on 1 and 115 DF,  p-value: 0.06453

係数が0.06873/yearだから、100年に一週間ほどのトレンド(初冠雪日が遅くなる)となる。ただし、p値が約0.0645であるから危険率5%でトレンドがない可能性を否定できない。データのばらつきが大きいことから、さもありなんといったところである。

2010年10月1日金曜日

富士山の初冠雪日(1) データ取得



富士山 一気に冬の顔
初冠雪、平年より6日早く
山頂から8合目付近にかけてうっすらと雪をかぶった富士山=富士吉田市上吉田 甲府地方気象台は25日、富士山が初冠雪したと発表した。平年より6日、昨年より12日早い。独自に観測している富士吉田市も同日、昨年より15日早く「富士山初雪化粧」を宣言した。山梨県内は22日に甲府など3地点で最高気温が35度以上の猛暑日を記録したばかり。富士山は早くも冬に向けての“衣替え”が始まった。
2010年09月26日 山梨日日新聞 web魚拓

甲府気象台のwebページ表から、富士山初冠雪日を取得するプログラムを作る。
以下がプログラム。
関数 read_FirstSnowFuji()が初冠雪日を取得するモジュールで、年、月、日をnumpyの配列として返す。HTMLから表を読むのにtable parserを用いている。
メインプログラムでは、日付をscikits.timeseriesで年初からの日にち(1月1日を1とする)に変換し、グラフにしている。平均値を黒い実線で、平均値から標準偏差(約14日)の2倍離れた値に黒点線を引いた。ばらつきがかなり大きい印象だ。今年は少しだけ平年より早い。

read_FirstSnowFuji.py
import numpy as np
# table parser from http://simbot.wordpress.com/2006/05/17/html-table-parser-using-python/
from table_parser import *

def read_FirstSnowFuji():
    import urllib
    """
   read first snow cover day of the FUJI mountain from KOFU local meteorological observatory
   usage:
      year,month,day=read_FirstSnowFuji()
   input
      none
   output
      year,month,day:  first snow cover day (numpy array)
   
   """

    f = urllib.urlopen('http://www.jma-net.go.jp/kofu/menu/siryo_hatsukansetsu.dat.html')
    p = TableParser()
    p.feed(f.read())
    f.close()
    data=p.doc # Get to the data

    #get 
    year=[[],[],[]] # 3 column
    month=[[],[],[]] # 3 column
    day=[[],[],[]] # 3 column
    for d in data[0][1:]:
        for n,c in enumerate([0,4,8]):
            if not d[c]=="":
                year[n].append(int(d[c]))
                mi,di=d[c+1].split("/")
                month[n].append(int(mi))
                day[n].append(int(di))
    # to numpy array
    ayear=np.array([],dtype="i4")
    amonth=np.array([],dtype="i4")
    aday=np.array([],dtype="i4")
    for n in range(3):
        ayear=np.append(ayear,year[n])
        amonth=np.append(amonth,month[n])
        aday=np.append(aday,day[n])

    return ayear,amonth,aday

############################################
if __name__ == '__main__':
    import matplotlib.pyplot as plt
    import scikits.timeseries as ts

    ayear,amonth,aday= read_FirstSnowFuji()

    days=np.array([],dtype="i4")
    for  n in range(ayear.size):
        td=ts.Date("D",year=ayear[n],month=amonth[n],day=aday[n])
        days=np.append(days,td.day_of_year)

    #plot
    plt.plot(ayear,days,"b-")
    mean=days.mean()
    stdp=days.mean()+2*days.std()
    stdm=days.mean()-2*days.std()
    plt.plot([ayear[0],ayear[-1]],[mean,mean],"k-")
    plt.plot([ayear[0],ayear[-1]],[stdp,stdp],"k--")
    plt.plot([ayear[0],ayear[-1]],[stdm,stdm],"k--")
    plt.xlim(ayear[0],ayear[-1])
    plt.xlabel("year")
    plt.ylabel("day of year")
    plt.savefig("FujiFirstSnow.png")
    plt.show()

2010年9月27日月曜日

今年8月の500hpa高度場



異常高温の夏、今後増加? 
エルニーニョや偏西風蛇行で
1898年の統計開始後最高だった今夏の高温の原因について、気象庁の「異常気象分析検討会」(会長、木本昌秀東大大気海洋研究所教授)が3日、見解をまとめた。春まで継続したエルニーニョと、夏に発生したラニーニャ両現象の相乗効果や、日本上空付近を吹く偏西風の蛇行などの複合要因を指摘した。(2010/09/03 47News) web魚拓

偏西風の位置の指標としての500hpa高度場(geopotential height)をGoogle Earthでプロットしてみた。
GradsにはGoogle Earth用のデータを作るオプションがある(version 2.0a5以降)
http://www.iges.org/grads/gadoc/gradcomdsetkml.html
それを用いて、NCEP NCAR/Reanalysisの今年8月のデータから画像を作るGradsスクリプトが以下の通り。

*************KML
"sdfopen http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/ncep.reanalysis.derived/pressure/hgt.mon.mean.nc"
"set lev 500"
"set time aug2010"
"set x 1 144"
"set y 1 73"
"set kml hgt2010aug"
"set gxout kml"
"d hgt"
************FIGURE
"c"
"set parea 0 11 0 8.5"
"set grid off"
"set grads off"
"set mproj scaled"
"set mpdraw off"
"set gxout shaded"
"color 5400 5900 20 -kind blue->skyblue->yellow->red->brown"
"d hgt"
"printim  hgt2010aug.png x2048 y1024 -t 0"

前半で、
hgt2010aug.kml
hgt2010aug.tif
という二つのファイルが出来る。

本来なら、これだけで十分なはずだが、これだと図がいまいちなので、
http://gradsusr.org/pipermail/gradsusr/2010-January/010117.html
を参考に後半で図だけを好きなように作り直す。
その後、hgt2010aug.kmlをエディタで開いて、hgt2010aug.tifで指定しているところをhgt2010aug.pngに書き換える。

後は、hgt2010aug.kmlをGoogle Earthで開けばよい。

ちなみに、”set x 1 144”としているが、本来このデータはサイクリック用のグリッドが145まで用意されているが、そのままだとうまく端つながらないので、144までにしている。

また、データはOPeNDAPを使いネットから直接読み、シェイドの色をcolorスクリプト
http://kodama.fubuki.info/wiki/wiki.cgi/GrADS/script/color.gs?lang=jp
で指定した。

できたデータをブログに貼るために、
Embed your Google Earth Project in your Website
http://earth.google.com/outreach/tutorial_kmlembed.html
を利用した。
データは一旦Google Earthで開いたファイルをkmz方式で保存し、こちらにおいた。


(注.firefoxでは見れないことがあるようだ。その場合は他のブラウザでためしてみてください。)


2010年9月25日土曜日

TRMM 3B42 降水データ

人工衛星TRMMの降水データ3B42をプロットする。
この降水データは水平分解能0.25度で3時間ごとにデータがある。
このデータをNASA MiradorからOPeNDAPを通して入手する。
以下の例は7月27日0時世界標準時(日本標準時9時)の日本付近のデータをプロットする例である。この日は東シナ海に熱帯低気圧が存在し、それによる雨が見える。
OPeNDAPからはPyNIOを使ってデータを入手する。
関数name_makerはファイルのあるURLはこういう規則だろうと推測して作ったものである。間違いかもしれないので確認すること。

import Nio
import numpy as np

def read_TRMM_3B42(date,lon1,lon2,lat1,lat2):
    """
   read TRMM 3B42 3 hourly rainfall  from NASA Mirador

   usage:
      lon,lat,sst=read__TRMM_3B42(date,lon1,lon2,lat1,lat2)
     
   input
     
      date (year,month,day,hour) 
      lon1,lon2  Western,East bounday
      lat1,lat2  South,West bounday
   output
      lon longitude
      lat latitude
      rainfall data (mm/hour)         
    """
#check
    assert date[3] % 3 ==0, "Error, hour must be multiple of 3!!!"
# open OPENDAP
    fname=name_maker(date[0],date[1],date[2],date[3])
    f = Nio.open_file(fname)
## variable name check
    #variables = f.variables.keys()
    #print f
    #print "variables",variables

# read data
    temp=f.variables["DATA_GRANULE.PlanetaryGrid.precipitation"]

#grid
    # grid
    lon=-179.875+np.arange(temp.shape[1])*0.25
    lat=-49.875 +np.arange(temp.shape[2])*0.25
    # selection
    xrange=np.where(np.logical_and(lon>=lon1,lon<=lon2))
    yrange=np.where(np.logical_and(lat>=lat1,lat<=lat2))
    
#precipitation
    x=xrange[0]
    y=yrange[0]
    precip=(temp[0,x[0]:x[-1]+1,y[0]:y[-1]+1].T).reshape((len(y),len(x)))

# closing 
    f.close()
    return lon[xrange],lat[yrange],precip

############################################

def name_maker(year,month,day,hour):
    import scikits.timeseries as ts
    #
    t=ts.Date('H', year=year,month=month,day=day,hour=hour)
    if t.hour==0:
        tc=t-1
    else:
        tc=t
    cyear = "%04d"  % tc.year
    cyear2= "%02d"  % (t.year-2000)
    cmonth2= "%02d" % t.month
    cday2=   "%02d" % t.day
    shour = str(t.hour)
    yd3= "%03d" % tc.day_of_year
    #
    fname="http://disc2.nascom.nasa.gov/opendap/TRMM_L3//TRMM_3B42//%(cyear)s/%(yd3)s/3B42.%(cyear2)s%(cmonth2)s%(cday2)s.%(shour)s.6A.HDF.Z" %locals()
    return fname

############################################
if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from mpl_toolkits.basemap import Basemap
    # example plot

# range
    lon1=120.
    lon2=145.
    lat1=20.
    lat2=40.

#date
    year=2009
    month=7
    day=27
    hour=0 
    lon,lat,precip=read_TRMM_3B42(date=(year,month,day,hour),lon1=lon1,lon2=lon2,lat1=lat1,lat2=lat2)

   #plot
    vmax=int(precip.max())
    vmin=int(precip.min())
    fig=plt.figure()
    ax = fig.add_axes([0.1,0.1,0.8,0.8])
    map = Basemap(projection='merc',llcrnrlat=lat1,urcrnrlat=lat2,\
                      llcrnrlon=lon1,urcrnrlon=lon2,resolution='i')
    lons,lats=np.meshgrid(lon[:],lat[:])
    x,y=map(lons,lats)
    map.drawcoastlines()
    map.drawmapboundary()
    map.pcolor(x,y,precip,vmin=vmin,vmax=vmax,cmap=plt.cm.Blues)
    ti=str(year)+"/"+str(month)+"/"+str(day)+":"+str(hour)+"Z"
    plt.title(ti)
# colorbar
    pos = ax.get_position()
    l, b, w, h = pos.bounds
    cax = plt.axes([l+w+0.02, b, 0.03, h]) # setup colorbar axes.
    plt.colorbar(cax=cax) # draw colorbar
    plt.savefig("TRMM_3B42.png")
    plt.show()


参考
NASA Giovanniのサイト
http://disc2.nascom.nasa.gov/Giovanni/tovas/TRMM_V6.3B42.2.shtml

TRMM 3B42 readme
ftp://meso-a.gsfc.nasa.gov/pub/trmmdocs/3B42_3B43_doc.pdf

2010年9月17日金曜日

Sageで方程式を解く



Sageで方程式を解いてみる。
x,a,b,c=var('x a b c')
solve(x^2+1==0,x)

f=a*x^2+b*x+c==0
solve(f,x)


Q=solve(f,x)
for q in Q:
    q.substitute({a:1,b:-5,c:6})



Latexの書式を出すときには
latex(_)
などとする。

2010年9月4日土曜日

方程式の解を指定範囲で全て探すプログラム



以下のプログラムは、方程式を関数f(x)=0で与えたときに、指定した範囲内で解を全て求めることを意図したものである。
基本は、範囲をdivの数で分割し、それぞれの領域でScipyのbrentqを適用し、解を集めている。
しかし、関数f(x)が解の前後で符号を変えないケースがあり、このような場合の解を求めるためにNewton法も用いている。

f1d_solve_range.py
import numpy as np

def f1d_solve_range(f,range,div=1000,args=(),newtontirigger=1.E-4):
    """
Solve f(x)=0 to otain all solutions between range
range is devided by div to find solution in each subrange

When |f(x)| < 1.E-4, Newton Method is also used
because brentq can not find solution if f(x) does not change sign at a solution.
"""
    from scipy.optimize import brentq,newton

    x=np.linspace(range[0],range[1],div)
    dx=x[1]-x[0] 

    #solution 1 by bysection
    solution1=[]
    for x0  in x[:-1]:
        try:
            a=brentq(f,x0,x0+dx,args=args)
            solution1.append(a)
        except:
            continue
    solution1=np.unique(solution1)

    # solution2 by Netwon method just in case f(a) f(b) has same sign
    solution2=[] 
    for x0 in x:
        if np.abs(f(x0,*args))<newtontirigger:
            b=newton(f,x0,args=args)
            solution2.append(b)
    solution2=np.unique(solution2)
    solution2.clip(range[0],range[1])  # remove out of range

    # final solution
    solution=solution1
    for s2 in solution2:
        if np.alltrue(np.abs(solution-s2)>dx):
            print "New solution added by Newtonian!:",s2
            solution=np.append(solution,s2)
    
    return np.sort(solution)  

例1 
#example 1
    def f(x):
        return x**3 
    
    print "ex1=",f1d_solve_range(f,(-10.,10))
結果:
ex1= [ 0.]

例2 
#example 2
    def f(x):
        return x**3-x 
    print "ex2=",f1d_solve_range(f,(-10.,10)) 
結果:
ex2= [-1.  0.  1.]


例3
#example 3
    def f(x):
        return x**3-x**2 
    print "ex3=",f1d_solve_range(f,(-10.,10)) 
    x=np.linspace(-2,2,100)
結果:
ex3= New solution added by Newtonian!: 1.92717530048e-08
[  1.92717530e-08   1.00000000e+00]
解の前後で符号を変えないケースでNewton法で解が求まっている。

のグラフを参照

例4
#example 4
    def f(x):
        return x**2+1 
    print "ex4=",f1d_solve_range(f,(-10.,10)) 
結果:
ex4= []
解なし。

例5
#example 5
    def f(x,a,b,c):
        return a*x**2 + b*x +c 
    print "ex5=",f1d_solve_range(f,(-10.,10),args=(1,2,1)) 

結果:
ex5= New solution added by Newtonian!: -0.999999982865
[-0.99999998]

EORC MODIS SST



JAXA EORC提供のMODIS SSTをプロットするスクリプトを作成した。
データのバイナリはEORCに提供していただいた。
下の例ではは2010年8月2日の領域6のデータから紀伊半島付近をプロットしている。 A2GL11008020431OD1_OSTAQ_01000_01000_sst_06
ファイルのフォーマットの情報はここ


import numpy as np

def read_MODIS_EORC(file,lon1,lon2,lat1,lat2):
    """
   read MODIS_SST from EORC

   usage:
      lon,lat,sst=read_MODIS_EORC(file,lon1,lon2,lat1,lat2)
     
   input
     
      file filename 
      lon1,lon2  Western,East bounday
      lat1,lat2  South,West bounday
   output
      lon longitude
      lat latitude
      SST sst data (masked array) in C          
    """
    # open file
    f=open(file, 'r')
    # read header
    pixel=f.read(6); print "pixel=",pixel
    line=f.read(6); print "line=",line
    nwlon=f.read(8); print "NW lon.=",nwlon
    nwlat=f.read(8); print "NW lat.=",nwlat
    res=f.read(8);   print "resolution=",res
    slope=f.read(9);   print "slope=",slope
    offset=f.read(9);  print "ofset=",offset
    dummy=f.read(1)
    parameter=f.read(8); print "parameter name=",parameter
    dummy=f.read(1)
    filename=f.read(45); print "file name=",filename
    f.close()
    # read data
    f=open(file,'r')
    f.seek(int(pixel)*2) #skip header
    data=np.fromfile(f,dtype=">u2")
    data=data.reshape((int(line),int(pixel)))
    msk=np.logical_or(data==0,data>=65534) # 0 cloud, 65534 out of obs., 65535 land 
    data=np.ma.masked_array(data[::-1],mask=msk[::-1,:])
    data=data*float(slope)+float(offset)-273.15 # to degree C
    f.close()
    # grid
    lon=float(nwlon)+np.arange(int(pixel))*float(res)
    lat=float(nwlat)-np.arange(int(line))*float(res)
    lat=lat[::-1]
    # selection
    xrange=np.logical_and(lon>=lon1,lon<=lon2)
    yrange=np.logical_and(lat>=lat1,lat<=lat2)
    sst=(data[yrange,:])[:,xrange] 

    return lon[xrange],lat[yrange], sst

############################################
if __name__ == '__main__':
    import matplotlib.pyplot as plt
    from mpl_toolkits.basemap import Basemap
    # example plot

    #read data
    filename="A2GL11008020431OD1_OSTAQ_01000_01000_sst_06"
    lon1=135.
    lon2=138.
    lat1=32.
    lat2=35.
    lon,lat,sst=read_MODIS_EORC(filename,lon1,lon2,lat1,lat2)

    #plot
    tmax=int(sst.max())
    tmin=int(sst.min())
    fig=plt.figure()
    ax = fig.add_axes([0.1,0.1,0.8,0.8])
    map = Basemap(projection='merc',llcrnrlat=lat1,urcrnrlat=lat2,\
                      llcrnrlon=lon1,urcrnrlon=lon2,resolution='i')
    lons,lats=np.meshgrid(lon[:],lat[:])
    x,y=map(lons,lats)
    map.drawcoastlines()
    map.drawmapboundary()
    map.pcolor(x,y,sst,vmin=tmin,vmax=tmax)
# colorbar
    pos = ax.get_position()
    l, b, w, h = pos.bounds
    cax = plt.axes([l+w+0.02, b, 0.03, h]) # setup colorbar axes.
    plt.colorbar(cax=cax) # draw colorbar
    plt.savefig("MODIS_EORC_SST.png")
    plt.show()



謝辞
ここで用いたSSTバイナリは宇宙航空研究開発機構(JAXA)地球観測研究センター(EORC)から提供されたものである。ここに感謝する。

関連ポスト
MODIS aqua Global Level 3 Mapped Mid-IR SST (4km)
http://oceansciencehack.blogspot.com/2010/09/modis-aqua-global-level-3-mapped-mid-ir.html

参考
MODIS EORC SST FAQ
http://kuroshio.eorc.jaxa.jp/ADEOS/mod_nrt_new/html/05_faq.html
JAXA MODISページ
http://www.eorc.jaxa.jp/hatoyama/satellite/sendata/modis_j.html
MODIS wikipedia
http://ja.wikipedia.org/wiki/MODIS

2010年9月3日金曜日

CEReS NOAA/AVHRR SST



CEReSで配布しているNOAA AVHRR SSTをプロットしてみた。

FTPサイトからデータをダウンロードして、解凍する。
ここでは n1810080316.sst.giを利用する。
(NOAA 18 ,2010年,8月3日,16世界標準時)
以下がスクリプト。
このデータには雲や陸地のマスクはないようだ。
前回はPyNGLでプロットしたが、今回はmatplotlib basemap toolkitを利用した。
matlabのpplot関数に対応する関数を使いたかったからだ(PyNGLにはあるのかな?)。

import numpy as np
import matplotlib.pyplot as plt

def read_CEReS_SST(dir,file,lon1,lon2,lat1,lat2):
    """
   read CERes SST
   usage:
      lon,lat,sst=read_CEReS_SST(dir,file,lon1,lon2,lat1,lat2)     
   input
      dir working dir
      file filename 
           gzipped file is fine.
           URL also works
      lon1,lon2  Western,East bounday
      lat1,lat2  South,West bounday
   output
      lon longitude
      lat latitude
      SST sst data (masked array) in C          
    """
    # read data
    ds=np.DataSource(dir)
    f=ds.open(file, 'r')
    f.seek(80) # skip header
    ssttemp=np.frombuffer(f.read(),dtype=">i2",count=6378*5562)
    f.close()
    # as masked array
    sst=ssttemp.reshape((5562,6378))*0.1
    sst=np.ma.masked_array(sst[::-1,:],mask=(sst[::-1,:]==0  ))

    # grid
    lon=100.+np.arange(6378)*0.01097869
    lat=9.97971+np.arange(5562)*0.00899322
    # selection
    xrange=np.logical_and(lon>=lon1,lon<=lon2)
    yrange=np.logical_and(lat>=lat1,lat<=lat2)
    sst=(sst[yrange,:])[:,xrange]-273.15 # Kelvin to C

    return lon[xrange],lat[yrange], sst
############################################
if __name__ == '__main__':
    from mpl_toolkits.basemap import Basemap
    # example plot

    #read data
    filename="n1810080316.sst.gi"
    lon1=130.
    lon2=140.
    lat1=30.
    lat2=36.
    lon,lat,sst=read_CEReS_SST(".",filename,lon1,lon2,lat1,lat2)

    #plot
    tmax=30.
    tmin=20.
    fig=plt.figure()
    ax = fig.add_axes([0.1,0.1,0.8,0.8])
    map = Basemap(projection='merc',llcrnrlat=lat1,urcrnrlat=lat2,\
                      llcrnrlon=lon1,urcrnrlon=lon2,resolution='i')
    lons,lats=np.meshgrid(lon[:],lat[:])
    x,y=map(lons,lats)
    map.drawcoastlines()
    map.drawmapboundary()
    map.pcolor(x,y,sst,vmin=tmin,vmax=tmax)
 # colorbar
    pos = ax.get_position()
    l, b, w, h = pos.bounds
    cax = plt.axes([l+w+0.02, b, 0.03, h]) # setup colorbar axes.
    plt.colorbar(cax=cax) # draw colorbar
    plt.savefig("CEReS_SST.png")
    plt.show()


2010年9月1日水曜日

MODIS aqua Global Level 3 Mapped Mid-IR SST (4km)



MODIS aqua Global Level 3 Mapped Mid-IR 海水面温度PyNIOで読み込み、PyNGLでプロットした(daily, 4km)。


データはNASA PO.DAACのサイトFTPサイトより、8月3日のデータを入手し(A2010215.L3m_DAY_SST4_4.bz2)、bunzip2で解凍しておく。
データはhdf4フォーマットである。

以下がスクリプト。
SSTは変数名"l3m_data"でunsigned 16bit integer
クオリティフラッグが変数名"l3m__qual"でunsigned 8bit integer
これらはHDFVIEW で確かめた。
クオリティフラッグが0以外は欠損値とした。
データは、南北が逆になっているので反転してある。

import Nio
import Ngl
import numpy as np
import scikits.timeseries as ts

#day
year=2010
month=8
day=3
t=ts.Date("D",year=year,month=month,day=day)
days='%03d' % t.day_of_year
td=str(year)+"/"+str(month)+"/"+str(day)


# open file
nfile=Nio.open_file("A%(year)s%(days)s.L3m_DAY_SST4_4" %locals(), mode='r',format="h4")
nfile.set_option("MaskedArrayMode","MaskedNever")

#grid
lon=nfile.Westernmost_Longitude+nfile.Longitude_Step[0]*np.arange(nfile.Number_of_Columns)
lat=nfile.Southernmost_Latitude+nfile.Latitude_Step[0]*np.arange(nfile.Number_of_Lines)

#grid salection
xrange=np.logical_and(lon>=130,lon<=140.)
yrange=np.logical_and(lat>=30,lat<=36.)

temp=nfile.variables["l3m_data"]
flag=nfile.variables["l3m_qual"]

temp2=((temp[::-1,:])[yrange,:])[:,xrange].astype("uint16")
flag2=((flag[::-1,:])[yrange,:])[:,xrange].astype("uint8")

sst=temp2*temp.Slope[0]+temp.Intercept[0]
sst=np.ma.masked_array(sst,mask=flag2>0)
nfile.close()

# plot by PyNGL
#  Open a workstation.
#
wks_type = "png"
wks = Ngl.open_wks(wks_type,"MODIS")

resources = Ngl.Resources()

resources.tiXAxisString = "~F25~longitude"
resources.tiYAxisString = "~F25~latitude"

resources.cnFillOn              = True     # Turn on contour fill.
resources.cnLineLabelsOn        = False    # Turn off line labels.
resources.cnInfoLabelOn         = False    # Turn off info label.
resources.mpGridAndLimbOn             = False

resources.sfXCStartV = float(min(lon[xrange]))   # Define where contour plot
resources.sfXCEndV   = float(max(lon[xrange]))   # should lie on the map plot.
resources.sfYCStartV = float(min(lat[yrange]))
resources.sfYCEndV   = float(max(lat[yrange]))

resources.mpLimitMode = "LatLon"    # Limit the map view.
resources.mpMinLonF   = float(min(lon[xrange]))
resources.mpMaxLonF   = float(max(lon[xrange]))
resources.mpMinLatF   = float(min(lat[yrange]))
resources.mpMaxLatF   = float(max(lat[yrange]))
resources.mpDataBaseVersion = "MediumRes"
resources.tiMainString = "MODIS aqua Global Level 3 Mapped Mid-IR SST day=%(td)s"  %locals()# Set a title.
resources.tiMainFontHeightF=0.015
resources.cnLevelSelectionMode = "ExplicitLevels" # Define own levels.
resources.cnLevels             = np.arange(20.,30.,0.5)

#
# draw contours over map.
#
map = Ngl.contour_map(wks,sst,resources)

Ngl.end()
 MODISにはAquaとTerra、それぞれに海面水温データとしてはMid-IR SSTとthermal IR SSTがある。どのように使いわけたらいいのだろうか?

参照
JAXA MODISページ
http://www.eorc.jaxa.jp/hatoyama/satellite/sendata/modis_j.html
EORC
http://kuroshio.eorc.jaxa.jp/ADEOS/mod_nrt_new/index.html
MODIS wikipedia
http://ja.wikipedia.org/wiki/MODIS

2010年8月31日火曜日

Scipyで常微分方程式を解く (2) 最適化



前回の応用として、

を解き、


を満たすようなを求める。
真の解は

より


以下がスクリプト。

[1] a が与えられた時に、(x,y)=(0,1)から常微分方程式を積分し、x=x1でのyの値を返す関数y_at_x(a,x1)を定義。
[2] y_at_x(a=1,x1=3)を真の解と比較。
[3] F(a,x1,y1)=y_at_x(a,x1)-y1 という関数を定義。 
[4] F(a,x1=3,y1=4)=0 を解いてaを求める。aはscipyのbrentq関数を使って0から1の範囲で探す。最後に真の解を使って答え合わせ。

import numpy as np
from scipy import integrate
from scipy import optimize 

#solve dy/dx=a y
# x=0, y=1  
# x=3, y=4
# what is a?

#define derivative
def dy_dx(y,x,a):
    return a*y

#[1]
def y_at_x(a,x1):
    y1, infodict = integrate.odeint(dy_dx, 1., [0,x1],args=(a,),full_output=True)
    #print infodict["message"]
    return y1


# [2] test y_at_x(a,x1)
y_test= y_at_x(a=1.,x1=3.)
print "test y_at_ax(1,3)",y_test[1],np.exp(1.*3.)

# [3] define y_at_x(a,x1)-y1
def F(a,x1,y1):
    return y_at_x(a,x1)[1][0]-y1


# [4] Solve  y_at_x(a,3)-4=0
asolution=optimize.brentq(F,0.,1.,args=(3.,4.))
print "solution of a=",asolution
print "true solution", np.log(4)/3.
print "test solution1",y_at_x(a=asolution,x1=3.)[1][0], np.exp(asolution*3.)

出力は
test y_at_ax(1,3) [ 20.08553873] 20.0855369232
solution of a= 0.462098103279
true solution 0.462098120373
test solution1 4.0 3.99999979487

2010年8月30日月曜日

Scipyで常微分方程式を解く



Scipyodeint関数を使い、常微分方程式を解いてみる。

簡単な例として、

を、初期値

で解く。
真の解は

である。

以下がpythonスクリプト。xの0から3まで積分している。数値計算で得られた値を+で、真の解より得られる値を実線で図示している。

import numpy as np
from scipy import integrate
import matplotlib.pyplot as plt 

#solve dy/dx=y

#define derivative
def dy_dx(y,x):
    return y

#integration
x1=np.linspace(0,3, 10)
x=np.linspace(0,3, 100)
y1, infodict = integrate.odeint(dy_dx, 1., x1, full_output=True)
print infodict

# plot
plt.plot(x1,y1,'+',markersize=12)  #solusion 
plt.plot(x,np.exp(x))            #true solution 
plt.savefig("ode_ex.png")
plt.show()


参照:
LoktaVolterraTutorial
http://www.scipy.org/LoktaVolterraTutorial 
A Coupled Spring-Mass System
http://www.scipy.org/Cookbook/CoupledSpringMassSystem

bloggerで数式を書くテスト



こちらの記事を参照した。
LaTeX for BloggerをSafariと新しい編集インターフェイスのために改造する。
http://satomacoto.blogspot.com/2009/11/latex-for-bloggersafariblogger.html 

以下のような数式が書ける。

2010年8月29日日曜日

PyNGLとPyNIOをインストール



PyNGL(1.31)とPyNIO(1.4.0)をUbuntu10.04にインストールした。

PyNGLは NCAR Command Language (NCL)をもとにしたpythonベースのグラフィックモジュールで、PyNIOはいろいろな形式のファイルを読み込むモジュールである。

以下がインストール手順。

(1) プレコンパイル済みのファイルを入手
PyNGL-1.3.1.linux-debian-i686-gcc432-py255-numpy130.tar.gz と PyNIO-1.4.0.linux-debian-i686-gcc432-py255-numpy130.tar.gz をEarth System Gridより入手。
Pythonのバージョンは2.6.5を使っているが、numpyは1.3.0を使っているので、プレコンパイル済みのバイナリの中からこれらを選択した。

(2) 以下を実行する。
sudo tar -C /usr/local -xzf PyNIO-1.4.0.linux-debian-i686-gcc432-py255-numpy130.tar.gz
sudo tar -C /usr/local -xzf PyNGL-1.3.1.linux-debian-i686-gcc432-py255-numpy130.tar.gz 

(3) パスを通す。
export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python2.5/site-packages:/usr/local/lib/python2.5/si
te-packages/PyNIO:/usr/local/lib/python2.5/site-packages/PyNGL
export PYNGL_NCARG=/usr/local/lib/python2.5/site-packages/PyNGL/ncarg


(4) pynglexの書き換え
/usr/local/bin/pynglexの一行目を
#!/usr/bin/env python 

のように編集する。

2010年5月27日木曜日

東北大学 外洋域新世代海面水温



東北大学外洋域新世代海面水温(http://www.ocean.caos.tohoku.ac.jp/~merge/sstbinary/actvalbm.cgi)(分解能1/20度)を読むモジュールである。
ファイル名としてはgzipされたファイルを指定でき、解凍する必要はない。
(ftpのURLも直接指定できるがネットワークの負荷を考えること。)
メインプログラムとして走らせた時は領域一部をmatplotlibでプロットする。

readNGSST.py
import numpy as np
import matplotlib.pyplot as plt

def readNGSST(dir,file):
    """
   read Tohoku univ. New Generation Sea Surface Temp.

   usage:
      lon,lat,sst=readNGSST(dir,file)
     
   input
      dir working dir
      file filename 
           gzipped file is fine.
           URL also works
   output
      lon longitude
      lat latitude
      SST sst data (masked array)          
    """
    # read data
    ds=np.DataSource(dir)
    f=ds.open(file, 'r')
    f.seek(200) # skip header
    sst=np.frombuffer(f.read(),dtype="u1")
    f.close()

    # as masked array
    sst=np.ma.masked_array(sst,mask=np.logical_or(sst==255, sst==0))
    sst=sst.reshape(1000,1000)*0.15-3.

    # grid
    lon=116.+np.arange(1000)*0.05
    lat=13.+np.arange(1000)*0.05

    return lon,lat, sst

############################################
if __name__ == '__main__':

    # example plot

    #read data
    filename="msst2009010112.raw.gz"
    lon,lat,sst=readNGSST(dir=".",file=filename)

    #grid salection
    xrange=np.logical_and(lon>=130,lon<=145.)
    yrange=np.logical_and(lat>=30,lat<=45.)
    # plot
    plt.figure()
    plt.pcolor(lon[xrange],lat[yrange],(sst[yrange,:])[:,xrange]) 
    plt.savefig("ngsst.png")
    plt.show() 

参考
書籍
Matplotlib for Python Developers: Build Remarkable Publication Quality Plots the Easy Way

2010年5月26日水曜日

Rでwavelet



前回までのようにRにNINO3のデータを渡し、今回は連続wavelet解析を行ってみる。

Wavelet解析にはwmtsaパッケージを用いる。

以下がプログラムである。
プロットする時、series=TRUEでもとの時系列もプロットする。

from read_NINO import read_NINO

#for R
import rpy2.robjects as robjects

data=read_NINO()
x=data["NINO3 ANOM"]

year=str(x.year[0])
month=str(x.month[0])

#data to R
nino3=robjects.FloatVector(x)
robjects.globalEnv["nino3"] = nino3

#r script
r=robjects.r
rscripts="""
nino3ts=ts(nino3,start=c(%(year)s,%(month)s),frequency=12)
png('NINO3_R_wavelet.png')

#import library wmtsa
library("wmtsa")
## calculate the CWT of the sunspots series using
## a Mexican hat wavelet (gaussian2)
a<-wavCWT(nino3ts)    
## print the result
print(a)
## plot an image of the modulus of the CWT and the
## time series
plot(a,series=TRUE) 

dev.off()
"""
r(rscripts %locals())

結果
Continuous Wavelet Transform of nino3ts
---------------------------------------
Wavelet           : Mexican Hat (Gaussian, second derivative)
Wavelet variance  : 1
Length of series  : 724
Sampling interval : 0.08333333
Number of scales  : 73
Range of scales   : 0.0833333333333333 to 60.3333333333333
x軸は年。Y軸は0が1年(2^0)、2が4年(2^2)に対応する。

参考
A Practical Guide to Wavelet Analysis
http://paos.colorado.edu/research/wavelets/


書籍
Wavelet Methods for Time Series Analysis (Cambridge Series in Statistical and Probabilistic Mathematics)


2010年5月15日土曜日

Rで自己相関



前回に続いてRにNINO3のデータを渡し、今回は自己相関を求めてみる。

Rで自己相関を求める関数はacf である。
自己相関を求めるlagは72点(72ヶ月)までとした(lag.max=72)
自己相関の他に
自己共分散 type="covariance"
偏自己相関 type="partial"
も求めることができる。
以下、プログラム

#coding: utf-8
from read_NINO import read_NINO

#for R
import rpy2.robjects as robjects

data=read_NINO()
x=data["NINO3 ANOM"]

year=str(x.year[0])
month=str(x.month[0])

#data to R
nino3=robjects.FloatVector(x)
robjects.globalEnv["nino3"] = nino3

#r script
r=robjects.r
rscripts="""
nino3ts=ts(nino3,start=c(%(year)s,%(month)s),frequency=12)
png('NINO3_R_acf.png')

op<- par(mfrow=c(1,3)) #  描画設定:1行3列画面表示
acf(nino3ts,lag.max=72,xlab="lag (year)")              # 自己相関プロット
acf(nino3ts,type="covariance",lag.max=72,xlab="lag (year)") #自己共分散プロット
acf(nino3ts,type="partial",lag.max=72,xlab="lag (year)")    #偏相関プロット
par(op)                  #作業前の描画設定に戻す

dev.off()
"""
r(rscripts %locals())

図の横の点線は95%の信頼区間をしめす(これより絶対値の小さい相関は95%の信頼区間で無相関ではないとは言えない)。 これを90%などにしたいときはci=0.9などとする。

参考
R の基本パッケージ base, stats 中の時系列オブジェクトの簡易解説
(RjpWiki)

 R言語による時系列分析
(hamadakoichi blog)

R による時系列分析入門
(書籍)

過去のRに関する記事

2010年5月14日金曜日

Rで時系列



以前につくったpythonで取り込むNINO監視領域のデータを、統計ソフトRに持って行き、時系列データとする。Rに取り込むとRを使って統計解析ができるから便利だ。

Pythonの世界からRの世界に持っていくにはrpy2を用いる。
NINO3データを渡して、Rではtsを使って時系列オブジェクトを作り、
plot.tsでプロットする。

以下、プログラム。

from read_NINO import read_NINO


#for R
import rpy2.robjects as robjects


data=read_NINO()
x=data["NINO3 ANOM"]

year=str(x.year[0])
month=str(x.month[0])

#data to R
nino3=robjects.FloatVector(x)
robjects.globalEnv["nino3"] = nino3

#r script
r=robjects.r
rscripts="""
png('NINO3_R.png')
nino3ts=ts(nino3,start=c(%(year)s,%(month)s),frequency=12)
plot.ts(nino3ts)
dev.off()
"""
r(rscripts %locals())



参考
R の基本パッケージ base, stats 中の時系列オブジェクトの簡易解説
(RjpWiki)

 R言語による時系列分析
(hamadakoichi blog)

Rによる時系列分析入門
(書籍)

過去のRに関する記事


2010年4月30日金曜日

k-平均法



k-平均法は非階層型クラスタリング手法の一つである。

参考 
クラスタリングの定番アルゴリズム「K-means法」をビジュアライズしてみた(てっく煮ブログ)

以前つくった串本浦神の潮位差と黒潮緯度の分布(下図)を2つのグループに分けてみる。

道具はscipyのk-means2関数を用いる。分けられたグループで色分けする。大きな●はそれぞれのグループの重心である(kmeans2で出てくる重心はNormarizeされたデータのものなのでそのままはつかわないこと)。重心が収束しているかの確認もおこなっている。


#read module
from read_kuroshio_lat import read_kuroshio_lat
from read_kushimoto_uragami import read_kushimoto_uragami

import matplotlib.pyplot as plt
import numpy as np

from scipy.cluster.vq import  kmeans2, whiten,vq # functions for k-means

#read data
kuroshio_lat=read_kuroshio_lat()
kushimoto_uragami=read_kushimoto_uragami()   

#kmeans
nomissing=np.logical_and(~kuroshio_lat.mask,~kushimoto_uragami.mask) #missing value should 
be excluded.
lat=kuroshio_lat.data[nomissing]
level=kushimoto_uragami.data[nomissing]
data=np.column_stack((level,lat))

whiteneddata=whiten(data) #whitened beforehand
centroids,index=kmeans2(whiteneddata,2,iter=20) #k-means

#check centroids are converged
label = vq(whiteneddata, centroids)[0]
for i in range(2):
    print 'group=',i
    print 'estimated=', centroids[i,:]
    print 'actual   =',whiteneddata[label==i,:].mean(axis=0)

#### plot
plt.figure()
#1stgroup 
plt.plot(level[index==0],lat[index==0],'r.')
plt.plot(level[index==1].mean(),lat[index==1].mean(),'go',markersize=12)
#2nd group
plt.plot(level[index==1],lat[index==1],'g.')
plt.plot(level[index==0].mean(),lat[index==0].mean(),'ro',markersize=12)

plt.xlabel("Kushimoto-Uragami")
plt.ylabel("Kuroshio Latitude")
plt.savefig("kurosho_kmeans.png")
plt.show()


出力
group= 0
estimated= [ 1.58989573 42.05977249]
     actual = [ 1.58989573 42.05977249]
group= 1
estimated= [ 0.14166519 40.3829155 ]
     actual = [ 0.14166519 40.3829155 ]

2010年4月27日火曜日

NINO3のスペクトル密度を信頼区間つきで



前回つくった信頼区間つきでスペクトル密度をもとめる関数を、NINO3監視指数に適用する。NINO3の読み込みは以前に作ったread_NINOモジュールを用いる。

下の例ではtaperにはHammingフィルタ、周波数空間でのスムージングに1-2-1フィルタ、信頼度は90%を用いた。
黒線がスペクトル密度、赤点線が信頼区間を示す。縦の黒点線は2年と7年に対応する補助線である。


from read_NINO import read_NINO

import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import numpy as np

from  psd_withError import psd_withError #import psd_withError module  

data=read_NINO()
x=data["NINO3 ANOM"]

# case1
Pxx,f,upper,lower=psd_withError(x, NFFT=len(x),Fs=12,window=np.hamming(len(x)),smooth=[1,2,1],Confidencelevel=0.90)  
plt.figure()
plt.loglog(f,Pxx,'k-o')
plt.loglog(f,upper,'r--')
plt.loglog(f,lower,'r--')
plt.loglog([1./2.,1./2.],[1.E-5,1.E2],'k--') #vertical line for 2 years
plt.loglog([1./7.,1./7.],[1.E-5,1.E2],'k--') #vertical line for 7 years
plt.savefig("NINO_psd_withError.png")
plt.show() 
 
 

2010年4月26日月曜日

psd関数をスペルトル密度誤差も出せるよう拡張



前回の見延さんのスペクトル密度の推定誤差理論を使って、以前用いたmatlabplotlib.mlab.psd関数を拡張したモジュールを作った(psd_withError)。

stand aloneで用いる時(if __name__ == '__main__':以下)は、見延さんのスクリプトと同じことを確認するプログラム。結果が同じにならないのは、
  • 見延さんのスクリプトではfftの後データ数で割ってから二乗をとっている
  • matplotlibのpsd関数でonesidedを取るとき2倍している
  • matplotlibのpsd関数では、データテーパーをかけることによる振幅の減少を補償している
ためである。もし同じ結果を得たければ、プログラム中のコメントアウトしている部分をはずすことで可能である。

 
psd_withError.py
import numpy as np
from numpy.random import randn
import scipy as sp
import scipy.signal as signal
import scipy.stats as stats

import matplotlib.pyplot as plt 
import matplotlib.mlab as mlab
import matplotlib.cbook as cbook


#------------------------------------------
def psd_withError(x, NFFT=256, Fs=2, detrend=mlab.detrend_none, window=mlab.window_hanning,noverlap=0, pad_to=None, sides='def
ault', scale_by_freq=None,smooth=None,Confidencelevel=0.9):
    """
Extention of matplotlib.mlab.psd
The power spectral density with upper and lower limits within confidence level
You should not use Weltch's method here, instead you can use smoother in frequency domain with *smooth* 

Same input as matplotlib.mlab.psd except the following new inputs
*smooth*:
    smoothing window in frequency domain  
    for example, 1-2-1 filter is [1,2,1]
    default is None
*Confidencelevel*
    Confidence level to estimate upper and lower estimates.
    Default is 0.9

Returns the tuple (*Pxx*, *freqs*,*upper*,*lower*).
    upper and lower are limits of psd within confidence level.   
"""

    Pxxtemp,freqs = mlab.psd( x, NFFT, Fs, detrend, window, noverlap, pad_to, sides,scale_by_freq)

# commented out, if you want to get Minobe-san's result  Pxxtemp=Pxxtemp/float(NFFT)/2.*((np.abs(window)**2).mean())

#smoothing
    smooth=np.asarray(smooth)
    if smooth is not None:
        avfnc=smooth/float(sum(smooth))
        Pxx=np.convolve(Pxxtemp[:,0],avfnc,mode="same")
    else:
        Pxx=Pxxtemp[:,0]
        avfnc=np.asarray([1.])

#estimate uppper and lower estimate woth equivalent degree of freedom
    if pad_to is None:
        pad_to = NFFT

    if cbook.iterable(window):
        assert(len(window) == NFFT)
        windowVals = window
    else:
        windowVals = window(np.ones((NFFT,), x.dtype))

    edof=(1.+(1./sum(avfnc**2)-1.)*float(NFFT)/float(pad_to)*np.mean(windowVals))# equivalent degree of freedom
    a1=(1.-Confidencelevel)/2.
    a2=1.-a1
    lower=Pxx*stats.chi2.ppf(a1,2*edof)/stats.chi2.ppf(0.50,2*edof)
    upper=Pxx*stats.chi2.ppf(a2,2*edof)/stats.chi2.ppf(0.50,2*edof)
    return Pxx,freqs,upper,lower

########################################################

if __name__ == '__main__':
    plt.figure()
    for npnl in range(4):
        tlng=100; nsmpl=1000
        if npnl==0 :
            nfft=100
            taper=signal.boxcar(tlng)
            avfnc=[1, 1, 1, 1, 1];
            cttl='no padding, boxcar, 5-running'
        elif npnl==1:
            nfft=100
            taper=signal.hamming(tlng)
            avfnc=[1, 1, 1, 1, 1]
            cttl='no padding, hamming, 5-running'
        elif npnl==2:
            nfft=200
            taper=signal.boxcar(tlng)
            avfnc=[1, 1, 1, 1, 1];
            cttl='double padding, boxcar, 5-running'
        elif npnl==3:
            nfft=200
            taper=signal.hamming(tlng)
            avfnc=np.convolve([1, 2, 1],[1, 2, 1])
            cttl='double padding, hamming, double 1-2-1'
    
        tsrs=randn(tlng,nsmpl)
        ds_psd=np.zeros([nfft/2+1,nsmpl])
        upper_psd=np.zeros([nfft/2+1,nsmpl])
        lower_psd=np.zeros([nfft/2+1,nsmpl])
        for n in range(nsmpl):
            a,b,u,l=psd_withError(tsrs[:,n],NFFT=tlng,pad_to=nfft,Fs=1,window=taper,smooth=avfnc,Confidencelevel=0.9)
            ds_psd[:,n]=a[:]
            upper_psd[:,n]=u[:]
            lower_psd[:,n]=l[:]
        frq=b[:]

# 90% confidence level by Monte-Carlo
        srt_psd=np.sort(ds_psd,axis=1)
        c=np.zeros([frq.size,2])                
        c[:,0]=np.log10(srt_psd[:,nsmpl*0.05])
        c[:,1]=np.log10(srt_psd[:,nsmpl*0.95])

# estimate from extended degree of freedom 
        ce=np.zeros([frq.size,2]) 
        ce[:,0]=np.log10(np.sort(upper_psd,axis=1)[:,nsmpl*0.5])
        ce[:,1]=np.log10(np.sort(lower_psd,axis=1)[:,nsmpl*0.5])
#plot
        plt.subplot(2,2,npnl+1)
        plt.plot(frq,c,'b',frq,ce,'r')
        plt.title(cttl)
        plt.xlabel('frq')
        plt.ylabel('psd')
        if (npnl==0): plt.legend(('Monte-carlo','','Theory'),'lower center',labelspacing=0.05)
    plt.subplots_adjust(wspace=0.6,hspace=0.4)
    plt.savefig("psd_withError.png")
    plt.show()

2010年4月20日火曜日

スペクトルの推定誤差



見延さんのスペクトル誤差に関するmatlab scriptをpythonに移植してみた。
見延 2001:大 気海洋物理学特論4 大気海洋統計データ解析 第3章
http://ocw.hokudai.ac.jp/Course/GraduateSchool/Science/MeteorologyAndOceanology/2001/page/materials/MeterologyAndOceanology-2001-Note-03.pdf
Page15-16
numpy/scipy/matplotlibを使えば、matlabのscriptを移植するのはそれほど難しくない。
χ二乗分布の累積分布の逆関数はscipyのstats.chi2.ppfをそのまま用いた。
(注:今の場合関係ないが、この関数は自由度が1以下の場合はうまくいかないバグがあるようだ。すくなくともversion 0.7.0で確認した。)


#coding: utf-8
import numpy as np
from numpy.random import randn
import scipy as sp
import scipy.signal as signal

import matplotlib.pyplot as plt 
#---------------------------------------
def chi2inv_nint(p,df):
    import scipy.stats as stats
    c=stats.chi2.ppf(p,df)
    return c

#------------------------------------------
def psd_ex1(tsrs,nfft,ctaper,avfnc):
    """
function [ds_psd,frq]= psd_ex1(tsrs,nfft,ctaper,avfnc)
Power Spectrum Density を直接法によって求めるpython Script
平滑化は周波数領域で行なう.
複数時系列(sz2$gt;1)についても計算する.
入力
tsrs: 入力時系列 (sz1, sz2)各列についてスペクトルを求める
nfft: fft を計算する時間サンプル数
ctaper: taper 関数('hamming','hanning'など)
avfnc: 平滑化ウインドウ
出力
ds_psd: スペクトル密度.1×nfft/2+1.
frq: 周波数.1×nfft/2+1.
todo
PSD の定義に振幅がきちんと合うようにする.
"""
    if tsrs.ndim==1:
        sz1=tsrs.size #tsrs をsz2*1 のデータに変換.
        sz2=1
        tsrs=tsrs.reshape((sz1,1))
    else:
        sz1,sz2=tsrs.shape
    print sz1,nfft
    assert sz1<=nfft, ' sz1$gt;nfft' #nfft はtlng よりも大きくなくてはならない
#満たさないとエラーメッセージを出して終了
    avfnc=avfnc/float(np.sum(avfnc)) #後で説明する平滑化ウインドウの規格化    
    w = getattr(signal, ctaper)(sz1) #実際のtaper 計算
    for n in range(sz2):
        tsrs[:,n]=tsrs[:,n]*w #時系列にテーパーをかける
    tsrs=np.append(tsrs,np.zeros((nfft-sz1+1,sz2)),axis=0) #ゼロパッティング
    famp=sp.fft(tsrs,axis=0) #フーリエ変換
    famp=famp[0:(nfft/2+1),:]/sz1 #実数時系列のスペクトルなら,この範囲のみが必要.
    d_psd=abs(famp)**2; #直接スペクトル値 (d: direct)
    ds_psd=np.zeros_like(d_psd)
    for n in range(sz2):
        ds_psd[:,n]=np.convolve(d_psd[:,n],avfnc,mode='same'); #畳込みを使って平滑化 (ds: direct-smoothed) #畳み込みで増えた部分を除く
    frq=np.arange(nfft/2+1)/float(nfft); #周波数を求めておく
    return ds_psd,frq

########################################################

if __name__ == '__main__':
    plt.figure()
    for npnl in range(4):
        tlng=100; nsmpl=1000
        if npnl==0 :
            nfft=100
            taper=signal.boxcar(tlng)
            ctaper='boxcar'
            avfnc=np.asarray([1, 1, 1, 1, 1]);
            cttl='no padding, boxcar, 5-running'
        elif npnl==1:
            nfft=100
            taper=signal.hamming(tlng)
            ctaper='hamming'
            avfnc=np.asarray([1, 1, 1, 1, 1])
            cttl='no padding, hamming, 5-running'
        elif npnl==2:
            nfft=200
            taper=signal.boxcar(tlng)
            ctaper='boxcar'
            avfnc=np.asarray([1, 1, 1, 1, 1]);
            cttl='double padding, boxcar, 5-running'
        elif npnl==3:
            nfft=200
            taper=signal.hamming(tlng)
            ctaper='hamming'
            avfnc=np.convolve([1, 2, 1],[1, 2, 1])
            cttl='double padding, hamming, double 1-2-1'
    
        tsrs=randn(tlng,nsmpl);
        [ds_psd,frq]=psd_ex1(tsrs,nfft,ctaper,avfnc)
# Monte-Carlo 法で得る90%信頼限界
        srt_psd=np.sort(ds_psd,axis=1)
        c=np.zeros([frq.size,2])                
        c[:,0]=np.log10(srt_psd[:,nsmpl*0.05])
        c[:,1]=np.log10(srt_psd[:,nsmpl*0.95])
        avfnc=avfnc/float(sum(avfnc)) # frequency domain の平滑化関数の規格化
        c0=srt_psd[:,nsmpl*0.5]

        edof=(1+(1/sum(avfnc**2)-1)*tlng/nfft*np.mean(taper))# 等価自由度の計算
        ce=np.zeros([frq.size,2]) 
        ce[:,0]=np.log10(c0*chi2inv_nint(0.050,2*edof)/chi2inv_nint(0.50,2*edof))
        ce[:,1]=np.log10(c0*chi2inv_nint(0.950,2*edof)/chi2inv_nint(0.50,2*edof))
#plot
        plt.subplot(2,2,npnl+1)
        plt.plot(frq,c,'b',frq,ce,'r')
        plt.title(cttl)
        plt.xlabel('frq')
        plt.ylabel('psd')
        if (npnl==0): plt.legend(('Monte-carlo','','Theory'),'lower center',labelspacing=0.05)
    plt.subplots_adjust(wspace=0.6,hspace=0.4)
    plt.savefig("psd_ex1.png")
    plt.show()  

結果の図