サイトの導線を評価する:応用編2


前回は、ページ間の遷移数を距離として理解し、MDSを用いて2次元グラフ上にマッピングする方法を検討しました。今回は同じデータを用いて、違った見方を考えてみます。

よく見ると

今回も前回同様、出発点はページの遷移を表したこの表です。

この表をよーく見てみます。そして気づきます。よくよく考えてみると、ページ間の遷移というのは、Web上のリンクをクリックした数だということを。当然、ログデータなので、エラー値は存在します(タブブラウザを使った場合など)。しかし、多くの場合、ページ間を遷移するということはリンクをクリックしたこと(もしくはブラウザの戻るボタン)になります。ということは、リンク自体を評価してみれば良いのではないでしょうか?

リンクの評価

リンクの有無のみを考えた場合、数値の大小は不要です。実際のWebサイトでリンク構造をチェックしてみて、同じようなページ遷移表を作成しましょう。

では、リンクを評価してみましょう。リンクの評価といえばおなじみのページランクです。
ページランクとは、たくさんリンクされているところからリンクされているリンクが偉いという考え方です。
アルゴリズム自体はそれほど複雑ではありません。アルゴリズムの詳しい解説は専門家に任せるとして、簡単な考え方だけ記しておきます。

まずは平等に考えます。全てのページに同じ確率だけ訪れると考えます。
100人いたら、それぞれのページに14.3人ずつ訪れます。
index.htmlに訪れた14.3人中の1/3(4.8人)はa.htmlに、同じく1/3はb.html、残りの1/3はc.htmlに遷移します。
a.htmlにはリンクが一個しかないので、14.3人全てindex.htmlに遷移します。
b.htmlには2個リンクがあるので、14.3人の1/2がindex.htmlへ、残りの1/2がb_1.htmlへ遷移します。

これを全てのページで繰り返すと、サイト来訪者100人が一回のリンククリック終了時に次の割合でページにいることになります。
index.html:54.8人
a.html:4.8人
b.html:4.8人
b_1.html:7.1人
c.html:1.9人
c_1.html:4.8人
c_2.html:4.8人

ここからもう1回同じ作業をすると、2回目終了時には

index.html:25.4人
a.html:18.3人
b.html:18.3人
b_1.html:2.4人
c.html:23.0人
c_1.html:6.3人
c_2.html:6.3人

となります。これを何度も何度も繰り返すと一定の値に収束していきます。
今回のプログラムでは、ほとんど変わらなくなったか、1000回ループしたら終了としています。
これはマルコフ連鎖モデルなので、行列の固有値問題を解決することでも解くことはできますが、面倒なので上のようにしています。恐らく本家Googleもそうしているのではないかと思っています。
最終的には次のような値になります。(100人中)
index.html:37.5人
a.html:12.5人
b.html:12.5人
b_1.html:6.2人
c.html:18.8人
c_1.html:6.2人
c_2.html:6.2人

確率モデルになるので、合計で1になるようにすると、
index.html:0.37
a.html:0.13
b.html:0.13
b_1.html:0.06
c.html:0.19
c_1.html:0.06
c_2.html:0.06

となります。これがPageRankです。
コードはrubyで書きました。
結構前に書いたものなので、内容はちょっと忘れています。

require 'csv'

LOOPMAX = 1000
FILE = "matrix.csv"


class Array
  def sum
    s = 0.0
    n = 0
    self.each do |v|
      next if v.nil?
      s += v.to_f
      n += 1
    end
    return s
  end
 end




class PageRank
  def initialize(file)
    @file = file
    @matrix = self.change_to_float
    @mat_shr = self.tr_share(@matrix)
    @rank = self.init_shr
  end
  attr_accessor :file, :matrix, :mat_shr, :rank

#change to integer from file data
  def change_to_float
    arr = Array.new
    @file.each do |x|
      tmp = Array.new
      x.each do |y|
        tmp << y.to_f
      end
      arr << tmp
    end
    return arr
  end

#make share matrix transposed
  def tr_share(mtx)
    arr = Array.new
    mtx.each do |x|
        tmp = Array.new
      if (x.sum != 0)
        x.each do |y|
          tmp << y / x.sum
        end
      else
        s = x.size.to_f
        tmp = Array.new(s){|i| 1 /s }
      end
      arr << tmp
    end
    return arr.transpose
  end

#make initial PageRank
  def init_shr
    s = @matrix.size.to_f
    arr = Array.new(s){|i| 1 / s}
  end

  #step for making PageRank
  def step
    tmpArr =Array.new
    i = 0
    @mat_shr.each do |x|
      j = 0
      tmpArr[i] = Array.new
      x.each do |xb|
        tmpArr[i] << xb * @rank[j]
      j += 1
      end
      i += 1
    end

    @rank = []
    tmpArr.each do |y|
      @rank << y.sum
    end
  #  p @rank
    return @rank
  end

  # do Markov chain
  def markov
    @mArray = Array.new
    @mArray << self.step

    1.upto(LOOPMAX) do |i|
        @mArray << self.step
        j = self.decide(@mArray[i-1], @mArray[i])
        if(j < 1e-10)
            break
        end
    end
    return @mArray
  end

  #decide the end point
  def decide(arr1, arr2)
    tmp = Array.new
    i = 0
    arr1.each do |x|
     tmp << (x - arr2[i])**2
     i += 1
    end
#   p tmp.sum
    return tmp.sum
  end
end

f = CSV.read(FILE)
m = PageRank.new(f)
p m.markov.last

遷移データへの応用

さて、勘のいい人は既に気づいていると思いますが、このモデルにデータが0,1である必要性はありません。GoogleのPageRankはリンクの有無だけを判断しているので、0,1でモデル化していますが、僕らにはログデータがあります。ログの実際の遷移のデータから次にどのページへ遷移するのかという確率を出すことができます。

例えば、index.htmlから、どこかのページへ遷移した回数は合計で94回です。そのうち、20回はa.html、30回はb.html、35回はc.htmlです。つまり100人いるとすると、
21.3人がa.html
32.0人がb.html
37.2人がc.html
へ遷移したと見なすことができます。

これを繰り返すと、以下のようなPageRankになります。
index.html:0.33
a.html:0.08
b.html:0.11
b_1.html:0.04
c.html:0.19
c_1.html:0.07
c_2.html:0.05
out:0.13

これで、ページ遷移をベースとしたPageRankも出すことができるようになりました。

もう一つ別のPageRankを

これだけで終わってもいいのですが、ここまで来たらもう一つ別のPageRankも出してみましょう。それは設計の意図に基づいたPageRankです。
各ページのリンクはリンクの位置や見せ方などで、特にクリックして欲しいリンクとそうではないリンクを区別しています。ページの設計者が思うリンクの重みは0,1ではありません。各リンクを段階別に重要度を付けていきます。とりあえず、今回は全てのリンクを3段階で重み付けしました。

このデータからPageRankを算出すると、下記のような結果となりました。

index.html:0.34
a.html:0.19
b.html:0.08
b_1.html:0.06
c.html:0.11
c_1.html:0.05
c_2.html:0.05
out:0.13

データの比較

これで、「リンク構造」をベースとしたPageRank、「設計の意図」をベースとしたPageRank、「実際のログデータ」をベースとしたPageRankの3種類のPageRankができました。
これらのデータを比較してみましょう。

データを見てみると、a.htmlが設計の意図では大きなPageRankだったのに対し、実際のログデータではそれほど大きな値とはなっていません。つまり設計の意図に反して、あまりa.htmlへ遷移していないということがわかります。ページ遷移表をよく見ると、index.htmlからa.htmlへの遷移が想定よりも少ないと考えられます。コンテンツ自体に魅力がないのか、リンクのファインダビリティが悪いのかはわかりませんが、ここは改善の余地があるといえるでしょう。

というような分析ができます。とても素敵です。
PageRankを用いることで、それぞれのリンク評価をシンプルなデータとして評価することができます。PageRankが高ければ高いほど、サイト内で多くの有効な遷移を獲得していると考えられます。重要と考えているページが高いPageRankとなっているかどうかを見ることで、サイトの導線を評価することが可能になります。

Leave a Reply