hatenaブログのカテゴリについて

CM曲「iPhone 5s Powerful篇」Pixies Gigantic ピクシーズ - MUSIC NET
このiPhone5sのCMめちゃめちゃいいですね。
Pixiesのベースラインも紹介されるアプリもかっこいい。
(2つ目のテルミン風に演奏するのもGarageBand?)
さっきからPixies一色です。
 
 

さて本題

今日もどうでもいい話なんですが
このブログではhatena blogのカテゴリモジュールの表示をタグクラウドっぽく表示させるようにしています。
でもblogの内容に偏りがあり過ぎて全然それっぽくないなと思って
ちょっと手を入れました。ハードコーディングなので改悪ですが。
 
修正前
misc/hatena/tag_clouds/tag_clouds_on_hatena_v1.js at master · rochefort/misc
f:id:rochefort:20140510005324p:plain

修正後
misc/hatena/tag_clouds/tag_clouds_on_hatena_v2.js at master · rochefort/misc
f:id:rochefort:20140510005334p:plain

 
参考:
hatenaブログのカテゴリをタグクラウドちっくに表示する - rochefort's blog
 
 

そもそもどんぐらい偏っているのか

というのが気になったのでスクリプトを書いてみました。

実行結果

下部のグラフっぽいのは、縦軸にblogの記事数、横軸にカテゴリ数を*印で配置したものなのですが
10個の記事に満たないカテゴリがほとんどです。7割を占めています。
一方で上位5%のカテゴリを見てみると、ほとんどrailsrubyの話しかしていないということになりました。
可視化おもしろい。

http://rochefort.hatenablog.com/
number of categories              :    81
number of articles linked category:   982
top 5% categories                 :
  rails:   220
  ruby :   184
  雑記 :    82
  mac  :    68
--------------------------------------------------------------------------------
y axis: number of articles
x axis: number of categories

 220|*(1)
 210|
 200|
 190|
 180|*(1)
 170|
 160|
 150|
 140|
 130|
 120|
 110|
 100|
  90|
  80|*(1)
  70|
  60|*(1)
  50|
  40|
  30|
  20|******(6)
  10|********(8)
   0|***************************************************************(63)

ソース

# coding: utf-8
require 'mechanize'

module Hatena
  module Blog
    class Stat
      def initialize(url)
        @agent = Mechanize.new
        @url = url
        @categories = nil
      end

      def show
        @categories ||= scrape_categories
        puts @url
        puts "number of categories              : %5d" % @categories.size
        puts "number of articles linked category: %5d" % @categories.values.inject(&:+)
        puts 'top 5% categories                 :'
        show_top5_percent_categories
        puts '-' * 80
        show_distribution_by_category
      end

      # カテゴリ毎の分布を表示
      def show_distribution_by_category()
        @categories ||= scrape_categories
        draw_distribution
      end

      private
        def scrape_categories
          page = @agent.get(@url)
          category_elms = page.search(".hatena-module-category ul li a")
          elm_to_hash(category_elms)
        end

        def elm_to_hash(category_elms)
          categories = {}
          category_elms.map do |c|
            if c.text.strip.match(/(.+) \((\d+)\)/)
              categories[$1] = $2.to_i
            end
          end
          categories
        end

        def draw_distribution
          puts 'y axis: number of articles'
          puts 'x axis: number of categories'
          puts

          max_scale = @categories.values.max / 10

          distribution = {}
          (0..max_scale).step do |n|
            distribution[n] = @categories.values.select {|x| x >= n*10 && x < (n+1)*10}.size
          end
          distribution.reverse_each do |k,v|
            output = "% 4d|" % (k.to_i * 10)
            output << '*' * v
            output << "(#{v})" if v > 0
            puts output
          end
        end

        def show_top5_percent_categories
          top5_num = (@categories.size * 0.05).floor
          top_categories = @categories.sort_by{|k,v| v}.reverse[0,top5_num]
          max_key_size = 0
          top_categories.each do |k,v|
            key_size = k.ascii_only? ? k.size : k.size * 2
            max_key_size = [max_key_size, key_size].max
          end
          top_categories.each do |k,v|
            if k.ascii_only?
              puts "  #{k.ljust(max_key_size)}: %5d" % v
            else
              spaces = ' '*(max_key_size - k.size * 2)
              puts "  #{k}#{spaces}: %5d" % v
            end
          end
        end
    end
  end
end

if $0 == __FILE__
  hb = Hatena::Blog::Stat.new('http://rochefort.hatenablog.com/')
  # hb.show_distribution_by_category
  hb.show
end