RをPandasに置き換える(ヒストグラム)
ビジネス活用事例で学ぶ データサイエンス入門
タイトルのとおり、ビジネス事例(アプリ開発会社の事例)を元にしてデータ分析を学ぶスタイルになっています。馴染みのある事例なのでデータ活用のイメージがしやすい内容になっています。
本書はRを使っていますが、どちらかというとPython Pandasの方が好きなので、RからPandasに置き換えてみました。
(0)R・データの準備
Rインストール
R-3.2.3.pkgをインストールしました。
データダウンロード
上記からダウンロードしました。
(1)CSVファイル読み込み
- DAU(1日1回以上アクセスしたユーザのデータ
- DPU(1日1円以上課金したユーザのデータ
- Install(ユーザごとにいつゲームを利用開始したかのデータ)
R
> dau <- read.csv("section3-dau.csv", header = T, stringsAsFactors = F) > head(dau) log_date app_name user_id 1 2013-06-01 game-01 116 2 2013-06-01 game-01 13491 3 2013-06-01 game-01 7006 > dpu <- read.csv("section3-dpu.csv", header = T, stringsAsFactors = F) > head(dpu) log_date app_name user_id payment 1 2013-06-01 game-01 351 1333 2 2013-06-01 game-01 12796 81 3 2013-06-01 game-01 364 571 > install <- read.csv("section3-install.csv", header = T, stringsAsFactors= F) > head(install) install_date app_name user_id 1 2013-04-15 game-01 1 2 2013-04-15 game-01 2 3 2013-04-15 game-01 3
Pandas
>>> import pandas as pd >>> dau = pd.read_csv("section3-dau.csv") >>> dau log_date app_name user_id 0 2013-06-01 game-01 116 1 2013-06-01 game-01 13491 2 2013-06-01 game-01 7006 >>> dpu = pd.read_csv("section3-dpu.csv") >>> dpu log_date app_name user_id payment 0 2013-06-01 game-01 351 1333 1 2013-06-01 game-01 12796 81 2 2013-06-01 game-01 364 571 >>> install = pd.read_csv("section3-install.csv") >>> install install_date app_name user_id 0 2013-04-15 game-01 1 1 2013-04-15 game-01 2 2 2013-04-15 game-01 3
(2)DAUのデータにInstallデータをくっつける
R
> dau.install <- merge(dau, install, by = c("user_id", "app_name")) > head(dau.install) user_id app_name log_date install_date 1 1 game-01 2013-06-03 2013-04-15 2 1 game-01 2013-06-14 2013-04-15 3 1 game-01 2013-07-09 2013-04-15
Pandas
>>> dauinstall = pd.merge(dau, install, how='left', on=['user_id', 'app_name']) >>> dauinstall log_date app_name user_id install_date 0 2013-06-01 game-01 116 2013-04-17 1 2013-06-01 game-01 13491 2013-06-01 2 2013-06-01 game-01 7006 2013-05-03
(3)dau.installに、さらにDPUデータをくっつける
R
> dau.install.payment <- merge(dau.install, dpu, by = c("log_date", "app_name", "user_id"), all.x = T) > head(dau.install.payment) log_date app_name user_id install_date payment 1 2013-06-01 game-01 1 2013-04-15 NA 2 2013-06-01 game-01 3 2013-04-15 NA 3 2013-06-01 game-01 6 2013-04-15 NA
Pandas
>>> dauinstallpayment = pd.merge(dauinstall, dpu, how = 'left', on = ['log_date', 'app_name', 'user_id']) >>> dauinstallpayment log_date app_nam user_id install_date payment 0 2013-06-01 game-01 116 2013-04-17 NaN 1 2013-06-01 game-01 13491 2013-06-01 NaN 2 2013-06-01 game-01 7006 2013-05-03 NaN
(4)payment=NAのデータを除外する
R
> head(na.omit(dau.install.payment)) log_date app_name user_id install_date payment 7 2013-06-01 game-01 19 2013-04-15 162 81 2013-06-01 game-01 351 2013-04-18 1333 84 2013-06-01 game-01 364 2013-04-18 571
Pandas
>>> dauinstallpayment.dropna(subset=['payment']) log_date app_name user_id install_date payment 12 2013-06-01 game-01 19 2013-04-15 162 84 2013-06-01 game-01 351 2013-04-18 1333 87 2013-06-01 game-01 364 2013-04-18 571
(5)payment=NAのデータに課金額0を設定する
R
> dau.install.payment$payment[is.na(dau.install.payment$payment)] <- 0 > head(dau.install.payment) log_date app_name user_id install_date payment 1 2013-06-01 game-01 1 2013-04-15 0 2 2013-06-01 game-01 3 2013-04-15 0 3 2013-06-01 game-01 6 2013-04-15 0
Pandas
>>> dauinstallpayment.fillna(0) log_date app_name user_id install_date payment 0 2013-06-01 game-01 116 2013-04-17 0 1 2013-06-01 game-01 13491 2013-06-01 0 2 2013-06-01 game-01 7006 2013-05-03 0 >>> dauinstallpayment.fillna(0, inplace=True) >>> dauinstallpayment log_date app_name user_id install_date payment 0 2013-06-01 game-01 116 2013-04-17 0 1 2013-06-01 game-01 13491 2013-06-01 0 2 2013-06-01 game-01 7006 2013-05-03 0
(6)月次で集計する
R
> dau.install.payment$log_month <- substr(dau.install.payment$log_date, 1, 7) > dau.install.payment$install_month <- substr(dau.install.payment$install_date, 1, 7) > library("plyr") > mau.payment <- ddply(dau.install.payment, .(log_month, user_id, install_month), summarize, payment = sum(payment)) > head(mau.payment) log_month user_id install_month payment 1 2013-06 1 2013-04 0 2 2013-06 2 2013-04 0 3 2013-06 3 2013-04 14994
Pandas
dauinstallpayment['log_month'] = dauinstallpayment['log_date'].str[0:7] dauinstallpayment['install_month'] = dauinstallpayment['install_date'].str[0:7] >>> maupayment = dauinstallpayment.groupby(['user_id','log_month', 'install_month'])['payment'].sum() >>> maupayment user_id log_month install_month 1 2013-06 2013-04 0 2013-07 2013-04 0 2 2013-06 2013-04 0 3 2013-06 2013-04 14994
(7)新規ユーザか既存ユーザかの区分を追加する
R
> mau.payment$user.type <-ifelse(mau.payment$install_month == mau.payment$log_month, "install", "existing") > mau.payment.summary <- ddply(mau.payment, .(log_month, user.type), summarise, total.payment = sum(payment)) > head(mau.payment) log_month user_id install_month payment user.type 1 2013-06 1 2013-04 0 existing 2 2013-06 2 2013-04 0 existing 3 2013-06 3 2013-04 14994 existing 4 2013-06 4 2013-04 0 existing 5 2013-06 6 2013-04 0 existing 6 2013-06 7 2013-04 0 existing > head(mau.payment.summary) log_month user.type total.payment 1 2013-06 existing 177886 2 2013-06 install 49837 3 2013-07 existing 177886 4 2013-07 install 29199
Pandas
>>> dauinstallpayment['user_type'] = np.where(dauinstallpayment['log_month'] == dauinstallpayment['install_month'], 'install', 'existing') >>> dauinstallpayment log_date app_name user_id install_date payment log_month install_month user_type 0 2013-06-01 game-01 116 2013-04-17 0 2013-06 2013-04 existing 1 2013-06-01 game-01 13491 2013-06-01 0 2013-06 2013-06 install 2 2013-06-01 game-01 7006 2013-05-03 0 2013-06 2013-05 existing
(8)グラフによりデータを可視化する(新規、既存比較)
R
ggplot(mau.payment.summary, aes(x = log_month, y = total.payment, fill = user.type)) + geom_bar(stat="identity") + scale_y_continuous(label = comma)
Pandas
TODO..
(9)グラフによりデータを可視化する(金額別売上比較)
R
ggplot(mau.payment[mau.payment$payment > 0 & mau.payment$user.type == "install", ], aes(x = payment, fill = log_month)) + geom_histogram(position = "dodge", binwidth = 2000)
Pandas
TODO..
補足 Pandasを0.17にした
Equivalent of R/ifelse in Python/Pandas? Compare string columns?
conda update anaconda conda update conda conda update pandas numpy: 1.9.2-py34_0 --> 1.10.4-py34_0 pandas: 0.16.2-np19py34_0 --> 0.17.1-np110py34_0 pytz: 2015.4-py34_0 --> 2015.7-py34_0 six: 1.9.0-py34_0 --> 1.10.0-py34_0
まとめ
Rの良いところの感想は以下です。しばらくRとPandasを併用してみてPro/Conを考えてみます。いずれにしても両方知っておくと、情報量を増やすことができるので良いと思いました。
- Rのインストールが簡単
- グラフがmatplotlibより(少し)オシャレ
参考
Python pandas 図でみる データ連結 / 結合処理
Equivalent of transform in R/ddply in Python/pandas?
Equivalent of R/ifelse in Python/Pandas? Compare string columns?
TipMe
Tower of Babelを使ってES2015を学ぶ
ES6を学べるチュートリアル
github yosuke-furukawa/tower-of-babel
多少カンニングしましたが.. 一応クリアしました。ES2015の新機能を学べます。継承、Block Scope、モジュールなど、これまでよりスッキリ実装できそうです。
ES6の全ての新機能を網羅しているわけではないのでfolkして項目を追加しても良さそう(問題作るのは大変そうですが...)
ES5についても曖昧(prototypeとか)なので、ES5版も作って欲しいです..
ES2015を使いたくなってきました!
参考
github yosuke-furukawa/tower-of-babel
ES6 を学べる tower-of-babel を作りました。 (workshopper の作り方)
JavaScriptでforEach, filter, map, reduceとか
ES2015(ES6)な時代だからこそ、ES5を改めて調べたJavaScript初級者のメモ
ECMAScript 6 compatibility table
React.js + Babel + Browserify + gulp の環境を作ってみた
TipMe
counterwallet.ioが改竄された時に僕のビットコインは安全か
ウィルス感染でWebサービスが20日間ダウン
ウィルス感染でWebサービスが20日間ダウン。本当にごめんなさい
サーバーを乗っ取られて信用を落とした顛末が書かれてある記事。他人事ではない。
counterwallet.ioは秘密鍵を保持しないから安全!?
counterwallet.ioは秘密鍵を保持しないから安全と言われています。ただしサーバーがハッキングされてAPIが改竄されていた場合に以下のような危険があります。
送信先が改変されたtxにサインしてしまう危険性
XCP送信例。トランザクション作成
- 送信元: 12sWrxRY7E7Nhmuyjbz4TtGE9jRewGqEZD
- 送信先: 1965areciqapsuL2hsia2yKkRLfAsH1smG
- 送金額: 0.0005XCP
curl http://xxx:xxx/api/ --user xxx:xxx -H 'Content-Type: application/json; charset=UTF-8' -H 'Accept: application/json, text/javascript' --data-binary '{"jsonrpc":"2.0", "id":0, "method":"create_send", "params":{"source":"12sWrxRY7E7Nhmuyjbz4TtGE9jRewGqEZD", "destination":"1965areciqapsuL2hsia2yKkRLfAsH1smG", "quantity":50000, "asset":"XCP"}}'
レスポンス
未サイントランザクションが返ります。
{"id": 0, "result": "01000000038273bcfe9961f0eb03fa90bdb91af8d1a96a18cdfcf7e362e6260dff7ebf9851000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88acffffffff7dd4cdce57f4cd83137e669cd4c64ae749433b889df781fb4fc30b6f219ae61b000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88acffffffffccc2c1ddf717c52276461ad031d9471c8f459a5ba3865cb381ecb5a914fd6cfe000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88acffffffff0336150000000000001976a91458b6e991b45487df810f4d96d5315da739637f1788ac00000000000000001e6a1c176441d9b0d5e714def4becc729931ec91004dfaf58b4d18c6757f1c73310000000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88ac00000000", "jsonrpc": "2.0"}
送信先の改竄のリスクがある
例えばPHPでAPIを実装していた場合なら、送信先を こそっと 変更することは簡単です。レスポンスを見ただけでは正しいかは分かりません。
counterwallet.ioは未サイントランザクションのチェックがある
さすがにマズイのでcounterwallet.ioは、サイン前に未サイントランザクションのチェックを行っています。以下に必要そうなところだけ抜き出しました。
CWBitcore.checkTransactionDest = function(txHex, source, dest) { var tx = CWBitcore.parseRawTransaction(txHex); for (var i=0; i<tx.outs.length; i++) { var addresses = CWBitcore.extractAddressFromTxOut(tx.outs[i]).split(','); var containsSource = _.intersection(addresses, source).length > 0; var containsDest = _.intersection(addresses, dest).length > 0; if ((containsSource == false && containsDest == false) && tx.outs[i].getScript().classify() != bitcore.Script.TX_RETURN && tx.outs[i].getScript().classify() != bitcore.Script.TX_UNKNOWN) { return false; } return true; } CWBitcore.extractAddressFromTxOut = function(output) { switch (output.script.classify()) { case bitcore.Script.types.PUBKEY_OUT: return output.script.toAddress(NETWORK).toString(); case bitcore.Script.types.PUBKEYHASH_OUT: return output.script.toAddress(NETWORK).toString(); case bitcore.Script.types.SCRIPTHASH_OUT: return output.script.toAddress(NETWORK).toString(); case bitcore.Script.types.MULTISIG_OUT: var addresses = CWBitcore.extractMultiSigAddressesFromScript(output.script); return addresses.join(","); case bitcore.Script.types.DATA_OUT: return ""; default: throw new Error("Unknown type [" + output.script.classify() + "]"); } }
未サイントランザクションCheck簡易版
説明を簡単にするために簡易版のスクリプトを用意しました。
bower install
bower install bitcore-lib bower install bitcore-mnemonic
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script src="bower_components/bitcore-lib/bitcore-lib.min.js"></script> <script src="bower_components/bitcore-mnemonic/bitcore-mnemonic.min.js"></script> </head> <body> <script type="text/javascript"> var bitcore = require('bitcore-lib'); var Mnemonic = require('bitcore-mnemonic'); var raw = new bitcore.deps.Buffer('01000000038273bcfe9961f0eb03fa90bdb91af8d1a96a18cdfcf7e362e6260dff7ebf9851000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88acffffffff7dd4cdce57f4cd83137e669cd4c64ae749433b889df781fb4fc30b6f219ae61b000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88acffffffffccc2c1ddf717c52276461ad031d9471c8f459a5ba3865cb381ecb5a914fd6cfe000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88acffffffff0336150000000000001976a91458b6e991b45487df810f4d96d5315da739637f1788ac00000000000000001e6a1c176441d9b0d5e714def4becc729931ec91004dfaf58b4d18c6757f1c73310000000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88ac00000000', 'hex'); var tx = new bitcore.Transaction(raw); for (var i = 0; i < tx.outputs.length; i++) { var type = tx.outputs[i].script.classify(); switch (type) { case bitcore.Script.types.PUBKEY_OUT: case bitcore.Script.types.PUBKEYHASH_OUT: case bitcore.Script.types.SCRIPTHASH_OUT: console.log(tx.outputs[i].script.toAddress(bitcore.Networks.livenet).toString()); break; case bitcore.Script.types.MULTISIG_OUT: case bitcore.Script.types.DATA_OUT: default: console.log('Unknown type [' + type + ']'); } } </script> </body> </html>
コンソールログ
index.html:28 1965areciqapsuL2hsia2yKkRLfAsH1smG index.html:34 Unknown type [Data push] index.html:28 12sWrxRY7E7Nhmuyjbz4TtGE9jRewGqEZD
Blockchain.info
3つのスクリプトに含まれる2つのアドレスが、コンソールに出力されたアドレスと一致しています。この一致を比較することで意図した送金を確認できます。
Data pushスクリプトの中身は見なくていいの?
BitcoinとCounterpartyの送信トランザクションの比較 に書きましたが、Data pushスクリプトには送金元、送金先の情報は含めないので、悪意あるユーザーに送金されることはありえません。
ただし、愉快犯的なクラッカーにより不正な値のトランザクションが送信される可能性があります。(例えば、勝手にアセットにロックをかけたり、配当もできる..?)
チェックスクリプト自体を改変された場合はどうなるのか
Webアプリの場合はチェックスクリプト自体を改変・無効に変更することができますね.. コード改竄監視は必要になりそうです。気になるのでcounterwallet.ioコミュニティに質問投げてみます。
クライアントがモバイルアプリの場合は、チェックスクリプトの改竄リスクは低いでしょう。
まとめ
サーバのセキュリティを強化しないといけない.. と思いました。どのプロダクトが良いのか調べてみます。
参考
Problem with Buffer when trying to add a string to a script
Bitcoins the hard way: Using the raw Bitcoin protocol
BitcoinとCounterpartyの送信トランザクションの比較
TipMe
実質実効為替レートと失業率の相関を調べる
円安ってそんなに大事なのか
どちらかというと円安の方が景気に良いって言われるけど、ほんとにそうなのか気になったので失業率との相関を調べてみました。
失業率のデータ取得
就業者データと同様に総務省統計局からExcelデータを取得してpandasで読み込みます。
データ取得
import pandas as pd import urllib.request as urllib2 link = 'http://www.stat.go.jp/data/roudou/longtime/zuhyou/lt01-a70.xls' socket = urllib2.urlopen(link) xls = pd.ExcelFile(socket) df = xls.parse(xls.sheet_names[0], skiprows=10, header=0, skip_footer=3) df.columns = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N'] df = pd.DataFrame({'Total': df['J']})
日付設定
rng = pd.date_range('1/1/1968', periods=576, freq='M') df['Date'] = rng
グラフ化
import matplotlib.pyplot as plt import numpy as np plt.figure(figsize=(10,5)) plt.grid() x = np.array(df['Date']) y = np.array(df['Total']) plt.plot(x, y) plt.show()
失業率が1.0%の時代がある!! 驚き!
実質実効為替レートとは
次は失業率のグラフに実質実効為替レートを重ねます。
実効為替レートは、特定の2通貨間の為替レートをみているだけでは捉えられない、相対的な通貨の実力を測るための総合的な指標です。具体的には、対象となる全ての通貨と日本円との間の2通貨間為替レートを、貿易額等で計った相対的な重要度でウエイト付けして集計・算出します。
1ドルX円とかでなくて、80とか100とかの数値で表したレート。レートが高い方が円高。
日銀から実質実効為替レートデータ取得
日銀の時系列統計データ検索サイトから1968年〜2015年の実質実効為替レートデータを取得してください。(CSVファイル、ヘッダなし)
以下メニューをたどるとダウンロードできます。(だいぶ面倒です...)
各種マーケット関連統計 (ST) > 外国為替市場 > 実効為替レート(名目・実質)
CSVファイル読み込み
df_rate = pd.read_csv("/tmp/nme_R031.633.20160222234838.01.csv", header=None) df_rate.columns = ['A','B'] df['Rate'] = df_rate['B'] df
Total Date Rate 0 1.3 1968-01-31 NaN 1 1.3 1968-02-29 NaN 2 1.2 1968-03-31 NaN .. ... ... ... 573 3.1 2015-10-31 71.64 574 3.3 2015-11-30 70.66 575 3.3 2015-12-31 71.57
実質実効為替レートと失業率の相関をグラフ化
x = np.array(df['Date']) y_total = np.array(df['Total']) y_rate = np.array(df['Rate']) fig = plt.figure(figsize=(10,5)) ax1 = fig.add_subplot(111) plt.plot(x, y_total, color='b') plt.ylabel("Unemployment Rate",fontsize=14,color='b') ax2 = ax1.twinx() plt.plot(x, y_rate, color='r') plt.ylim(ymin=60) plt.ylim(ymax=140) plt.ylabel("Exchange Rate",fontsize=14,color='r') plt.grid(True) plt.show()
- 1973年(第1次)と1979年(第2次)オイルショック
- 1986年~1991年 バブル景気
まとめ
2000年以降は実質実効為替レートと失業率の相関は高そうです。バブルの頃は円高でも失業率に影響しなかったのでしょうか.. 現在の失業率(3%台半)は完全雇用の状態らしいです。これ以上改善されないということでしょうか今後の推移が気になります。
なるほど
為替は2つの通貨の交換比率なので、どちらが相対的に多いかどうかできまる。円安は円がドルに対して相対的に多いということなので、日本が金融緩和すればそうなる。その一方、モノに対しても円が多い状態になるので、モノの価格は上がる。つまりデフレになりにくい。
参考
TipMe
総務省 労働力調査 長期時系列データ(Excel)をpandasで読み込む
就業者数の推移が気になった
リーマン・ショック、民主党政権、第2次安倍内閣。ここ10年ぐらいの就業者数の推移が気になったのでグラフ化しました。
労働力データ取得
import pandas as pd import urllib.request as urllib2 link = 'http://www.stat.go.jp/data/roudou/longtime/zuhyou/lt01-a30.xls' socket = urllib2.urlopen(link) xls = pd.ExcelFile(socket) df = xls.parse(xls.sheet_names[0], skiprows=10, header=0, skip_footer=3) df.columns = ['A','B','C','D','E','F','G','H','I','J','K','L','N'] df = pd.DataFrame({'Total': df['E']})
日付はdate_rangeで生成
rng = pd.date_range('1/1/1953', periods=756, freq='M') df['Date'] = rng
グラフ化
import matplotlib.pyplot as plt import numpy as np x = np.array(df['Date']) y = np.array(df['Total']) plt.plot(x, y) plt.show()
2007年から2015年までの期間
x = np.array(df['Date'][648:]) y = np.array(df['Total'][648:]) plt.plot(x, y) plt.show()
- 2008年9月15日 リーマン・ブラザーズ破綻
- 2009年7月21日 鳩山内閣発足
- 2011年3月11日 東日本大震災
- 2012年12月26日 第2次安倍内閣発足
まとめ
安倍内閣の間に雇用が150万人増えていることが分かります。参考数値として鹿児島県の人口は約160万人。ほぼ同数の雇用が増えたということですね。政府は一億総活躍社会の実現を目指していますが今後の就業者数の推移が楽しみです。
参考
Time Series / Date functionality
Python pandas で日時関連のデータ操作をカンタンに
TipMe
Parse.comの代替としてAmazonSNSを使う
Parse.com終わりますね..
あちこちで悲鳴が聞こえます... Facebookに買収されたから安心していたんですけどね..
AmazonSNSにします
Amazon SNSでモバイルPush/HTTP Push通知
いろいろ比較しましたが乗り換え先はAmazonSNSに決めました。決め手はAmazonだから(..過去から学んでない)、価格、API、Webコンソールが便利。などです。
クラウドのサービスはGoogle Cloud Platformをメインに使っているのでFirebaseを検討しましたがPush通知機能が無いので諦めました。(今後に期待)
fastlane pemは便利だった
サンプルアプリでAmazonSNSの動作を確認しようとしてPush通知の設定を忘れていました。調べてみると最近はfastlane pemなるものがあり便利でした。(fastlaneは言語ごとのスクリーンショットを保存できたり他にも便利機能ありますよね)
インストール
$sudo gem install pem
証明書作成
$pem --development
AmazonSNSの設定
(1)アプリケーション作成ボタンクリック
(2)アプリケーション設定
fastlane pemで作成した.p12ファイルを選択した後に、Load Credentials from FileボタンをクリックするとCertificateとPrivate Key項目は自動で設定されます。
(3)エンドポイント作成ボタンクリック
(4)エンドポイント設定
(5)Publishボタンクリック
(6)Publish設定
(7)Push通知受信
無事通知来ました
AmazonSNS APIを使う
API経由でエンドポイント作成とパブリッシュを行います。
composer install
PHPのライブラリがあるので簡単です
"aws/aws-sdk-php": "3.*"
エンドポイント作成
Laravelのコマンドに実装しました
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Aws\Sns\SnsClient; class PublishPushNotification extends Command { protected $signature = 'aws-sns:publish'; protected $description = 'This command will publish a notification'; public function handle() { $sns = new SnsClient([ 'credentials' => [ 'key' => 'xxxx', 'secret' => 'xxxx' ], 'region' => 'us-west-2', 'version' => '2010-03-31' ]); // Create Endpoint $arn = 'arn:aws:sns:xxxx'; $token = 'xxxx'; $options = array( 'PlatformApplicationArn' => $arn, 'Token' =>$token, 'CustomUserData' => '', ); try { $result = $sns->createPlatformEndpoint($options); echo $result['EndpointArn']; } catch (Exception $e) { echo $e->getMessage(); // TODO logging } }
パブリッシュ
// Publish $arn = 'arn:aws:sns:xxxx'; $options = array( 'Message' => 'Hello!', 'TargetArn' => $arn ); try { $result = $sns->publish($options); echo $result['MessageId']; } catch (Exception $e) { echo $e->getMessage(); // TODO logging }
まとめ
以下がTODOです。今度やります。
- 言語毎のメッセージ分けを簡単にできないかな
- IAMで権限を絞る
あとiOS9からプッシュ通知に変更があったの知らなかったのでメモしておきます。
WWDC 2015 – Big changes to Apple push notifications
- Push notification actions
- APNS token size
- Providers API
参考
Amazon SNSでモバイルPush/HTTP Push通知
WWDC 2015 – Big changes to Apple push notifications
Amazon Simple Notification Service
AWS SDK for PHPのVersion 3に更新して、EC2自動起動処理を書き換えてみた
TipMe
URLスキームはダイアログが出るからUniversal Linksを試してみた
URLスキームはiOS9からダイアログが出るようになった
いまさらネタですが... URLスキームを使ってアプリを開く、またはアプリダウンロードを促す画面に遷移する。これをやるためにアレコレ実装していましたが、iOS9からできなくなりました。
これまではなんとか実装していた
iOSはURLスキーマ起動を試みて、500ms待ってもダメだったらストアに遷移するって実装をしてましたが、iOS9から確認ダイアログが表示される仕様になりました。
if (userAgent.search(/iphone|ipad|ipod/) > -1) { launch_frame.location.href = IOS_SCHEME + '://'; setTimeout(function() { location.href = IOS_STORE; }, 500); }
アプリインストール済みの時
アプリをインストールしていない時
ちなみにAndroidは問題ない
Androidは以下の設定で実現できます。(アプリが未インストールならストアが開く。簡単!)
if (userAgent.search(/android/) > -1) { document.location = 'intent://#Intent;scheme=' + ANDROID_SCHEME + ';package=' + ANDROID_PACKAGE + ';end'; }
Universal Linksなら解決できる?
iOS9の新機能Universal Linksなら解決できるかもしれないと思い、まずUniversal Linksを体験しました。
51 iOS 9 Apps That Support Universal Links (updated Dec 19th)
(1)Citymapperのサンプルリンクをクリック
(2)アプリ未インストール時
Safariで地図が表示されます。
(3)アプリインストール時
アプリが開き周辺の情報が表示されます。
シームレスですね。なんとなくUniversal Linkのことが分かりました。
自分のアプリで試してみます
(1)設定(アプリ、サーバ側)
URLスキーム・独自ディープリンク実装に代わる、Universal Links(iOS 9で導入)でより良いUXを実現
新規プロジェクトを作成して、上記サイトの
を行えばOKです。(詳しい説明は省略します)
(2)実行
iPhoneのメモ帳にURLを書きます。
https://sample.com/hoge
URLをタップした時に、(1)で作成したアプリをインストール済みの場合はアプリが開き、未インストール時は https://sample.com/hoge のWebページがSafariで表示されますね。
まとめ
パラメータの受け取り、pathsの設定など本格的に利用するにはもっと詳しく調べる必要がありそうです。
参考
URLスキームを利用して、アプリが入ってたらアプリ起動、入ってなかったらストアへ!を実現。
URLスキーム・独自ディープリンク実装に代わる、Universal Links(iOS 9で導入)でより良いUXを実現
iOS9でカスタムURLスキームの遷移に失敗するときの注意点
[iOS9] カスタムURLスキームで起動に失敗する(呼び出し元)
Universal Links に対応するときに考慮したいこと
URLSchemeでアプリの有無によって挙動を変える(iOS9対応版)
51 iOS 9 Apps That Support Universal Links (updated Dec 19th)
Universal Links のサーバ側の対応をやってみた
Breaking down iOS 9 Universal Links
iOS 9 Universal Links (apple-app-site-association) blues