SEOワードの有効性と関係性を視覚化してみる

SEOワードの有効性と関係性を視覚化してみる

自作したWebShopCreatorですが、数名の方に見て頂いたところ「わかりずらい」との評価を頂いてしまいました。
どうやら『ビジュアル面』がネックの様で、反省しきりです。
まぁね、最近グラフ系の記事が多いのはそんな理由からなんですけど、今回は数字ではなく【文字列の視覚化】についてです。

文字列の視覚化の目的

まずは判断したい事を明確にしてみます。

  • キーワードの繋がり ※サジェストとか
  • キーワードの有効性 ※集客数とか

ECにおいてどころか、どんなサイトでもSEO対策はこの2つに集約されていると思います。
その上で上手に文章を書くと言うね。

文字列の関係性を表現するネットワーク図

 <= こんなヤツ ※D3.jsで描いてみた

ネットワーク図を描くライブラリは結構色んなものがありました。
代表格は『NetworkX』『D3js』の2つ。個人的にはD3jsの方が理解しやすかったです。

多分今後もいろいろ弄って行くと思うので、備忘録リンク大量投下しておきます。

NetworkXでQiitaのタグ関係図を描く
Pythonのnetworkxで簡単にネットワーク分析
グラフの探索※大東文化大学
Pythonでネットワークを分析・可視化しよう!必要手順まとめ(NetworkX)
D3.jsを使ってかっこいい相関図を書きたい

ビジュアルで有効キーワードわかるWordCloud

 <=こんなヤツ

単語の有効性をポイント化しそのポイントを文字の大きさとして表現します。
この表現方法もD3.jsライブラリにある。うん、D3.jsは凄い。

NetworkXでのWeb出力とD3.jsのWeb出力

リンクを見て頂くと解るかもしれませんが、私はNetworkXでネットワーク図描こうと思ってました。
Python=NetworkX みたいなイメージが先行したからなんですけど、私の表現したい場所はWEBのページ内。
これが課題となり最終的にはD3.jsで表現しようとこちらに落ち着きました。

NetworkXにはWEB側への表現方法がない

具体的にはHTML化の方法です。描写した後に画像化して読み込むと言う流れで表現する事になります。
これを「JavaScriptで描写してしまえ」と言うのがD3.jsになります。

ネットワーク図に利用したスクリプト(具体例)

   <script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
    <script>
    var nodes = [ {id:0, label:"フライパン",group:1},
                  {id:1, label:"大きい",group:1},
                  {id:2, label:"深型",group:2},
                  {id:3, label:"32cm",group:2},
                  {id:4, label:"小さい",group:2},
                  {id:5, label:"軽い",group:1},
                  {id:6, label:"くっつかない",group:1},
                  {id:7, label:"ティファール",group:1},
                  {id:8, label:"マイヤー",group:1},
                  {id:9, label:"軽量",group:1},
                  {id:10, label:"調理道具",group:1},
                  {id:11, label:"鍋",group:1},
                  {id:12, label:"揚げ物",group:1}
                ];
    var links = [ {source:0, target:1,weight:1},
                  {source:0, target:2,weight:12},
                  {source:1, target:3,weight:1},
                  {source:0, target:4,weight:1},
                  {source:0, target:5,weight:1},
                  {source:0, target:6,weight:1},
                  {source:0, target:7,weight:1},
                  {source:0, target:8,weight:1},
                  {source:5, target:9,weight:1},
                  {source:10, target:0,weight:1},
                  {source:10, target:11,weight:1},
                  {source:11, target:12,weight:1},
                  {source:12, target:1,weight:1},
                  {source:12, target:4,weight:1},
                ];

if ($('#keyword_networks').length) {
    var w = 1000;
    var h = 500;
    // svg領域の作成
    var svg = d3.select("body").append("svg").attr({width:w, height:h});

   // forceLayout自体の設定はここ。ここをいじると楽しい。
   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkStrength(0.1)
                 .distance(50) // node同士の距離
                 .friction(0.9) // 摩擦力(加速度)的なものらしい。
                 .charge(-300) // 寄っていこうとする力。推進力(反発力)というらしい。
                 .gravity(0.2) // 画面の中央に引っ張る力。引力。
                 .theta(0.8)
                 .alpha(0.1)
                 .start();

    // link線の描画(svgのline描画機能を利用)
    var link = svg
                  .selectAll(".line")
                  .data(links)
                  .enter()
                  .append("line")
                  .attr("class", "link")
                  .style({"stroke": "#ccc",
                          "stroke-width": function(d) { return Math.sqrt(d.weight);} });

    var node = svg.selectAll("circle")
                  .data(nodes)
                  .enter()
                  .append("circle")
                  .attr({
                    // せっかくなので半径をランダムに※半径の大きさをWeightにゆだねる事で重要度に合わせた径になる。
                    r: function(d) { return Math.sqrt(d.weight*100);}
                  })
                  //.attr({r: 20,
                  //       opacity: 0.5})
                  .style({"fill": "red"})
                  .call(force.drag)
                  .on("click", clicked);

    var label = svg.selectAll('text')
                    .data(nodes)
                    .enter()
                    .append('text')
                    .attr({"text-anchor":"middle",
                           "fill":"white"})
                     //文字サイズの変更はココ
                    .style({"font-size":11})
                    .text(function(d) { return d.label; });

  node.append("circle")
      .attr("r","5");

  node.append("text")
      .attr("dx", 12)
      .attr("dy", ".35em")
      .text(function(d) { return d.name });

    function clicked(d) {

    d3.selectAll(".selected").classed("selected", false);
    d3.selectAll(".conected").classed("conected", false);
    d3.selectAll("line").classed("linkSelected", false);

    d3.select(this).classed("selected", true);

    d3.selectAll("line")
      .filter(function(vi, i) {
        if(d == vi.source) {
          node.each(function(vj, j) {
            if(vi.target == vj) d3.select(this).classed("conected", true);
          });
          return true;
        } else if(d == vi.target) {
          node.each(function(vj, j) {
            if(vi.source == vj) d3.select(this).classed("conected", true);
          });
          return true;
        }
      }).classed("linkSelected", true);
}


    // tickイベント(力学計算が起こるたびに呼ばれるらしいので、座標追従などはここで)
    force.on("tick", function() {
      link.attr({
        x1: function(d) { return d.source.x; },
        y1: function(d) { return d.source.y; },
        x2: function(d) { return d.target.x; },
        y2: function(d) { return d.target.y; }
      });
      node.attr({
        cx: function(d) { return d.x; },
        cy: function(d) { return d.y; }
      });
      // labelも追随するように
      label.attr({
        x: function(d) { return d.x; },
        y: function(d) { return d.y; }
      });
    });
  }
</script>

現状の課題は【散らばり】と【まとまり】をうまく操作出来ていない事。上の画像が見難いのはこれが原因です。
45行目~48行目辺りを調整していくと関係の強さや塊のコントロールが出来るみたいです。

ワードクラウドに利用したスクリプト(具体例)

<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script src="/static/build/js/d3.layout.cloud.js"></script>
<script>
  wordcloud_list = [
        {
            "word": "日本",  # 表示される文字
            "property": "country",  # 種別
            "count": 100,  # prefecture=word
        },
        {
            "word": "America",
            "property": "country",
            "count": 30,
        },
        {
            "word": "北京",
            "property": "country",
            "count": 20,
        },
        {
            "word": "中国",
            "property": "country",
            "count": 40,
        },
        {
            "word": "イタリア",
            "property": "country",
            "count": 70,
        },
        {
            "word": "ドイツ",
            "property": "country",
            "count": 10,
        },
        {
            "word": "フランス",
            "property": "country",
            "count": 10,
        }
    ]

var TARGET_ELEMENT_ID = '#wordcloud'; // 描画先

// Word Cloudを作成して、id="wordcloud"の要素に描写する関数
function draw_wc(data){
  var random = d3.randomIrwinHall(2); // アーウィンホール分布
  var countMax = d3.max(data, function(d){ return d.count} );
  var sizeScale = d3.scaleLinear().domain([0, countMax]).range([10, 100])
  var words = data.map(function(d) {
    return {
    text: d.word,
    property: d.property,
    size: sizeScale(d.count) //頻出カウントを文字サイズに反映
    };
  });
  d3.layout.cloud().size([w, h])
    .words(words)
    .rotate(function() { return (~~(Math.random() * 6) - 3) * 0; })//角度: 0 の箇所を30に変えると30度刻みの斜め描きが入る
    .font("Impact")
    .fontSize(function(d) { return d.size; })
    .on("end", draw) //描画関数の読み込み
    .start();
  return words;

  // wordcloud 描画実行部分
  function draw(words) {
    d3.select(TARGET_ELEMENT_ID)
      .append("svg")
        .attr("class", "ui fluid image") // style using semantic ui
        .attr("viewBox", "0 0 " + w + " " + h )  // ViewBox : x, y, width, height
        .attr("width", "100%")    // 表示サイズの設定
        .attr("height", "100%")   // 表示サイズの設定
      .append("g")
        .attr("transform", "translate(" + w / 2 + "," + h / 2 + ")")
      .selectAll("text")
        .data(words)
      .enter().append("text")
        .style("font-size", function(d) { return d.size + "px"; })
        .style("font-family", "ヒラギノ角ゴ Pro W3.otf")
        .style("fill", function(d, i) { return d3.schemeCategory10[i % 10]; })
        .attr("text-anchor", "middle")
        .attr("transform", function(d) {
          return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
        })
        .text(function(d) { return d.text; });
  }
}

// 初回実行はDjangoから受け取ったデータを引数に、関数draw_wcを呼び出し
var data_list = [
  {% for td in wordcloud_list %}
  {
    "word": "{{td.word}}",
    "count": {{td.count}},
    "property": "{{td.property}}",
  },
{% endfor %}
]
words = draw_wc(data_list)
</script>

まとめ

表現方法としてはビジュアルでぱっと見で判断できるというのはとても面白いし理解に易いのだと思います。
私はプログラム作る側なので、どうしても「これをどうすんの?」が先行してしまうのですが、それが見やすさ/判り易さの足を引っ張っているのでしょう。

実際に商品名生成を自動化しようとしたらこんな描写よりも「必要なものと不要なもの、絶対つけてはいけないモノの篩分けルール」の方が重要なので、私の脳みそはそっち側にばかり捉えられていたようです。
人に意見聞くと言うのは、やっぱり絶対に必要な事ですね。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください