2014年8月9日土曜日

データベースを使う(4)

データを取り出すのは簡単だ。SQLのselectを使えばいいだけ。下のような形で、馬名で指定する馬の一定期間内のデータを取得することが出来る。

public List<HorseData> select(String horsename, Date mindate, Date maxdate) {
try {
PreparedStatement ps = conn.prepareStatement("select * from Horse where name = ? and date < ? and date >= ? order by date DESC");
ps.setString(1, horsename);
ps.setDate(2, new java.sql.Date(maxdate.getTime()));
ps.setDate(3, new java.sql.Date(mindate.getTime()));
ResultSet rs = ps.executeQuery();
return readResultSet(rs);
} catch (SQLException se) {
for (SQLException e = se; e != null; e = e.getNextException()) {
TLog.e("%s: %s%n", e.getSQLState(), e.getMessage());
}
}
return null;

}
private List<HorseData> readResultSet(ResultSet rs) {
List<HorseData> list = new ArrayList<HorseData>();
try {
while (rs.next()) {
HorseData hd = new HorseData();
hd.name = rs.getString("name");
hd.term = rs.getInt("term");
hd.place = rs.getInt("place");
hd.day = rs.getInt("day");
hd.rnum = rs.getInt("rnum");
hd.date = new Date(rs.getDate("date").getTime());
hd.frame = rs.getInt("frame");
hd.number = rs.getInt("number");
hd.order = rs.getInt("ordernumber");
hd.time = rs.getInt("time");
hd.popularity = rs.getInt("popularity");
hd.weight = rs.getInt("weight");
hd.loadweight = rs.getFloat("loadweight");
hd.threefurlong = rs.getFloat("threefurlong");
hd.odds = rs.getFloat("odds");
hd.setPassorder(rs.getString("passorder"));
list.add(hd);
}
} catch (SQLException se) {
for (SQLException e = se; e != null; e = e.getNextException()) {
TLog.e("%s: %s%n", e.getSQLState(), e.getMessage());
}
}
return list;
}

2014年8月8日金曜日

書評: ある奴隷少女に起こった出来事

奴隷だった著者による自伝。著者のハリエットは1813年生まれ。リンカーンが奴隷解放宣言をしたのが1862年なので、49歳の頃ということになる。アメリカの南部で奴隷があったことは知識として知っていても、具体的にどういうものなのか知らない人は多いだろう。僕もそうだった。

著者が、奴隷として使えていた家から抜け出し、自由を得るまでの話であるが、それまでの紆余曲折が書かれている。その時々に最善を考えて行動しているのだろうが、論理的にそう思えないところが数々あるため、小説として読んでしまうと少し辛いかもしれない。著者の実質的な所有者であるドクターフリントがストーカー気質で精神的な嫌がらせはしつくすのに、力尽くでということはしないという不思議な性格。また、著者が、祖母の家の屋根裏で7年間過ごすというのはすさまじい。実際にあったことということに驚くしかない。

奴隷の売買や相続による所有者の変更や、母が奴隷の場合は子供も奴隷になるという法律、自由州に逃げたとしても連れ戻される法律、いろいろな形で絡まり合い、読みどころの多い本である。

2014年8月6日水曜日

データベースを使う(3)

データベースを使う(1)で作ったテーブルに、馬のデータを差し込んで行く。普通にSQLのinsertを使えばいいだけ。executeUpdate()を使えば、Primary Keyが同じ場合はエラーでなく、上書きされる。これをYahoo競馬から取得したデータをすべてデータベースに登録し直し。これで、使いやすくなったはず。

public boolean insert(Horse horse, int term, int place, int day, int rnum, Date date) {
try {
final String sql = "insert into Horse values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
final PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, horse.name);
ps.setInt(2, term);
ps.setInt(3, place);
ps.setInt(4, day);
ps.setInt(5, rnum);
ps.setDate(6, new java.sql.Date(date.getTime()));
ps.setInt(7, horse.frame);
ps.setInt(8, horse.number);
ps.setInt(9, horse.order);
ps.setInt(10, horse.time);
ps.setInt(11, horse.popularity);
ps.setInt(12, horse.weight);
ps.setFloat(13, horse.loadweight);
ps.setFloat(14, horse.threefurlong);
ps.setFloat(15, horse.odds);
ps.setString(16, horse.getPassorder());
ps.executeUpdate();
} catch (SQLException se) {
for (SQLException e = se; e != null; e = e.getNextException()) {
TLog.e("%s: %s%n", e.getSQLState(), e.getMessage());
}
}
return true;

}

2014年8月4日月曜日

データベースを使う(2)

テーブルを作るかどうかを判定するには、データを取ってみたらいいようだ。
ResultSet rs = null;
try {
final Statement s = conn.createStatement();
rs = s.executeQuery("select count(1) from Horse");
} catch (SQLException se) {
final String state = se.getSQLState();
if (state.equals("42X05")) {
makeTable(conn);
} else {
for (SQLException e = se; e != null; e = e.getNextException()) {
TLog.e("%s: %s%n", e.getSQLState(), e.getMessage());
}
}
} finally {
if (rs != null) try {rs.close();} catch (Exception e) {};

}

makeTableの先は、データベースを使うで書いたcreate tableを呼び出している。

2014年8月3日日曜日

データベースを使う

すべてオンメモリで動かせば、データベースを使う必要はないだろう。と、思ったが、結構でかい。全部読み込んだらエラーが出る。ヒープ領域を大きくすればいいのかもしれないが、すべてをメモリに読み込むのは止めることにした。それにあたり、Json形式でファイルを保存することにしていたが、読み込み時にデータをしぼることを考えれば、やはりデータベースを使うほうがいいだろう。なお、PCではなく、Google Appを使うことを考えたら、BigTableに置き換えることを想定したほうがいいのだが、普通にSQLを使う。JavaではDerbyを使うのがいいようだ。で、ライブラリに、ダウンロードしたderby.jarを追加した。
String CREATEHORSETABLE = 
"create table Horse " + 
"(name varchar(20) NOT NULL, term smallint NOT NULL, " + 
"place smallint NOT NULL, day smallint NOT NULL, " + 
"rnum smallint NOT NULL, date date NOT NULL, frame smallint, " + 
"number smallint, ordernumber smallint, time smallint, " + 
"popularity smallint, weight smallint, loadweight real, " + 
"threefurlong real, odds real, passorder char(20), " + 
"PRIMARY KEY(name, date))";
Connection conn = DriverManager.getConnection("jdbc:derby:/Users/tkydevelop/Documents/workspace/HorseRaceAnalyzer/db;create=true");
Statement s = conn.createStatement();
s.execute(CREATEHORSETABLE);
のようにしてテーブルを作成した。

2014年8月2日土曜日

Jsonで書き込みと読み出し

Yahoo競馬から取得したデータをjson形式でファイルに保存することにした。jsonのライブラリとして、jacksonを使った。Annotations, Streaming API, DataBindのライブラリを追加した。
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
.setVisibility(PropertyAccessor.CREATOR, Visibility.NONE)
.setVisibility(PropertyAccessor.SETTER, Visibility.NONE)
.setVisibility(PropertyAccessor.GETTER, Visibility.NONE)
.setVisibility(PropertyAccessor.IS_GETTER, Visibility.NONE);
om.writeValue(file, list);
writeValueを使うとオブジェクトの属性値がそのまま書き出される。加えて、たとえば、getXXXX的な名前の関数の値も書き出されてしまう。そのため、setVisibilityを使って、それらを書き出さないように変更した。
一方で読み込みをしようとしても、うまくオブジェクトに読み込むことができなかった。仕方ないので、
JsonNode rootNode;
rootNode = om.readTree(new File(filename));
for (JsonNode node: rootNode) {
Race race = new Race();
race.date = new Date(node.get("date").longValue());
のような形で、リストを繰り返しながら読み込んだ。

2014年7月31日木曜日

距離による時間差(2)

距離による時間差の記事で、2013年の京都の芝1200mと2400mの上位3頭平均タイムを示したが、クラスが上なのに遅いタイムの部分があった。これは、馬場が不良のときのデータも含んでいたのが理由のように思われるので、良馬場だけのデータにする。

2013年の京都の芝1200mの上位3頭の平均タイムは、以下である。
新馬(1レース) 1:10:1
未勝利(5レース) 1:09:4
500万下(4レース) 1:08:7
1000万下(7レース) 1:08:5
1600万下(2レース) 1:08:7
オープン(4レース) 1:07:8
GIII (2レース) 1:08:0
で、2400mは以下である。
未勝利(1レース) 2:26:2
500万下(4レース) 2:27:4
1000万下(4レース) 2:27:2
1600万下(3レース) 2:25:1
GII(2レース) 2:24:0

未勝利と500万下の差は、0.7秒と-1.2秒、
500万下と1000万下の差は、0.2秒と0.2秒、
1000万下と1600万下の差は、-0.2秒と2.1秒
である。少しはましになったが、やっぱり、きれいな差にはなっていない。スピード指数で比較するのは、難しいのではないか。