読者です 読者をやめる 読者になる 読者になる

後楽園でマーケティングを研究する学生のブログ

マーケティング、KPI管理、グロースハック、ときどきPRML

PRML第1章と併せて読みたい記事まとめ

始めに

PRMLの第1章を読んでいて、内容が分からない部分を適宜、先人の記事を参考にさせていただいてました。

今から紹介する記事を併用して読み進めていけば、より理解が深まる記事にしていきたいと思います。

それに自分が復習するときにまた記事を調べなくていいですからね。

ターゲット

大学で統計学と確率の授業を履修していた人
機械学習って名前だけ流行してるけど実際何やってるのか気になる人
これから機械学習を使って何か作ってみたい人

1章 序論

P1 ルールと機械学習のアプローチの違いを理解する。

P2 教師あり学習と教師なし学習の違いをさらに理解する。

おまけ : p3 強化学習をもう少し勉強してみたい人へ ブロック崩しの動画とか有名です。

1.1 多項式曲線フィッティング

P9 正則化(正規化ではない)で出てくるノルム
{ \displaystyle
 \it ||\bf w  \it||^2 \equiv \bf{w}^{\it T}{\bf w} \it = w_0^{2} + w_1^{2} + \cdots + w_M^{2}
}
ノルム - Wikipedia

1.2 確率論

P14 ベイズの定理についてもう少し詳しく知る

1.2.4 ガウス分布

P25 2次のモーメントとは モーメント (確率論) - Wikipedia

期待値μは, 1次のモーメント { \displaystyle m_1} に等しい。分散 σ2 は、2次のモーメント m1, m2 で表すことができる。

1.2.5~1.2.6

こちらの記事がほとんど網羅しており、とても参考になりました。

1.3 モデル選択

確認用集合とテスト集合の違い

AICBICの違い

1.4 次元の呪い

次元の呪いは「サクサクメロンパン問題」とも言うようです。 高次元空間では体積は表面に集まると言われてもイメージがまるで湧きませんでしたが、これを読むとなんとか飲み込むことができました。

1.5 決定理論

決定理論の具体的な手法例 決定理論<決定理論とゲームの理論<オペレーションズ・リサーチ<Web教材<木暮仁

1.5.5 回帰のための損失関数

損失関数の種類 交差エントロピーと二乗誤差がよく使われるみたいですね。

1.6 情報理論

ラグランジュの未定乗数法
付録より分かりやすかったです。

カルバックライブラーダイバージェンス機械学習での意義

最後に

読み始める前のイメージよりPRMLは読みやすいなと思いました。多分1章だからだと思いますが。 2章はまた読み終わったらまとめます。

機械学習について知るためにPRML読み始めました

なんで読むのか

機械学習って実際なにかよくわからないからです。 あとは、機械学習の技術を使って、購買予測や売り上げ要因分析の精度をあげれるのではないかと考えているからです。

他には、大学在学中にアカデミックなこともある程度勉強し、理系出身としてきちんと論文も書けるようになっておきたいからです。 今はTeXの書き方すらも忘れてますが。。。

第一印象

数学の教科書。 数式がかなり出現してきて、僕は、2時間で10ページしか進めません。こつこつやります。

最低でも、数学3Cの知識と共分散、標準偏差などの概念は理解できておいたほうが読みやすいです。 あと数式を見たときに拒否反応出る人には向いてません。すぐに閉じましょう。

注意点

わからないところはなくすくらいの勢いで勉強すること。 一度飛ばし始めると飛ばす癖がついて、わかった気のまま理解できず最終ページを迎えることになるでしょう。

自分の中できちんと区切りをつけながら読み進めていくこと。 それぞれの説明を分かりやすくするために、説明の流れが断片的であることがあることがある。 どこまでがどの説明なのか、どこまでが繋がっているのか自分で判断しながら読み進めていく。

バスケット分析の難しさ

以前インターン先で、スーパーのPOSデータを使って併売実験を行ったのですが、バスケット分析で出した結果、あまりいい結果にならなかったので注意点というか、分析をする上で大事だと感じたことをメモります。

目次

ターゲット

分析に興味のあるかた、始めたばっかの方、lift値ってなんだっけ?という方

バスケット分析とは

バスケット分析とは一言でいうと、よく一緒に買われている商品の組み合わせをみつける分析手法です。
信頼度や支持度やリフト値など聞きなれない言葉が出てきますが、詳しくはこちらの記事と本を参照してください。とても細かく説明されていてとても参考にさせていただきました。
R本は特にRのコードが紹介されていて、パラメータのチューニングの際にとても参考になりました。
商品分析の手法(ABC分析、アソシエーション分析)

リフト値ってなに?

バスケット分析で用いられるリフト値というのは、簡単に言うと、なにもしないより組み合わせて売ることがどのくらい効果があるのかを知る指標となります。
1以上の値であれば効果があると判断できます。
他にも支持度と確信度という指標があり、三つの指標を総合して判断していきます。

バスケット分析の注意点

バスケット分析で併売の効果があると判断できた組み合わせでも実際には併売の効果があまりないことがあります。
それは価格の情報が入っていないことや、商品のカテゴリが異なっていたりすることが考えられます。

例えば、チョコボールゴディバのチョコがよく買われているとします。そのときゴディバのチョコをチョコボールが売っている駄菓子ゾーンの近くに置いて、はたして本当に売れるでしょうか?
どちらかというとゴディバのチョコが置いてある高級菓子ゾーンの近くにチョコボールを置いたほうが売れそうですよね。値段が異なる場合、併売の向きも考える必要があります。
また、お肉と、洗剤がよく買われている組み合わせだとします。そこでお肉の売り場の近くに洗剤を置いたとします。結果はどうでしょうか?
あまり売れなそうですよね。このようにいくらよく買われている組み合わせだとしても併売の効果が出にくいこともあるのです。

因果関係と相関関係

上記のようにバスケット分析上では併売の効果が高いとされた商品でも実際には効果が出なかったというのは、まさしく相関関係でしかないからなんですね。
しかし、相関関係の中には因果関係がしっかりできているものもあるのでものは試しでいっぱい併売をやってみるというのも一つの手かもしれません。
分析手法はあくまで分析なので実践して効果測定まできちんと追っていくことが大切です。

Googleトレンドをスプレッドシートから検索

Googleトレンドで一度にたくさんのキーワードのトレンドを確認したいことがあるのですが、通常5つまでしか同時に検索することができないんです。
そこで今回はスプレッドシートで、検索したいキーワードのトレンドを取得できるような仕組みを作ってみました。

目次

どうやって作るか

スプレッドシートのimportxml()を使ってGoogle トレンドから元のデータを引っ張ってきます。
そのデータからleft(),right(),find()などを使いながらうまいこと処理して、トレンドの数字だけを引っ張ってくることができれば、完成です。

完成系

ちょっと複雑なので先に完成系を貼っておきます。コピーしてご利用ください。
検索数が多い場合はスクレイピングのスピードが下がるので、その場合は時間を空けて試してみてください。

f:id:iwa_k:20170301134702p:plain

作ってみましょう

1.googleトレンドから元データを取得

まずgoogleトレンドの検索結果の元データを取得します。

=importxml("https://www.google.com/trends/fetchComponent?q="&"検索したいキーワード"&"&geo=JP&hl=ja&cid=TIMESERIES_GRAPH_0&export=5")

試しにキーワード"楽天"のトレンドを取得してみました。

Google Trendsvar _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-4401283-1']); _gaq.push(['_setDomainName', 'google.com']); _gaq.push(['_setAllowLinker', true]);_gaq.push(['_setCookiePath', '/trends']);_gaq.push(['_trackPageview', window.location.pathname + window.location.search + window.location.hash]); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); google.load('visualization', '1',{'packages':['corechart']});google.setOnLoadCallback(drawChart); function drawChart() { var chartData = {""columns"":[{""id"":""d"",""label"":""Date"",""type"":""datetime""},{""role"":""annotation"",""type"":""string""},{""p"":{""html"":true},""role"":""annotationText"",""type"":""string""},{""id"":""q0"",""label"":""楽天"",""type"":""number""},{""role"":""annotation"",""type"":""string""},{""p"":{""html"":true},""role"":""annotationText"",""type"":""string""},{""role"":""certainty"",""type"":""boolean""}],""headlineDataPoints"":[],""width"":485,""axisAnnotations"":[{""row"":84,""url"":""//support.google.com/trends/bin/answer.py?hl\u003dja\u0026answer\u003d1383240""}],""rows"":[
[{""v"":new Date(2004, 0, 16, 12, 0),""f"":""2004年1月""},null,null,19,null,null,true],
[{""v"":new Date(2004, 1, 15, 12, 0),""f"":""2004年2月""},null,null,20,null,null,true],
[{""v"":new Date(2004, 2, 16, 12, 0),""f"":""2004年3月""},null,null,19,null,null,true],
[{""v"":new Date(2004, 3, 16, 0, 0),""f"":""2004年4月""},null,null,20,null,null,true],
[{""v"":new Date(2004, 4, 16, 12, 0),""f"":""2004年5月""},null,null,21,null,null,true],
[{""v"":new Date(2004, 5, 16, 0, 0),""f"":""2004年6月""},null,null,20,null,null,true],
[{""v"":new Date(2004, 6, 16, 12, 0),""f"":""2004年7月""},null,null,20,null,null,true],
[{""v"":new Date(2004, 7, 16, 12, 0),""f"":""2004年8月""},null,null,20,null,null,true],
...
[{""v"":new Date(2016, 8, 16, 0, 0),""f"":""2016年9月""},null,null,49,null,null,true],
[{""v"":new Date(2016, 9, 16, 12, 0),""f"":""2016年10月""},null,null,46,null,null,true],[{""v"":new Date(2016, 10, 16, 0, 0),""f"":""2016年11月""},null,null,47,null,null,true],
[{""v"":new Date(2016, 11, 16, 12, 0),""f"":""2016年12月""},null,null,53,null,null,true],
[{""v"":new Date(2017, 0, 16, 12, 0),""f"":""2017年1月""},null,null,48,null,null,true],
[{""v"":new Date(2017, 1, 15, 0, 0),""f"":""2017年2月(集計途中のデータ)""},null,null,46,null,null,true],[{""v"":new 
Date(2017, 2, 16, 12, 
0),""f"":""2017年3月""},null,null,null,null,null,true]],""showHeadlines"":false,""percentData"":false,""colors"":[""#3f85f2""],""height"":230}; 
var htmlChart = new trends.HtmlChart( 'time-chart', chartData.columns, chartData.rows, chartData.headlineDataPoints, null , chartData.showHeadlines,true, chartData.percentData, chartData.colors, chartData.width, chartData.height, chartData.axisAnnotations,true);htmlChart.render(); }  body {margin: 8px}; 

上がトレンドの検索結果となります。
データの概要を簡単に説明すると、2004年からのデータで、100を検索ボリュームの最大値としてトレンド結果を返してくれています。
[{""v"":new Date(2017, 0, 16, 12, 0),""f"":""2017年1月""},null,null,48,null,null,true]
2017年1月のトレンドは48となります。(nullに挟まれている数字)

2.元データから必要なデータを抽出する

取得した元データから必要なデータは、「2017年1月のトレンドは48」というデータのみです。
それらを集めて、下の表のようなものを作っていきます1

キーワード 1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月
楽天 20 40 60 80 20 40 60 80 20 40 60 80
amazon 50 60 80 70 50 60 80 70 50 60 80 70
メルカリ 10 20 40 90 10 20 40 90 10 20 40 90

元データからfind()してleft()やright()で該当の数字だけを取り除く処理はまた別の機会に書きたいと思います2:。
mid()を使うともっとシンプルにかけるよ。などございましたらコメントください。宜しくお願いします。


  1. 表内の数値は僕が独断と偏見で入力した値ですので、きちんと作成したシートで検索し直してください。

  2. 実は疲れてしまったとういうのはここだけの話です。

データベースを構築しよう〜PostgreSQL編〜

データコンペ用のデータベースをPostogreSQLで構築しました。 データ型さえきちんと頭に入れれば簡単に作れました。

作成手順

手順

  1. データベース作成
  2. テーブル作成
  3. データの挿入

今回のゴール

今回はレシートのデータを表にしてみます。 二行目の日本語は補足説明です。実際にはありません。 データベース名はposdata、表の名前はordersとします。

order_id product_id order_price order_amount order_date
レシート番号 商品番号 販売価格 購入量 購入時間
00001 101010 3000 1 2016/3/17 21:08
00002 202020 1000 2 2016/3/17 22:00
00002 303030 2000 1 2016/3/17 22:00
00003 123456 4000 2 2016/3/17 22:30
00004 223456 5000 1 2016/3/17 22:40

基本DB操作

まず表を入れる箱を用意します。 データベース作成

CREATE DATEBASE データベース名;

CREATE DATEBASE posdata;

これで表を入れる箱ができました。 次にテーブルのアウトラインを先に作ります。

テーブル作成

CREATE TABLE テーブル名 (カラム名 データ型,カラム名 データ型,カラム名 データ型,・・・);

CREATE TABLE テーブル名 (order_id text,product_id text,order_price integer,order_amount integer,order_date timestamp with time zone);

こんな感じの表のアウトラインができます。

order_id product_id order_price order_amount order_date
レシート番号 商品番号 販売価格 購入量 購入時間
text text integer integer timestamp with time zone

下の二行は説明のためにあります。実際には一行目しかありません。

最後に中身(データ)を入れていきます。 今回は、CSVファイルからデータを挿入するのですが、PostgreSQL特有のCOPY文を使ってます。

copy テーブル名 from 読み込むCSVの絶対パス;

copy orders from ’ORDER.csv’ WITH(encoding ’SJIS’,format csv,header true);
# with以降は csvのエンコードがshift-jisかつcsvにヘッダーがあったためつけてるオプションになります。

完成ー

order_id product_id order_price order_amount order_date
00001 101010 3000 1 2016/3/17 21:08
00002 202020 1000 2 2016/3/17 22:00
00002 303030 2000 1 2016/3/17 22:00
00003 123456 4000 2 2016/3/17 22:30
00004 223456 5000 1 2016/3/17 22:40

補足

データ型の変更

データ型の設定をミスって値が挿入できない場合は下のコマンドでデータ型を変えることできます。

ALTER TABLE テーブル名 ALTER COLUMN カラム名 TYPE 変更したいデータ型

データ型もっと知りたい!

データ型について知りたい方はこちらのリンクを参照してください。

データ型

textareaのカーソル位置に外部ボタンから値を挿入

本日は決まった言葉をボタンからワンタッチでtextareaに文字を挿入することができる機能を作成したので勉強メモを残しておきます。 スマホではtextareaにfocusがある時にキーボードが出現するのでキーボードを維持したままボタン機能を実装するのが今回の目標でした。 自分なりに使いやすくまとめられたと思ったので参考までにどうぞ。

実装手順

  1. ボタンを用意(display:none)
  2. textareaにfocusされたらボタン出現
  3. textareaからfocusが外れたらボタンを消す
  4. ボタンをおした時はボタンをの表示を維持し、focusを元のtextareaに戻す
  5. プラグインを使いカーソル位置にボタンの値を挿入する

詳しくはコードを参照ください。

jQuery.selectionというプラグインを使いました。 岩崎さんすごいです。この場を借りてお礼を申し上げたいと思います。ありがとうございました!

Copyright © 2010-2014 IWASAKI Koji (@madapaja). http://blog.madapaja.net/ Under The MIT License

index.html

<!DOCTYPE html>
<head>
   <meta charset="utf-8">
   <title>便利ボタン</title>
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
   <style>
        .sharp{
            width: 300px;
            height: 300px;
        }
        #sharp_btns{
            display: none;
            position: fixed;
            bottom: 0;
        }
    </style>
</head>
<body>
<!--便利ボタン このままコピー -->
    <div id="sharp_btns">
        <input class="sharp_btn" type="button" id="sharp1" value="こんにちは">
        <input class="sharp_btn" type="button" id="sharp2" value="こんばんは">
        <input class="sharp_btn" type="button" id="sharp3" value="さようなら">
    </div>
<!--        便利ボタン終わり-->
    
    
    
    <main>      
<!--        便利ボタン機能をつけたいtextareaに.shrapを追加-->
<textarea class="sharp">
あ
い
う
え
お
</textarea>
    <textarea name="" class="sharp" id="" cols="30" rows="10">
か
き
く
け
こ
    </textarea>
    <textarea name="" id="" cols="30" rows="10">
さ
し
す
せ
そ
    </textarea>
    </main>
<!-- javascript -->
    <script src="insert.js"></script>
</body> 

insert.js

/*!================プラグイン================
 * jQuery.selection - jQuery Plugin
 *
 * Copyright (c) 2010-2014 IWASAKI Koji (@madapaja).
 * http://blog.madapaja.net/
 * Under The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
    (function($, win, doc) {
    /**
     * get caret status of the selection of the element
     *
     * @param   {Element}   element         target DOM element
     * @return  {Object}    return
     * @return  {String}    return.text     selected text
     * @return  {Number}    return.start    start position of the selection
     * @return  {Number}    return.end      end position of the selection
     */
    var _getCaretInfo = function(element){
        var res = {
            text: '',
            start: 0,
            end: 0
        };

        if (!element.value) {
            /* no value or empty string */
            return res;
        }

        try {
            if (win.getSelection) {
                /* except IE */
                res.start = element.selectionStart;
                res.end = element.selectionEnd;
                res.text = element.value.slice(res.start, res.end);
            } else if (doc.selection) {
                /* for IE */
                element.focus();

                var range = doc.selection.createRange(),
                        range2 = doc.body.createTextRange();

                res.text = range.text;

                try {
                    range2.moveToElementText(element);
                    range2.setEndPoint('StartToStart', range);
                } catch (e) {
                    range2 = element.createTextRange();
                    range2.setEndPoint('StartToStart', range);
                }

                res.start = element.value.length - range2.text.length;
                res.end = res.start + range.text.length;
            }
        } catch (e) {
            /* give up */
        }

        return res;
    };

    /**
     * caret operation for the element
     * @type {Object}
     */
    var _CaretOperation = {
        /**
         * get caret position
         *
         * @param   {Element}   element         target element
         * @return  {Object}    return
         * @return  {Number}    return.start    start position for the selection
         * @return  {Number}    return.end      end position for the selection
         */
        getPos: function(element) {
            var tmp = _getCaretInfo(element);
            return {start: tmp.start, end: tmp.end};
        },

        /**
         * set caret position
         *
         * @param   {Element}   element         target element
         * @param   {Object}    toRange         caret position
         * @param   {Number}    toRange.start   start position for the selection
         * @param   {Number}    toRange.end     end position for the selection
         * @param   {String}    caret           caret mode: any of the following: "keep" | "start" | "end"
         */
        setPos: function(element, toRange, caret) {
            caret = this._caretMode(caret);

            if (caret === 'start') {
                toRange.end = toRange.start;
            } else if (caret === 'end') {
                toRange.start = toRange.end;
            }

            var userAgent = window.navigator.userAgent.toLowerCase();
            if (userAgent.indexOf("firefox") > -1) {
                element.focus();
            }
            try {
                if (element.createTextRange) {
                    var range = element.createTextRange();

                    if (win.navigator.userAgent.toLowerCase().indexOf("msie") >= 0) {
                        toRange.start = element.value.substr(0, toRange.start).replace(/\r/g, '').length;
                        toRange.end = element.value.substr(0, toRange.end).replace(/\r/g, '').length;
                    }

                    range.collapse(true);
                    range.moveStart('character', toRange.start);
                    range.moveEnd('character', toRange.end - toRange.start);

                    range.select();
                } else if (element.setSelectionRange) {
                    element.setSelectionRange(toRange.start, toRange.end);
                }
            } catch (e) {
                /* give up */
            }
        },

        /**
         * get selected text
         *
         * @param   {Element}   element         target element
         * @return  {String}    return          selected text
         */
        getText: function(element) {
            return _getCaretInfo(element).text;
        },

        /**
         * get caret mode
         *
         * @param   {String}    caret           caret mode
         * @return  {String}    return          any of the following: "keep" | "start" | "end"
         */
        _caretMode: function(caret) {
            caret = caret || "keep";
            if (caret === false) {
                caret = 'end';
            }

            switch (caret) {
                case 'keep':
                case 'start':
                case 'end':
                    break;

                default:
                    caret = 'keep';
            }

            return caret;
        },

        /**
         * replace selected text
         *
         * @param   {Element}   element         target element
         * @param   {String}    text            replacement text
         * @param   {String}    caret           caret mode: any of the following: "keep" | "start" | "end"
         */
        replace: function(element, text, caret) {
            var tmp = _getCaretInfo(element),
                    orig = element.value,
                    pos = $(element).scrollTop(),
                    range = {start: tmp.start, end: tmp.start + text.length};

            element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.end);

            $(element).scrollTop(pos);
            this.setPos(element, range, caret);
        },

        /**
         * insert before the selected text
         *
         * @param   {Element}   element         target element
         * @param   {String}    text            insertion text
         * @param   {String}    caret           caret mode: any of the following: "keep" | "start" | "end"
         */
        insertBefore: function(element, text, caret) {
            var tmp = _getCaretInfo(element),
                    orig = element.value,
                    pos = $(element).scrollTop(),
                    range = {start: tmp.start + text.length, end: tmp.end + text.length};

            element.value = orig.substr(0, tmp.start) + text + orig.substr(tmp.start);

            $(element).scrollTop(pos);
            this.setPos(element, range, caret);
        },

        /**
         * insert after the selected text
         *
         * @param   {Element}   element         target element
         * @param   {String}    text            insertion text
         * @param   {String}    caret           caret mode: any of the following: "keep" | "start" | "end"
         */
        insertAfter: function(element, text, caret) {
            var tmp = _getCaretInfo(element),
                    orig = element.value,
                    pos = $(element).scrollTop(),
                    range = {start: tmp.start, end: tmp.end};

            element.value = orig.substr(0, tmp.end) + text + orig.substr(tmp.end);

            $(element).scrollTop(pos);
            this.setPos(element, range, caret);
        }
    };

    /* add jQuery.selection */
    $.extend({
        /**
         * get selected text on the window
         *
         * @param   {String}    mode            selection mode: any of the following: "text" | "html"
         * @return  {String}    return
         */
        selection: function(mode) {
            var getText = ((mode || 'text').toLowerCase() === 'text');

            try {
                if (win.getSelection) {
                    if (getText) {
                        // get text
                        return win.getSelection().toString();
                    } else {
                        // get html
                        var sel = win.getSelection(), range;

                        if (sel.getRangeAt) {
                            range = sel.getRangeAt(0);
                        } else {
                            range = doc.createRange();
                            range.setStart(sel.anchorNode, sel.anchorOffset);
                            range.setEnd(sel.focusNode, sel.focusOffset);
                        }

                        return $('<div></div>').append(range.cloneContents()).html();
                    }
                } else if (doc.selection) {
                    if (getText) {
                        // get text
                        return doc.selection.createRange().text;
                    } else {
                        // get html
                        return doc.selection.createRange().htmlText;
                    }
                }
            } catch (e) {
                /* give up */
            }

            return '';
        }
    });

    /* add selection */
    $.fn.extend({
        selection: function(mode, opts) {
            opts = opts || {};

            switch (mode) {
                    /**
                 * selection('getPos')
                 * get caret position
                 *
                 * @return  {Object}    return
                 * @return  {Number}    return.start    start position for the selection
                 * @return  {Number}    return.end      end position for the selection
                 */
                case 'getPos':
                    return _CaretOperation.getPos(this[0]);

                    /**
                 * selection('setPos', opts)
                 * set caret position
                 *
                 * @param   {Number}    opts.start      start position for the selection
                 * @param   {Number}    opts.end        end position for the selection
                 */
                case 'setPos':
                    return this.each(function() {
                        _CaretOperation.setPos(this, opts);
                    });

                    /**
                 * selection('replace', opts)
                 * replace the selected text
                 *
                 * @param   {String}    opts.text            replacement text
                 * @param   {String}    opts.caret           caret mode: any of the following: "keep" | "start" | "end"
                 */
                case 'replace':
                    return this.each(function() {
                        _CaretOperation.replace(this, opts.text, opts.caret);
                    });

                    /**
                 * selection('insert', opts)
                 * insert before/after the selected text
                 *
                 * @param   {String}    opts.text            insertion text
                 * @param   {String}    opts.caret           caret mode: any of the following: "keep" | "start" | "end"
                 * @param   {String}    opts.mode            insertion mode: any of the following: "before" | "after"
                 */
                case 'insert':
                    return this.each(function() {
                        if (opts.mode === 'before') {
                            _CaretOperation.insertBefore(this, opts.text, opts.caret);
                        } else {
                            _CaretOperation.insertAfter(this, opts.text, opts.caret);
                        }
                    });

                    /**
                 * selection('get')
                 * get selected text
                 *
                 * @return  {String}    return
                 */
                case 'get':
                    /* falls through */
                default:
                    return _CaretOperation.getText(this[0]);
            }

            return this;
        }
    });
})(jQuery, window, window.document);
/* =========ここまでプラグイン=========== */




/* =========プラグインの設定&適用=========== */
$(function(){


    $("#sharp1")
        .click(function(){        /*text: "挿入する文字", mode: "before:カーソルの前に挿入""after:カーソルの後に挿入""replace:選択範囲を置き換え", caret: "keep:カーソル保持" */
        $(".sharp:focus").selection("insert",{ text: "こんにちは", mode: "before", caret: "keep" })
    });

    $("#sharp2")
        .click(function(){
        $(".sharp:focus").selection("insert",{ text: "こんばんは", mode: "before", caret: "keep" })
    });

    $("#sharp3")
        .click(function(){
        $(".sharp:focus").selection("insert",{ text: "さようなら", mode: "before", caret: "keep" })
    });


/* =========便利ボタンの表示の切り替え=========== */
    $('.sharp').focus(function(){
        $('#sharp_btns').css("display","block");
    });
    $('.sharp').blur(function(){
        if($("#sharp_btns").is("#sharp_btns:active")){
            this.focus();
        }else{
            $('#sharp_btns').css("display","none");
        }
    });


});

ユニークユーザーを集計する時の注意点

みなさんはWEBの状況を知るのにどんな数字を気にしていますか?

Google AnalyticsではCV数,CVR,PV,UU,Session数などいろいろな数字をみることができますがその中の一つにUUつまりユニークユーザー数という指標があると思います。 この数字が高ければ基本的に自分のサイトに訪れている人が多いという判断ができます。 もちろん参考にしているよ!という方にお聞きしたいのです。あなたは本当に意図しているユニークユーザー数をみることができていますか?と。

今回はGoogle Analyticsでの計測の仕方について紹介しますが、他の分析ツールを使っている方も同じ問題を実は持っているかもせいれません。 WEB担当の方やデータ分析を担当している方にぜひ一度読んでいただきたいです。

ユニークユーザーとはなにか?

ユニークユーザーとはどういう意味か。これは知っている人も多いと思います。 これはある期間中にそのサイトあるいはページを訪れた人数のこと。 つまり、ある期間中に同じ人が何回か同じサイトやページに訪れてもその数はカウントされないことになります。

ユニークユーザーの落とし穴

ユニークユーザーを見ればどのくらいの人が自分のサイトやページに訪れているのかがわかるので、ECサイトなど運営している側からするとかなり気にしてみたい数字の一つと言えるでしょう。 しかし、ユニークユーザーにも落とし穴があります。それは集計期間です。集計期間が異なるとユニークユウーザーの数にズレが生じてしまう問題です。 具体的に見ていきましょう。例えばAさんは4月と5月中に1回ずつECサイトで買い物するために訪れたとします。 集計するときに04/01~05/31の期間で集計したとします。この場合は上で説明したようにユニークユーザー数には1カウントしかされませんね。 しかしどうでしょう?04/01~04/30と5/01~05/31の期間の2回に分けて集計を行い、それを4,5月の分析データとしてみると、このときは2カウントされてしまうのです。

集計期間をわざわざ2回に分ける必要がないじゃん!と思ったかたは大丈夫なのですが、実は現場では、定期レポート作成などのために1ヶ月ごとにデータを集計する場合がありますよね。このときは上のようなズレの問題が生じてしまうのです。この方法だと同じ人が04/30と05/01に1度ずつ訪れたとしても別ユーザーとしてカウントされてしまうのです。これでは正しいデータを集計できているとは言えないですよね。

どのように集計すればよいか

上のような問題はユニークユーザーだけでなく他のデータでも起こりえることだと思います。 そこでどのようにデータを集計すればよいのか考えてみましょう。 皆さんのサイトでは一度訪問したユーザーがどのくらいの期間を空けて再訪したときにユニークユーザーとして認めますか? Amazonなどの小売りECサイトでは短期間で違うものを購入するために訪れることもあるので、比較的短く設定してもよいと思われます。 逆に、引越しサイトなどは一回頼むとあまりそのサイトは使わなくなることが多いと思うので、比較的長めに設定してもよいかと思われます。 つまりサイトによってユニークユーザーの定義をしっかり決めて決めた期間できちんと集計することが大事なのではないでしょうか?