github の puhch card みたいなのを自分で生成したいと思って cairo を使って描いてみた。
ChangeLog が元データなので、日本時間である。
また、円の面積が ChangeLog の項目数に比例する。
stat-changelog.rb:
#!/usr/bin/ruby1.8
# usage:
# ./stat-changelog.rb ruby/{ChangeLog,doc/ChangeLog-1.9.3,doc/ChangeLog-1.8.0}
require 'time'
WDAYS = %w[Sun Mon Tue Wed Thu Fri Sat]
MONTHS = %w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec]
h = Hash.new(0)
ARGF.each {|line|
if /\A((?:#{Regexp.union WDAYS})\s+(#{Regexp.union MONTHS})\s+\d+\s+\d\d:\d\d:\d\d\s+\d+)/o =~ line
t = Time.parse($1)
k = [t.wday, t.hour]
h[k] += 1
end
}
max = 0
h.each_value {|n|
max = n if max < n
}
if max == 0
puts 'no data'
exit
end
require 'cairo'
require 'pango'
format = Cairo::FORMAT_ARGB32
radius = 20
diameter = radius * 2
width = diameter * (1+24)
height = diameter * (1+7)
surface = Cairo::ImageSurface.new(format, width, height)
context = Cairo::Context.new(surface)
# background
context.set_source_rgb(1, 1, 1) # white
context.rectangle(0, 0, width, height)
context.fill
context.set_source_rgb(0, 0, 0) # black
hour_layouts = []
0.upto(23) {|hour|
layout = context.create_pango_layout
layout.text = "%02d" % hour
layout.width = diameter * Pango::SCALE
layout.alignment = Pango::ALIGN_CENTER
hour_layouts << layout
}
max_height = hour_layouts.map {|layout| layout.size[1] }.max
hour_layouts.each_with_index {|layout, hour|
context.save {
context.translate((1+hour) * diameter, radius - max_height / Pango::SCALE / 2)
context.show_pango_layout(layout)
}
}
wday_layouts = []
0.upto(6) {|wday|
layout = context.create_pango_layout
layout.text = WDAYS[wday]
layout.width = diameter * Pango::SCALE
wday_layouts << layout
}
max_width = wday_layouts.map {|layout| layout.size[0] }.max
max_height = wday_layouts.map {|layout| layout.size[1] }.max
wday_layouts.each_with_index {|layout, wday|
context.save {
context.translate(radius - max_width / Pango::SCALE / 2, (1+wday) * diameter + radius - max_height / Pango::SCALE / 2)
context.show_pango_layout(layout)
}
}
0.upto(6) {|wday|
0.upto(23) {|hour|
n = h[[wday, hour]]
r = Math.sqrt(n) / Math.sqrt(max) * radius
context.arc((1+hour) * diameter + radius, (1+wday) * diameter + radius, r, 0, 2 * Math::PI)
context.fill
}
}
surface.write_to_png("changelog.png")
system("feh changelog.png")Debian Bug #596492 - The unit of the amount of CPU time for RLIMIT_RTTIME
R の ggplot2 というのが良いらしいと聞いたので試してみる。(データは Wikipedia から)

bmi-gnu_r-ggplot.R:
library(ggplot2)
names <- c("唯", "澪", "律", "紬", "梓", "さわ子", "和", "憂")
heights <- c(156, 160, 154, 157, 150, 165, 158, 154)
weights <- c(50, 54, 48, 53, 46, 56, 52, 50)
data <- data.frame(name=names, height=heights, weight=weights)
bmi <- function(b) function(w) sqrt(w/b)*100
x = seq(40,60,1)
p <- ggplot() + xlim(40,60) + ylim(140,180)
p <- p + geom_text(aes(x=data$weight, y=data$height, label=data$name))
p <- p + xlab("体重[kg]") + ylab("身長[cm]")
p <- p + geom_line(aes(x=x, y=bmi(18.5)(x)))
p <- p + geom_line(aes(x=x, y=bmi(25)(x)))
p <- p + geom_line(aes(x=x, y=bmi(30)(x)))
p <- p + geom_line(aes(x=x, y=bmi(40)(x)))
p <- p + geom_text(aes(x=45,y=170,label="低体重"))
p <- p + geom_text(aes(x=58,y=170,label="普通"))
p <- p + geom_text(aes(x=58,y=145,label="前肥満"))
print(p)
みなさん普通で健康的ですな。
それはそれとして、曲線を端まで描く方法がわからない。
coord_cartesian() を使えばいいのか。

bmi-gnu_r-ggplot-2.R:
library(ggplot2)
names <- c("唯", "澪", "律", "紬", "梓", "さわ子", "和", "憂")
heights <- c(156, 160, 154, 157, 150, 165, 158, 154)
weights <- c(50, 54, 48, 53, 46, 56, 52, 50)
data <- data.frame(name=names, height=heights, weight=weights)
bmi <- function(b) function(w) sqrt(w/b)*100
xx = seq(40,60,1)
p <- ggplot()
p <- p + coord_cartesian(xlim=c(40,60), ylim=c(140,170))
p <- p + scale_y_continuous(breaks=seq(140,180,by=10))
p <- p + geom_ribbon(aes(x=xx, ymin=bmi(18.5)(xx), ymax=180), fill="skyblue", alpha=0.5)
p <- p + geom_ribbon(aes(x=xx, ymin=bmi(25)(xx), ymax=bmi(18.5)(xx)), fill="lightgreen", alpha=0.5)
p <- p + geom_ribbon(aes(x=xx, ymin=bmi(30)(xx), ymax=bmi(25)(xx)), fill="yellow3", alpha=0.5)
p <- p + geom_ribbon(aes(x=xx, ymin=130, ymax=bmi(30)(xx)), fill="orangered", alpha=0.5)
p <- p + geom_text(aes(x=data$weight, y=data$height, label=data$name))
p <- p + xlab("体重[kg]") + ylab("身長[cm]")
p <- p + geom_line(aes(x=xx, y=bmi(18.5)(xx)))
p <- p + geom_line(aes(x=xx, y=bmi(25)(xx)))
p <- p + geom_line(aes(x=xx, y=bmi(30)(xx)))
p <- p + geom_line(aes(x=xx, y=bmi(40)(xx)))
p <- p + geom_text(aes(x=44,y=163,label="低体重(BMI: 〜18.5)"))
p <- p + geom_text(aes(x=44,y=143,label="普通(BMI: 18.5〜25)"))
p <- p + geom_text(aes(x=56,y=143,label="前肥満(BMI: 25〜30)"))
print(p)
ぐらでーしょん

bmi-gnu_r-ggplot-3.R:
library(ggplot2)
names <- c("唯", "澪", "律", "紬", "梓", "さわ子", "和", "憂")
heights <- c(156, 160, 154, 157, 150, 165, 158, 154)
weights <- c(50, 54, 48, 53, 46, 56, 52, 50)
data <- data.frame(name=names, height=heights, weight=weights)
bmi <- function(b) function(w) sqrt(w/b)*100
xx <- seq(40,60,0.2)
yy <- seq(140,170,0.2)
bmi_w <- rep(xx, length(yy))
bmi_h <- rep(yy, rep(length(xx), length(yy)))
bmi_bmi <- bmi_w / (bmi_h/100)^2
bmi_table <- data.frame(w=bmi_w, h=bmi_h, BMI=bmi_bmi)
p <- ggplot()
p <- p + coord_cartesian(xlim=c(40,60), ylim=c(140,170))
p <- p + scale_y_continuous(breaks=seq(140,180,by=10))
p <- p + scale_fill_gradient2(low="blue", mid="green", high="red", midpoint=22)
p <- p + geom_tile(data=bmi_table, aes(x=w, y=h, z=BMI, fill=BMI, alpha=0.7))
p <- p + geom_text(aes(x=data$weight, y=data$height, label=data$name))
p <- p + xlab("体重[kg]") + ylab("身長[cm]")
p <- p + geom_line(aes(x=xx, y=bmi(18.5)(xx)))
p <- p + geom_line(aes(x=xx, y=bmi(25)(xx)))
p <- p + geom_line(aes(x=xx, y=bmi(30)(xx)))
p <- p + geom_line(aes(x=xx, y=bmi(40)(xx)))
p <- p + geom_text(aes(x=44,y=163,label="低体重(BMI: 〜18.5)"))
p <- p + geom_text(aes(x=44,y=143,label="普通(BMI: 18.5〜25)"))
p <- p + geom_text(aes(x=56,y=143,label="前肥満(BMI: 25〜30)"))
print(p)
凡例に alpha の 0.7 が出てくるのはわからない。
[latest]