とりあえず下記を参考にdependencyは記述されているものとする。
https://github.com/codelibs/elasticsearch-analysis-kuromoji-neologd
JapaneseTokenizerを利用して形態素解析する。unicode normalize等はFilter等は使わずにベタ書きしている。
import java.io.StringReader
import java.text.Normalizer
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute
import org.codelibs.neologd.ipadic.lucene.analysis.ja.JapaneseTokenizer
import org.codelibs.neologd.ipadic.lucene.analysis.ja.tokenattributes.BaseFormAttribute
import org.codelibs.neologd.ipadic.lucene.analysis.ja.tokenattributes.InflectionAttribute
import org.codelibs.neologd.ipadic.lucene.analysis.ja.tokenattributes.PartOfSpeechAttribute
import org.codelibs.neologd.ipadic.lucene.analysis.ja.tokenattributes.ReadingAttribute
object HtmlParser extends App {
val value = "今日は晴れておりますね"
val normValue = Normalizer.normalize(value, Normalizer.Form.NFKC).toLowerCase
val reader = new StringReader(normValue.toString())
val stream = new JapaneseTokenizer(null, true, JapaneseTokenizer.Mode.NORMAL)
stream.setReader(reader)
stream.reset()
while (stream.incrementToken()) {
println(s"term=${stream.getAttribute(classOf[CharTermAttribute])}, " +
s"baseForm=${stream.getAttribute(classOf[BaseFormAttribute]).getBaseForm}, " +
s"partOfSpeach=${stream.getAttribute(classOf[PartOfSpeechAttribute]).getPartOfSpeech}, " +
s"reading=${stream.getAttribute(classOf[ReadingAttribute]).getPronunciation}, " +
s"inflectionForm=${stream.getAttribute(classOf[InflectionAttribute]).getInflectionForm}, " +
s"inflectionType=${stream.getAttribute(classOf[InflectionAttribute]).getInflectionType}, ")
}
stream.close()
}
実行結果
term=今日, baseForm=null, partOfSpeach=名詞-副詞可能, reading=キョー, inflectionForm=null, inflectionType=null, term=は, baseForm=null, partOfSpeach=助詞-係助詞, reading=ワ, inflectionForm=null, inflectionType=null, term=晴れ, baseForm=晴れる, partOfSpeach=動詞-自立, reading=ハレ, inflectionForm=連用形, inflectionType=一段, term=て, baseForm=null, partOfSpeach=助詞-接続助詞, reading=テ, inflectionForm=null, inflectionType=null, term=おり, baseForm=おる, partOfSpeach=動詞-非自立, reading=オリ, inflectionForm=連用形, inflectionType=五段・ラ行, term=ます, baseForm=null, partOfSpeach=助動詞, reading=マス, inflectionForm=基本形, inflectionType=特殊・マス, term=ね, baseForm=null, partOfSpeach=助詞-終助詞, reading=ネ, inflectionForm=null, inflectionType=null,]]>
Exception in thread "main" scala.MatchError: [foo,28,995,0.0] (of class org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema)
String, Long, Long, Doubleをイメージしていたので、こんな感じでMatchさせようとしていた。
case Row(hostName: String, score1: Long, score2: Long, cost: Double) =>
中身を確認。
case x: GenericRowWithSchema => { println(x.getClass); println(x.schema) }
#=> StructType(StructField(host_name,StringType,true), StructField(score1,LongType,true), StructField(score2,LongType,false), StructField(cost,DecimalType(1,1),false))
Doubleではなくjava.math.BigDecimalというだけだった。。。
case Row(hostName: String, score1: Long, score2: Long, cost: java.math.BigDecimal) =>]]>
Recordが入れ子になっている場合は型調べつつ再帰的にやれば子要素まで取れると思う(未確認)。
Javaと言いつつコードはScala。まあ、同じようなものだし。
import org.apache.avro.generic.GenericRecord
import scala.collection.JavaConversions._
def getGenericRecord(): GenericRecord = {
// get generic record code
}
val record = getGenericRecord()
for (field <- record.getSchema.getFields) {
// 方法1
println(field.name, record.get(field.name))
// 方法2
println(field.name, record.get(field.pos))
}
GenericRecordのgetにはStringとInt双方の引数が取れるようになっていて、Fieldのnameを渡してもposを渡しても同じように取れる。
]]>とりあえずrvmインストール。
$ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 $ curl -sSL https://get.rvm.io | bash -s stable --ruby
ターミナルを立ち上げ直して.bashrcやら.bash_profileやらを読み込ませたあと、rubyを入れる。都合により.2.2.1を採用。
$ rvm list known $ rvm install ruby-2.2.1 $ rvm use ruby-2.2.1
postgresqlを入れる。
$ sudo apt-get install postgresql-9.5 libpq-dev
bundleを入れる。
$ gem install bundler
JavascriptのRuntimeがないと後で言われるのでnodejs入れとく。
$ sudo apt-get install nodejs
postgresql上でユーザを作る。
$ sudo -u postgres psql > create database foo_table > CREATE USER bar_user with password 'buz_passwd'; > GRANT ALL PRIVILEGES ON DATABASE foo_table to bar_user;
あとは立ち上げ。プロジェクトは既に出来合いのものがあるとする。
$ bin/bundle install $ bin/rake db:migrate $ bin/rails server
だいたいこんな感じで動いた。 ]]>
$ rvm use ruby-2.2.1 RVM is not a function, selecting rubies with 'rvm use ...' will not work. You need to change your terminal emulator preferences to allow login shell. Sometimes it is required to use `/bin/bash --login` as the command. Please visit https://rvm.io/integration/gnome-terminal/ for an example.
解決方法はStackOverflowに書いてあった。
http://stackoverflow.com/questions/9336596/rvm-installation-not-working-rvm-is-not-a-function
rvmは.bash_profileに設定書くけどGnome Shellとかだとデフォルトがインタラクティブシェルになってるから、ログインシェルにしないと読まないよね。とりあえず bash --login とか言っとけば動くよみたいな内容。
Gnomeのターミナルの設定に「ログインシェルを使う」みたいなのがあるので、そこをチェックすれば動くようになる。もしくは先に bash --login と言ってから打つか、source ~/.bash_profileするか、まあなんでもいいや。 ]]>
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
public class SimpleMessageSender {
private static final String postUrl = "https://slack.com/api/chat.postMessage";
public static String send(String token, String channel, String userName, String text)
throws UnsupportedEncodingException, IOException {
byte[] postData = String.format("token=%1s&channel=%2s&username=%3s&text=%4s", token,
channel, userName, URLEncoder.encode(text, "utf-8")).getBytes();
HttpURLConnection conn = (HttpURLConnection) new URL(postUrl).openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Length", Integer.toString(postData.length));
try (DataOutputStream wr = new DataOutputStream(conn.getOutputStream())) {
wr.write(postData);
}
try (Scanner s = new Scanner(conn.getInputStream())) {
return s.useDelimiter("\\A").hasNext() ? s.useDelimiter("\\A").next() : "";
}
}
}
]]>jreの場合はdefault-jreを入れるところだけど、jdkの場合はdefault-jdkで良いらしい。
$ sudo apt-get install default-jdk
これで最新(この記事を書いている時点では1.8.0_91)が入った。defaultと書いておけば済むのは便利。
前のバージョンを入れたい際は apt-get install openjdk-7-jdk のようにバージョン付きのパッケージがあるっぽいけど未検証。 ]]>
https://hadoop.apache.org/docs/r2.6.1/api/org/apache/hadoop/fs/FileSystem.html
じゃ、hadoop fs -rmdir path とやっているアレは何者かと見てみると、重要なところとしては下記のような記述になっていた。
if (item.fs.listStatus(item.path).length == 0) {
if (!item.fs.delete(item.path, false)) {
throw new PathIOException(item.toString());
}
}
// https://github.com/gnudeep/hadoop-2.6/blob/master/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Delete.java
listStatusして0だったら消しに行く感じらしい。まあ、これでいいか。 ]]>
$ ls -t | tail -100 | xargs rm]]>
下記で入る。
yum gcc-c++]]>
try {
// do something
} catch {
case e @ (_: IOException | _: IllegalFormatException) => { e.printStackTrace() }
case e => { throw e }
}
scala.util.Tryで似たようなことをどうするか調べようとしたのだけど、いい方法が見つからずこんな書き方になった。
scala.util.Try {
// do something
} match {
case Success(v) => { /* success */ }
case Failure(e) => {
e match {
case e @ (_: IOException | _: IllegalFormatException) => e.printStackTrace()
case e => throw e
}
}
}
まあ、いいか。 ]]>
下記はn_jobsを4に指定している。
import time
import joblib
def print_thread_number(thread_number):
for i in range(3):
print(thread_number, i)
time.sleep(1)
joblib.Parallel(n_jobs=4)(joblib.delayed(print_thread_number)(i) for i in range(10))
結果
0 0 1 0 2 0 3 0 0 1 1 1 2 1 3 1 0 2 1 2 3 2 2 2 4 0 5 0 6 0 7 0 4 1 7 1 5 1 6 1 4 2 7 2 5 2 6 2 8 0 9 0 8 1 9 1 8 2 9 2
n_jobsで指定した4個ずつ、きちんと消化されている。
n_jobsを10にすると、10個まとめて実行される。コア数を多少超えても大丈夫なようだ。
0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 0 1 4 1 1 1 3 1 2 1 5 1 6 1 7 1 9 1 8 1 0 2 4 2 1 2 3 2 2 2 5 2 6 2 7 2 9 2 8 2
100並列とかにしても問題なかった。
処理能力をしっかり食う処理を並列で実行させた場合はどうなるか。下記は重い処理を20並列でやらせた際のtop。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 5862 user 20 0 185448 22452 4004 R 21.6 0.1 0:01.62 python 5871 user 20 0 185448 22792 4320 R 21.6 0.1 0:01.60 python 5866 user 20 0 185448 22780 4320 R 20.9 0.1 0:01.57 python 5863 user 20 0 185448 22452 4004 R 19.6 0.1 0:01.57 python 5865 user 20 0 185448 22776 4320 R 19.6 0.1 0:01.54 python 5858 user 20 0 185448 22760 4320 R 19.3 0.1 0:01.50 python 5870 user 20 0 185448 22788 4320 R 19.3 0.1 0:01.54 python 5872 user 20 0 185448 22792 4320 R 19.3 0.1 0:01.50 python 5859 user 20 0 185448 22700 4256 R 18.9 0.1 0:01.51 python 5860 user 20 0 185448 22576 4128 R 18.9 0.1 0:01.53 python 5864 user 20 0 185448 22452 4004 R 18.9 0.1 0:01.55 python 5868 user 20 0 185448 22788 4320 R 18.9 0.1 0:01.51 python 5869 user 20 0 185448 22788 4320 R 18.9 0.1 0:01.50 python 5873 user 20 0 185448 22568 4096 R 18.9 0.1 0:01.49 python 5874 user 20 0 185448 22568 4096 R 18.9 0.1 0:01.51 python 5876 user 20 0 185448 22800 4320 R 18.9 0.1 0:01.48 python 5857 user 20 0 185448 22760 4320 R 18.6 0.1 0:01.53 python 5861 user 20 0 185448 22448 4004 R 18.6 0.1 0:01.50 python 5867 user 20 0 185448 22788 4320 R 18.6 0.1 0:01.52 python 5875 user 20 0 185448 22608 4128 R 18.6 0.1 0:01.48 python
綺麗にCPUが取り合いになっている。とりあえず負荷が軽い行為であれば、並列数を上げることは問題なさそう。 ]]>
StackOverFlowにPython2での抜き出し方が書いてあった。Python3用だと下記のように書けるようだ。
from urllib.parse import urlparse
url = 'http://www.example.com/foo/bar/baz'
'{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url))
#=> http://www.example.com/
]]>
# マイナスや1.0を超えた値が混ざる適当なDataFrameをでっち上げる
df = pd.DataFrame(1.3 * np.random.random([5, 2]) - 0.2, columns=['A', 'B'])
#=> A B
#=> 0 0.418959 0.127008
#=> 1 0.766270 0.939455
#=> 2 1.019421 -0.193150
#=> 3 0.802732 0.909558
#=> 4 0.197882 -0.067508
これを1.0以上だったら1.0に、0以下だったら0.0に変換したい。
これまで適当にこんな風に書いていた。
np.fmin(1.0, np.fmax(0.0, df))
どう考えても良い書き方とは思えないので調べたら、np.clipという関数が出てきた。引数に配列、min、maxの3つを指定することで範囲内に収められるらしい。
np.clip(df, 0.0, 1.0)
#=> A B
#=> 0 0.418959 0.127008
#=> 1 0.766270 0.939455
#=> 2 1.000000 0.000000
#=> 3 0.802732 0.909558
#=> 4 0.197882 0.000000
]]>