まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

Java で Git のリポジトリを操作するライブラリに JGit があります:
http://www.eclipse.org/jgit/
2016082801


現在は Eclipse 参加のサブプロジェクトとして提供されています。すごく便利な反面、ちょっとクセがあります。以下、基本的な使い方を説明しますが、まずは前提をいくつか:

【前提】
(1) 対象とする Git のリモートリポジトリ URI は http://xxxgit.com/name/project.git とします。
(2) 上記リポジトリにアクセス(特にプッシュ)する際の認証は ID: username, Password: password であるとします。
(3) ローカルリポジトリ(上記リポジトリをローカルにクローンする先)のディレクトリは ./project (つまりカレントディレクトリ上に project というサブディレクトリを作る)とします。


【準備】
JGit のダウンロードページから最新版の JGit をダウンロードし、展開して JAR を取り出し、自分のプロジェクト内に(Classpath を通すなどして)用意します。

ここまで用意できれば JGit を使うことができます。以下、一通りの git 操作(clone, pull, add, commit, push)を行う様子を順に紹介します。


【git clone】
目的のリモートリポジトリ(今回の例では http://xxxgit.com/name/project.git)からコードをクローンします:
try{
  Repository localRepo = new FileRepository( "./project/.git" );
  Git git = new Git( localRepo );

  if( git != null ){
    //. git clone
    git.cloneRepository().setURI( "http://xxxgit.com/name/project.git" ).setDirectory( new File( "./project" ) ).call();
  }
}catch( Exception e ){
  e.printStackTrace();
}

以下全ての例に言えることですが、ローカルリポジトリのインスタンス変数を作る際に指定するのは、ローカルリポジトリのフォルダに "/.git" をつけたものです。

一方、クローン実行時に指定するローカルフォルダはローカルリポジトリのフォルダそのものです。この辺りがクセのある所で、知らないと混乱します。


【git pull】
クローンしたローカルリポジトリに対し、リモートリポジトリに加えられている最新の変更を反映させます:
try{
  Repository localRepo = new FileRepository( "./project/.git" );
  Git git = new Git( localRepo );

  if( git != null ){
    //. git pull
    PullCommand pc = git.pull();
    pc.call();
  }
}catch( Exception e ){
  e.printStackTrace();
}

JGit では各コマンドを実行する際には ****Command クラスの(上記例では PullCommand クラス)インスタンスを作って、必要であれば設定を加えて、call() する、という処理を実行します。

そして次の add コマンドを実行する前に、このローカルリポジトリ内のファイルに何らかの変更が加わっていることを想定してください。


【git add】
ローカルリポジトリ内のファイルシステムに対して行った変更作業をリポジトリに加えます:
try{
  Repository localRepo = new FileRepository( "./project/.git" );
  Git git = new Git( localRepo );

  if( git != null ){
    //. git add
    AddCommand ac = git.add();
    ac.addFilepattern( "." );  //. 全ての変更を git add する
    try{
      ac.call();
    }catch( NoFilepatternException e ){
      e.printStackTrace();
    }
  }
}catch( Exception e ){
  e.printStackTrace();
}

AddCommand インスタンスにファイルパターンを指定して call() する、という手順です。


【git commit】
ローカルリポジトリの変更内容をコミットします:
try{
  Repository localRepo = new FileRepository( "./project/.git" );
  Git git = new Git( localRepo );

  if( git != null ){
    //. git commit
    CommitCommand cc = git.commit();
//. コミッターの名前とメールアドレス、コミットメッセージを指定 cc.setCommitter( "commiter_name", "committer_email" ).setMessage( "Some message." ); try{ cc.call(); }catch( NoHeadException e ){ e.printStackTrace(); }catch( NoMessageException e ){ e.printStackTrace(); }catch( ConcurrentRefUpdateException e ){ e.printStackTrace(); }catch( WrongRepositoryStateException e ){ e.printStackTrace(); } } }catch( Exception e ){ e.printStackTrace(); }

コミッターの名前とメールアドレス、そしてコミットメッセージを追加した上で call() します。


【git push】

ローカルリポジトリの変更内容をリモートリポジトリに対してプッシュします:
try{
  Repository localRepo = new FileRepository( "./project/.git" );
  Git git = new Git( localRepo );

  if( git != null ){
//. git push CredentialsProvider cp = new UsernamePasswordCredentialsProvider( username, password ); PushCommand pc = git.push(); pc.setCredentialsProvider( cp ).setForce( true ).setPushAll(); } }catch( Exception e ){ e.printStackTrace(); }

認証用の usernamepassword を指定してプッシュする、という流れです。


以上、JGit を使った基本的な git コマンドの実行方法を紹介しました。クセがある、と紹介しましたが、ある程度慣れてしまえばそんなに苦ではないと思います。何よりもプログラムから git が使えると複製機能をこれで実装できたりするのですごく便利です。

自分はエンジニアのキャリアの中でエクセルではない某表計算ソフトの開発に携わる機会がありました。そんなわけで、どちらかというとアンチ・エクセル派だったりします:
2016082100


また一部の人には公にしていますが、その後ノーツ/ドミノのエンジニアも担当させていただく機会もありました。ノーツは大好きです、.NSF 万歳!


だからというわけではないのですが、そんなバックグラウンドを持つ自分がエクセルデータファイル(*.xls や *.xlsx)をノーツデータベース(*.nsf)化するツールを作ってみました。これで幸せになれる人がいたら使ってほしいです(笑)。

ちなみに OpenNTF.org を探してみると、この逆(ノーツをエクセル化)を実現するようなものは(自分が動かしてみたわけではありませんが)見つかります。が「エクセルをノーツ化」という変態的のはさすがになさそうだったので、技術的挑戦も兼ねて作ってみました。正直かなり手間取りましたが、当初考えていた最低限の挙動はできるようになったので、一旦公開して、紹介します。実体はノーツの NTF および NSF ですが、ACL は誰でも開いてみれるようになっているはずなので、興味ある人は中身を確認したり、もう少し便利に作りなおしたりしてみてください。

まず仕様のおさらいの意味で、どんなインプットに対して、どんなアウトプットができるようになるのか、という点を紹介します。用意する必要があるのはエクセルシートファイルです:
2016081901


このエクセルファイルには複数のシートが存在していることを想定します。そして各シート毎になんらかのデータが格納されているものとします。この例ではシートは3つあり、1枚目(Sheet1)にはこんな感じで、7行5列のデータが入ってます(ちなみに E 列の値は同じ行の C 列の値を2倍したものになるような計算結果として定義されています):
2016082001


一方、2枚目のシート(Sheet2)には10行3列のこんなデータが入っているものとします。シートごとに列数や行数が全く異なるデータが入っていることを想定しています:
2016082002


3枚目のシート(Sheet3)には変換対象範囲を理解する上で役立つシートにしました。シート内のセルが虫食い状態のようにところどころに空白のセルがあるものです。ちなみにこの例では5行3列(A1 セルから C5 セルまでの矩形部分)を変換対象にするような仕様にしています(E2 セルにデータは存在していますが、D 列に全くデータがないので対象外と判定する仕様です):
2016082003


このデータをノーツ化するにあたり、以下の様なルールで変換(というかインポート)します:
  • 1つのエクセルシートの内容を1つのビューとして変換する
  • なので、3つのシートを持つエクセルファイルは3つのビューをもつノーツデータベースに変換される
  • ノーツを使いつつ、見栄えはなるべく表計算っぽくする

実際の作業は以下のようになります。まず今回用意したノーツデータベース "XLS2NSF" (リンクは後述)をノーツクライアントから開きます:
2016081902


いわゆる「ポリシー文書」が開き、使い方などが確認できます。内容は確認いただくとして、すぐに試すには「エクセルファイル指定」と書かれたボタンをクリックします:
2016082101


ファイルダイアログが開くので、エクセルファイル(*.xls / *.xlsx)を指定します。ここでは上記で紹介した MyBook1.xls という3つのシートを持つファイルを指定しています:
2016081904


後は全自動でノーツDB化が実行されます。事実上エクセルファイルを指定するだけ、です。

全ての変換処理が完了すると、指定したファイル名と同じタイトルのノーツDB(ファイルパスはノーツデータディレクトリ直下、ファイル名はタイトルの最後に ".nsf" を加えたもの)がワークスペース上に追加されているはずです。これをクリックすると変換結果が参照できます:
2016081903


この作成されたデータベースをノーツで開いた様子がこちらです。元ファイルには3つのシートが存在していましたが、それぞれのシートがノーツのビューとしてナビゲータから切り替えて見ることができます。こちらは Sheet1 のもの。7行5列の表が中身も含めて再現されています。なお元ファイルでは E 列の中身は同じ行の C 列の2倍になるような計算式でしたが、変換後は固定値になっています:
2016082004


2つ目の Sheet2 に切り替えるとこのようになります。元のシート通り、10行3列の表ができているはずです:
2016082005


3つ目の Sheet3 はこちら。セルの虫食い状態も再現されています。また上記で紹介したように元のシートの A1 セルから C5 セルの部分が変換されているはずです:
2016082006



この変換後のノーツデータベース(のビュー)は ViewEdit モードを実装しているので、特定のセルをクリックすると、そのセルの値を書き換えることができます。以下の例では Sheet2 の C6 列(もともと "4" と入力されていた箇所)を編集して "123" にしている所です:
2016082007


Enter を押すと変更が確定して、元のビューに戻ります:
2016082008


と、まあこんな需要があるのかないのか分からないものを作ってみました。折角なので GitHub で公開しています:
https://github.com/dotnsf/xls2nsf

(注 .nsf や .ntf のノーツデータベースを Git で管理すべきではありません。良い子はマネしちゃだめ)


実際に試してみたい! という奇特な方は、上記 GitHub から "Clone or Download" - "Download ZIP" を選択してダウンロードし、展開して使ってください:
2016081902


また、このブログの中で紹介に使っているエクセルのサンプルファイル(MyBook1.xls)も併せて公開します。ぜひ手持ちのファイルで試していただきたいのですが、良さ気なのがない場合は使ってください:
http://dotnsf.blog.jp/MyBook1.xls


以下、2016/Aug/21 時点での制約です:
  • LotusScript で実装しています。エクセル読み取りは OLE を使っています。そのためノーツクライアントとエクセルがインストールされた Windows 環境でないと動かないと思います。
  • 変換の対象となる部分の計算方法が特殊です。現時点では各シートの A1 セルには必ず値が入っている必要があります。
  • また列は行、列ともに空セルしかない最初の行や列を探して、その内側を対象とみなしています。下の霊であれば、D列と 6 行目には空セルしかないので、その内側の A1 セルから C5 セルまでが対象です(E2 セルにデータがありますが、現在の仕様ではデータ変換の対象にはしません)。
2016082001


ノーツ化することで式による計算機能を失ってしまうわけですが(汗)、代わりに強力な ACL によるアクセス管理ができるようになります。この辺りは改善できればしたいなあ、と思っていますが、ノーツのビュー内の各エントリーの中身を式の結果にするというのは現時点では困難と思ってます。あと1行追加とか、1列追加とか、できそうで実装してない機能もありますが、その辺りは興味ある人に追加実装していただきたいです(手抜きともいう)。

ま、自分で作っておいてなんですが、「そこそこ動く MAD なツール」だと思いますw こういうのは我ながら嫌いじゃない。

Python から R 言語を呼び出して実行する手順をまとめました。システムは CentOS 6.x を想定しています。


具体的には Python の PypeR ライブラリを使います。なので Python と R 言語と PypeR ライブラリをインストールすることになります。ただそれだけではまだ色々不便なので、numpy や pylab などの PypeR と一緒によく使うライブラリとまとめて環境を用意して使うことにします。

まず以下の手順を実行する上で前提となるライブラリ類と Python3 をまとめて導入しておきます。
# rpm -ivh http://mirror.yandex.ru/fedora/russianfedora/russianfedora/free/el/releases/6/Everything/x86_64/os/puias-release-6-2.R.noarch.rpm
# yum install -y python3.x86_64 python3-devel.x86_64 python3-tools.x86_64 freetype* libpng-devel libjpeg-devel lapack-devel

次に pip をインストールします:
# curl -kL https://bootstrap.pypa.io/get-pip.py | python

導入した pip を使って PypeR などのライブラリをインストールします(numpy や scipy などは結構時間がかかります):
# pip install numpy
# pip install pyper
# pip install pandas
# pip install scipy
# pip install pylab

最後に R 言語環境をインストールします:
# yum install R

これで全ての環境が整いました。では R 言語と導入したライブラリを使って動かす、以下の様な Python スクリプトを用意して実行してみます(なお、この Python スクリプトはここで紹介されているものです):
# -*- coding:utf-8 -*-

import numpy
import pandas
import pylab
import pyper

n = 200
# データの生成
score_x = numpy.random.normal(171.77, 5.54, n)
score_y = numpy.random.normal(62.49, 7.89, n)

score_x.sort()
score_x = numpy.around(score_x + numpy.random.normal(scale=3.0, size=n), 2)
score_y.sort()
score_y = numpy.around(score_y + numpy.random.normal(size=n), 2)

# 散布図を描く
pylab.scatter(score_x, score_y, marker='.', linewidths=0)
pylab.grid(True)
pylab.xlabel('X')
pylab.ylabel('Y')

# Rで回帰分析
df = {'X': score_x, 'Y': score_y}
df = pandas.DataFrame(df)

r = pyper.R(use_pandas='True')

# Rへデータ渡す
r.assign('df', df)

# Rのコマンド実行
print(r("summary(df)"))
r("result <- lm(Y~X, data=df)")
print(r("summary(result)"))

#予測区間と信頼区間を算出するため
new_x = numpy.arange(155, 190, 0.1)
new_df = pandas.DataFrame({'X': new_x})
r.assign('new', new_df)

# 予測区間(R)
r("prediction <- predict(result, new, interval='prediction')")
# 信頼区間(R)
r("confidence <- predict(result, new, interval='confidence')")

# Python側にとってくる
lm_result = r.get('result$fitted.values')
prediction = pandas.DataFrame(r.get('prediction'))
confidence = pandas.DataFrame(r.get('confidence'))

# 回帰直線, 予測区間, 信頼区間を描く
pylab.plot(score_x, lm_result, 'r', linewidth=2)

pylab.plot(new_x, prediction[1], 'g', linewidth=1)
pylab.plot(new_x, prediction[2], 'g', linewidth=1)

pylab.plot(new_x, confidence[1], 'c', linewidth=1)
pylab.plot(new_x, confidence[2], 'c', linewidth=1)

pylab.show()

この内容を例えば python01.py という名前で保存し、python で実行すると以下の青字部分のような出力が確認できます:
# python3 python01.py

try({summary(df)})
       X               Y
 Min.   :153.2   Min.   :41.81
 1st Qu.:167.1   1st Qu.:57.49
 Median :171.3   Median :63.45
 Mean   :171.4   Mean   :63.28
 3rd Qu.:176.3   3rd Qu.:68.39
 Max.   :186.8   Max.   :80.02

try({summary(result)})

Call:
lm(formula = Y ~ X, data = df)

Residuals:
   Min     1Q Median     3Q    Max
-8.389 -2.431 -0.233  2.405 10.974

Coefficients:
              Estimate Std. Error t value Pr(>|t|)
(Intercept) -106.42882    6.72661  -15.82   <2e-16 ***
X              0.99004    0.03921   25.25   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.67 on 198 degrees of freedom
Multiple R-squared:  0.763,     Adjusted R-squared:  0.7618
F-statistic: 637.5 on 1 and 198 DF,  p-value: < 2.2e-16

Python スクリプト内で指定されている PypeR や Pylab などのライブラリが正しく読み込まれて実行することができました。

(参考)
https://gist.github.com/mia-0032/6378324

このページのトップヘ