マツリさんの日記

androidプログラミング初心者の奮闘日記です。たまに統計学もしてます。

許可ダイアログの表示

 android6.0(marshmallow)以降では、permissionについて明示的な許可が必要になります。googleのdeveloper向けのサイトを元に、不完全なのですが、このように許可を作ってみました。stackoverflowには独自メソッドをつくるやり方もあったのですが・・・

 

if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS) || ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_PHONE_STATE)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
}
}

 

  最初に公開してきた時から、色々と変更していますので、あらためて一部のクラスを自分の備忘録として書いておきます。

 

 MainActivity.java

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

private final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 10;

@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
}
return;
}
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS) || ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_PHONE_STATE)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
}
}

Button instructionButton = (Button) findViewById(R.id.instruction);
assert instructionButton != null;
instructionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplication(), Instruction.class);
startActivity(intent);
}
});

Button instruction2Button = (Button) findViewById(R.id.instruction2);
assert instruction2Button != null;
instruction2Button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplication(), Instruction2.class);
startActivity(intent);
}
});

Button instruction3Button = (Button) findViewById(R.id.instruction3);
assert instruction3Button != null;
instruction3Button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplication(), Instruction3.class);
startActivity(intent);
}
});

Button instruction4Button = (Button) findViewById(R.id.instruction4);
assert instruction4Button != null;
instruction4Button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplication(), Instruction4.class);
startActivity(intent);
}
});

PackageInfo packageInfo = null;
TextView textView = (TextView)findViewById(R.id.version);
try {
packageInfo = getPackageManager().getPackageInfo("com.rnma2ri.saftyapp", PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
assert textView != null;
assert packageInfo != null;
textView.setText("versionCode : "+packageInfo.versionCode+" / "+"versionName : "+packageInfo.versionName);
}
}

MyDialog.java

import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;

import java.util.ArrayList;
import java.util.List;

public class MyDialog extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_my_dialog);

List<String> list = new ArrayList<String>();
list.add("風邪で声が変わったは詐欺!");
list.add("電話番号が変わったは詐欺!");
list.add("医療費還付金があるは詐欺!");
list.add("詐欺の電話に注意しましょう!");
final CharSequence[] charSequences = list.toArray(new String[list.size()]);
AlertDialog.Builder alert = new AlertDialog.Builder(this, R.style.Theme_AppCompat_DialogActivity);
alert.setTitle(R.string.dialog_title)
.setIcon(R.mipmap.btn1)
.setItems(charSequences, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int item) {
String string = charSequences[item].toString();
}
})
.setPositiveButton(R.string.dialog_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
MyDialog.this.finish();
}
});
alert.create().show();
}
}

PhoneReceiver.java

import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

class PhoneReceiver extends PhoneStateListener {
private int totalCount = 0;
private Context context;

PhoneReceiver(Context context) {
this.context = context;
}

// 通話状態の変化に応じて表示を変更する
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);

switch (state) {
// 着信時の処理内容
case TelephonyManager.CALL_STATE_RINGING:

// ContentResolverを使って、端末内の番号を取得し、着信中の番号と突き合わせる
Cursor addressTable = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
try {
if (addressTable != null) {
while (addressTable.moveToNext()) {
String phoneNumber = addressTable.getString(addressTable.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
for (int i = 0; i < addressTable.getCount(); i++) {
int count = BooleanSum(PhoneNumberUtils.compare(phoneNumber, incomingNumber));
totalCount += count;
}
}
}
} finally {
assert addressTable != null;
addressTable.close();
}
if (totalCount < 1) {
Intent i = new Intent(context.getApplicationContext(), MyDialog.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.getApplicationContext().startActivity(i);
}
break;
}
}

// PhoneNumberUtilメソッドのBoolean型の戻り値をint型(0 or 1)に変換するメソッド
private int BooleanSum(Boolean exchanger) {
if (exchanger) return 1;
else {
return 0;
}
}
}

PhoneStateReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class PhoneStateReceiver extends BroadcastReceiver {
// 各フィールドの定義
TelephonyManager manager;
PhoneReceiver phoneStateListener;
static boolean listener = false;

// intent情報を処理する
@Override
public void onReceive(Context context, Intent intent) {
// PhoneReceiverインスタンスの生成
phoneStateListener = new PhoneReceiver(context);
// TelephonyManagerインスタンスの生成
manager =((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));

if(!listener) {
manager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
listener = true;
}
}
}

 MainActivity.javaの最後では、versioncodeを表示するようにしています。

 まだまだ不完全ですが、一応これでひと段落です。間違いや効率的なやり方があれば教えていただけると助かります。

時系列データ解析 07 ~ ARMA過程

www.asakura.co.jp

 沖本先生の本の続きです。MA過程、AR過程ときて、続いてはARMA過程です。

 ARMA(自己回帰移動平均)過程は、AR過程とMA過程の両方を含んだ過程です。( p, q)次のARMA過程、つまりARMA(p,q)過程は、

 \displaystyle y_{t} = c + \phi_{1} y_{t-1} + \cdots + \phi_{p} y_{t-p} + \epsilon_{t} + \theta_{1} \epsilon_{t-q} + \cdots + \theta_{q} \epsilon_{t-q}, \epsilon_{t} \sim W.N. (\sigma^{2})

と記述されます。AR過程、MA過程、両方の性質のうち強い方がARMA過程の性質になります。

 たとえば、MA過程は常に定常となりますが、AR過程は常に定常であるとは限らないので、ARMA過程も定常になるとは限りません。

 ARMA過程の性質は下記のとおりです。

 \mu = E(y_{t}) = \frac{c}{1 - \phi_{1} - \phi_{2} - \cdots - \phi_{p}}

 q+1次以降の自己共分散( \gamma_{t})と自己相関( \rho_{t})は、 y_{t}が従うARMA過程のAR部分と同一の係数をもつp次差分方程式(ユール・ウォーカー方程式)に従います。
 \gamma_{k} = \phi_{1} \gamma_{k-1} + \phi_{2} \gamma_{k-2} + \cdots + \phi_{p} \gamma_{k-p}, k \geq q + 1
 \rho_{k} = \phi_{1} \rho_{k-1} + \phi_{2} \rho_{k-2} + \cdots + \phi_{p} \rho_{k-p}, k \geq q + 1

 ARMA過程の自己相関は指数的に減衰する。

 q+1次以降の自己共分散と自己相関は、ユール・ウォーカー方程式を解くことにより逐次的に求めることができる一方で、q次まではMA過程の影響があるため、表現することは難しいそうです。

 ARMA過程は、AR過程が定常とは限らない点、MA過程は常に定常である一方で任意のMA過程に同一の期待値と自己相関構造をもつ異なるMA過程が複数存在する点など問題点があります。

 自己相関構造のモデル化という点で、MAモデルを選択する基準が必要になるのですが、それが反転可能性という概念です。

 AR過程の定常性は、差分方程式と関連があり、AR過程と同一の係数をもつ差分方程式が安定的になる場合に、AR過程は定常になります。まず、AR(p)過程を再度記述します。

 \displaystyle y_{t} = c + \phi_{1} y_{t-1} + \phi_{2} y_{t-2} + \cdots + \phi_{p} y_{t-p} + \epsilon_{t}

 AR(p)過程の定常条件は前回記述した

 \displaystyle 1 - \phi_{1} z - \cdots - \phi_{p} z^{p} = 0

のすべての解の絶対値が1より大きいとき、AR過程は定常になります。この方程式は、AR特性方程式と呼ばれ、左辺の多項式はAR多項式と呼ばれます。沖本先生の例で言いますと、AR(1)過程

 y_{t} = c + \phi_{1} y_{t-1} + \epsilon_{t}

の定常性条件は、AR(1)過程のAR特性方程式

 1 - \phi_{1} z = 0

の解は z = \frac{1}{\phi_{1}}となりますから、 |\phi_{1}| < 1のとき、 |z| > 1となるので、|\phi_{1}| < 1となります。

 また、AR(2)過程

 y_{t} = 0.5 y_{t-1} + 0.5 y_{t-2} + \epsilon_{t}

の場合の特性方程式

 0 = 1 - 0.5 z - 0.5 z^{2} = (1 - z)(1 + 0.5z)

となりますので、解は-2と1となります。つまり、このAR(2)過程は非定常であることが分かります。

 MA過程は常に定常ですが、同一の期待値と自己相関構造をもつ異なるMA過程が存在することは既に上で話をしました。時系列モデルを利用する目的は、データの平均的な挙動と自己相関構造をモデル化することです。そのため、同一の期待値、自己相関構造をもつMAモデルが複数存在するときには、どのモデルを用いるかを決める基準として、MA過程の反転可能性があります。

 MA過程がAR(∞)過程に書き直せるとき、MA過程は反転可能といわれます。
 MA過程が反転可能であるとき、 \epsilon_{t}は過去の y_{t}の関数として表現でき、過去の yを用いて y_{t}を予測した時の予測誤差とも解釈できます。そのため、反転可能表現に伴う \epsilon_{t} y_{t}の本源的な撹乱項と呼ぶこともあります。

 この本源的な撹乱項を用いた場合、パラメータの推定や予測を自然な形で行うことができるので、同一の期待値、自己共分散構造をもつMA過程のうち、反転可能な過程を選択する方が望ましいということになります。

 同一の構造をもつMA(q)過程は一般的に 2^{q}個存在することが知られていますが、反転可能なものは1つしか存在しません。MA(q)過程の反転可能条件は、AR過程の定常条件と同様のものです。

 1 + \theta_{1} z + \theta_{2} z^{2} + \cdots + \theta_{p} z^{p} = 0

というMA特性方程式を用いて、この特性方程式のすべての解の絶対値が1より大きいとき、MA過程は反転可能となります。

 ARMA過程の定常性は、MA過程が常に定常であるので、ARMA過程はAR過程が定常であれば、定常なAR過程とMA過程の和として表現できるため、定常になります。したがって、AR過程の部分が定常であることが確認できればよいので、AR特性方程式のすべての解が1より大きければ、ARMA過程は定常になります。

 同様に、ARMA過程の反転可能性は、MA過程が反転可能性あればよいということになります。したがって、MA過程の部分のMA特性方程式のすべての解の絶対値が1より大きいとき、ARMA過程は反転可能となります。

安心アプリ

安心アプリ - Google Play の Android アプリ

 いよいよアプリを公開しました。

 アプリの内容ですが、知らない電話番号、電話帳に登録されていない番号から着信が入ると、AlertDialogが起動して、注意喚起をするという簡単なつくりです。

 主な目的は振り込め詐欺被害の防止です。

 電話帳に登録されているかを調べますので、電話帳、電話に対するpermissionが必要ですが、アプリ自体に通信機能がありません。ですから、電話帳のデータがリークすることはありません。

 よろしければ、おじいちゃん、おばあちゃんにおすすめしていただけると力を発揮できると思います。

プライバシー・ポリシー

当サイトおよびアプリでは、以下のとおり個人情報保護方針を定め、個人情報保護の仕組みを構築し、個人情報の保護を推進致します。

  • 個人情報の管理

当サイトおよびアプリでは、お客さまの個人情報を正確に保ち、個人情報への不正アクセス・紛失・破損・改ざん・漏洩などを防止するため、セキュリティシステムの維持・管理体制の整備の徹底等の必要な措置を講じ、安全対策を実施し個人情報の厳重な管理を行ないます。

  • お問合せ

当サイトおよびアプリは、苦情及び個人情報取扱い窓口を設置し適正かつ迅速に対応いたします。以下メールにてお問い合わせください(お手数ですが、必要な個所を@に置き換えてください)。

crimsonbach [at mark] yahoo.co.jp

時系列データ解析 06 ~ 自己回帰過程

www.asakura.co.jp

 沖本先生の本の続きです。MA過程の次はAR過程です。

 自己回帰(AR)過程は、過程が自己の過去に回帰されたモデルで表現されるものです。1次のAR過程、AR(1)は、

 \displaystyle y_{t} = c + \phi_{1} y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{2})

で定義され、 y_{t} \sim AR(1) と表記されます。

 AR過程は代表的な弱定常過程である

 \displaystyle y_{t} = \mu + \epsilon_{t}

に、 \phi_{1} y_{t-1} が追加された形をしていて、そのため y_{t} y_{t-1} が相関をもつことになります。

 AR過程の確率的変動は、MA過程と同様、撹乱項のホワイトノイズ  \epsilon_{t} によって決まります。 \epsilon が決まり、続いて y が決まっていきます。

 AR過程では初期値の決定が問題になります。 y が分布が定まっている場合は、その分布に従う確率変数とし、 y の分布が定まっていない場合は、ある定数とすることが一般的です。ただ、定常過程では、初期値の影響は時間とともになくなるので、大きな問題にはなりません。

 沖本先生の本のとおり、具体例を示します。次のようなAR(1)を考えます。

 y_{t} = 1 + 0.5 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N.(1)

 初期値を  y_{0} = 0 として、 \epsilon_{1} = - 2.1 \epsilon_{2} = 0.9 とします。このときに順番に解いていくと、

 y_{1} = 1 + 0.5 y_{0} \epsilon_{1} = 1 + 0.5 \dot - 2.1 = - 1.1
 y_{2} = 1 + 0.5 y_{1} \epsilon_{2} = 1 + 0.5 \dot (-1.1) + 0.9 = 1.35

となります。

 ここで、MA(1) 過程と同様に、パラメータを調整したAR(1)過程をグラフ化してみます。RのコマンドについてはMA(1)で多少説明していますので、必要な部分のみ解説します。

 以下の6つのパラメータのAR(1)過程をグラフ化します。

 \displaystyle \begin{align}
&(a) y_{t} = 2 + 0.8 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{1}) \\ 
&(b) y_{t} = - 2 + 0.3 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{0.5}) \\
&(c) y_{t} = 0 - 0.3 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{2}) \\
&(d) y_{t} = - 2 - 0.8 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{1}) \\
&(e) y_{t} = 2 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{1}) \\
&(f) y_{t} = 2 + 1.1 y_{t-1} + \epsilon_{t}, \epsilon_{t} \sim W.N. (\sigma^{1}) \end{align}

 Rのコードです。arima.sim関数の第2引数ですが、今回はAR過程なので、order=c(1, 0, 0) となっています。ちなみに、MA過程の時はorder=c(0, 0, 1) でした。次にAR過程の係数はar=c(0.8)として指定します。

set.seed(1)
d1 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(0.8)), sd=1) +2
plot(d1, main="(a) c=2, Φ=0.8, σ=1")
set.seed(1)
d2 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(0.3)), sd=0.5) - 2
plot(d2, main="(b) c=-2, Φ=0.3, σ=0.5")
set.seed(1)
d3 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(-0.3)), sd=2)
plot(d3, main="(c) c=0, Φ-=0.3, σ=2")
set.seed(1)
d4 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(-0.8)), sd=1) - 2
plot(d4, main="(d) c=-2, Φ=-0.8, σ=1")
set.seed(1)
d5 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(1)), sd=1) + 2
plot(d5, main="(e) c=2, Φ=1, σ=1")
set.seed(1)
d6 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(1.1)), sd=1) + 2
plot(d6, main="(f) c=2, Φ=1.1, σ=1")

 グラフ化するとこのようになります。


 まず、(e)と(f)ですが、係数が 1以上の場合、AR過程は定常ではなくなるため、エラーで出力されません。そのため、グラフも出力されません。

 AR過程はMA過程と違い、パラメータの値によって定常か非定常かが変わります。先ほど、係数が 1以上と書きましたが、正確には係数 |\phi_{1}| \ge 1の時に非定常となります。
 ここではAR(1)過程が定常という場合のみを解説していきます。

 AR(1)過程の期待値についてです。グラフでは概ね期待値と定数項が一致しているようにも見えますが、実際は期待値と定数項は一致しません。(a)あたりでは定数項は 2ですが、やや下の方に過程の期待値があるようです。AR(1)過程の期待値をみています。

 \displaystyle y_{t} = c + \phi_{1} y_{t-1} + \epsilon_{t}
 \displaystyle E(y_{t}) = E(c + \phi_{1} y_{t-1} + \epsilon_{t}) = c + \phi_{1} E(y_{t-1})

ここで、ホワイトノイズの性質により E(\epsilon_{t}) = 0 です。また、 yは定常過程ですから、

 \displaystyle E(y_{t}) = E(y_{t-1}) = \mu

です。よって、AR(1)過程の期待値

 \displaystyle \mu = c + \phi_{1} \mu

 \displaystyle \mu = \frac{c}{(1 - \phi_{1})}

が得られます。

 次に分散ですが、MA過程と同様に過程の分散は撹乱項の分散 \sigma^{2}と異なります。MA(1)過程で使用した(a)のグラフと今回の(a)のグラフを比べてみます。ちなみに左側はMA(1)過程、右側がAR(1)過程です。


 撹乱項の分散は 1ですが、過程の変動幅は違います。AR(1)過程の方がやや大きいです。つまり、AR(1)過程の分散は撹乱項のそれより大きくなります。では分散を見ていきます。

 \displaystyle y_{t} = c + \phi_{1} y_{t-1} + \epsilon_{t}
 \displaystyle \begin{align} Var(y_{t}) &= Var(c + \phi_{1} y_{t-1} + \epsilon_{t}) \\
&= \phi_{1}^{2} y_{t-1}^{2} + 2 \phi_{1} y_{t-1} (c + \epsilon_{t} - \mu) + (c + \epsilon_{t} - \mu)^{2} \\
&= \phi_{1}^{2} Var(y_{t-1}) + 2 \phi_{1} Cov(y_{t-1} , \epsilon_{t}) + Var(\epsilon_{t}) \\
&= \phi_{1}^{2} Var(y_{t-1}) + \sigma^{2} \end{align}

 ここで、 y_{t}が定常であれば、 \displaystyle \gamma_{0} = Var(y_{t}) = Var(y_{t-1})ですので、AR(1)過程の分散は、
 \displaystyle \gamma_{0} = \frac{\sigma^{2}}{(1 - \phi_{1}^{2})}
となります。

 最後に自己相関をみてみましょう。先ほどのMA(1)過程のグラフ(a)とAR(1)過程のグラフを比べると、係数( \theta_{1} \phi_{1} はともに 0.8)は同じでもAR(1)過程のグラフがより滑らかであることがわかります。

 これは、この場合のように係数が同じとき、1次自己相関の絶対値はAR(1)過程の方が大きくなることと、AR(1)過程は2次以降の相関係数もすべて正になることの2点によるからです。

 AR過程のk次の自己共分散はこのようになります。

 \displaystyle \begin{align} \gamma_{k} &= Cov(y_{t} , y_{t-k}) \\
&= Cov(\phi_{1} y_{t-1} + \epsilon_{t} , y_{t-k}) \\
&= Cov(\phi_{1} y_{t-k} , y_{t-k}) + Cov(\epsilon_{t} , y_{t-k}) \\
&= \phi_{1} \gamma_{k-1} \end{align}

 ここで、両辺を \gamma_{0} で割ると、

 \displaystyle \gamma_{k} = \phi_{1} \rho_{k-1}

となり、これはユール・ウォーカー方程式と呼ばれます。ユール・ウォーカー方程式は、AR過程の自己相関が、 y_{t} が従うAR過程と同一の係数 \phi_{1} をもつ差分方程式に従うということを表しています。

 ユール・ウォーカー方程式と \rho_{0} = 1を用いて、AR過程の自己相関は順番に求めることができます。また、ユール・ウォーカー方程式により、AR(1)過程の一般的な自己相関は、

 \displaystyle \rho_{k} = \phi_{1}^{2} \rho_{k-2} = \phi_{1}^{3} \rho_{k-3} = \cdots = \phi_{1}^{k} \rho_{0} = \phi_{1}^{k}

となります。また、[ | \phi_{1} | < 1 より、AR(1)過程の自己相関の絶対値は指数的に減衰していきます。コレログラムを描いてみます。

set.seed(1)
d1 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(0.8)), sd=1)
acf(d1, main="(a)  θ1=0.8, θ2=0")
set.seed(1)
d2 <- arima.sim(n=100, model=list(order=c(1, 0, 0), ar=c(-0.8)), sd=1)
acf(d2, main="(a)  θ1=0.8, θ2=0")

 コレログラムを描くacf()関数とarima.sim()関数は以前に説明していますのでここでは割愛します。AR(1)過程の自己相関の絶対値は指数的に減衰していくことが、(a)と(b)からよくわかると思います。AR(1)過程のコレログラムは \phi_{1}の符号によって、減衰していくことが確認できます。

 AR(1)過程を一般化したp次のAR過程AR(p)はこのように記述されます。

 \displaystyle y_{t} = c + \phi_{1} y_{t-1} + \phi_{2} y_{t-2} + \cdots + \phi_{p} y_{t-p} + \epsilon_{t}

 AR(p)過程はy_{t} を定数と自身のp期過去の値に回帰したモデルになります。AR(1)同様にAR(q)もパラメータにより定常となるか非定常となるか変わってきます。定常AR(p)過程の性質は、

 \displaystyle \mu = E(y_{t}) = \frac{c}{1 - \phi_{1} - \phi_{2} - \cdots - \phi_{p}}
 \displaystyle \gamma_{0} = Var(y_{t}) = \frac{\sigma^{2}}{1 - \phi_{1} \rho_{1} - \phi_{2} \rho_{2} - \cdots - \phi_{p} \rho_{p}}
 \displaystyle \gamma_{k} = \phi_{1} \gamma_{k-1} + \phi_{2} \gamma_{k-2} + \cdots + \phi_{p} \gamma_{k-p}, k \ge 1
 \displaystyle \rho_{k} = \phi_{1} \rho_{k-1} + \phi_{2} \rho_{k-2} + \cdots + \phi_{p} \rho_{k-p}, k \ge 1

であり、特に \gamma_{k}はユール・ウォーカー方程式と呼ばれます。そして、自己相関が指数的に減衰していきます。

 ここで、AR(2)過程のユール・ウォーカー方程式は、このようになります。

 \displaystyle \rho_{k} = \phi_{1} \rho_{k-1} + \phi_{2} \rho_{k-2}

これに、 k=1を代入し、 \rho_{0} = 1 \rho_{1} = \rho_{-1}から

 \displaystyle \rho_{1} = \phi_{1} \rho_{1} + \phi_{2} \rho_{-1} = \phi_{1} + \phi_{2} \rho_{1}

が得られます。これを変形すると、

 \displaystyle \rho_{1} = \frac{\phi_{1}}{(1 - \phi_{2})}

となります。次に、 k=2を代入すると、

 \displaystyle \rho_{2} = \phi_{1} \rho_{1} + \phi_{2} \rho_{0} = \frac{\phi_{1}^{2}}{1 - \phi_{2}} + \phi_{2} = \frac{\phi_{1}^{2} + \phi_{2} - \phi_{2}^{2}}{1 - \phi_{2}}

となり、 \rho_{2} を求めることができます。AR(2)過程の場合は、特に \rho_{k}の一般的な

 \displaystyle 1 - \phi_{1} z - \phi_{2} z^{2} = 0

という2次方程式が異なる2つの解をもつとき、その解の逆数を \lambda_{1} \lambda_{2}とすれば

 \displaystyle \rho_{k} = \frac{(1 - \lambda_{2}^{2}) \lambda_{1}^{k+1} - (1 - \lambda_{1}^{2}) \lambda_{2}^{k+1}}{(\lambda_{1} - \lambda_{2})(1 + \lambda_{1} \lambda_{2})}

で自己相関係数が得られます。

 AR(p)の場合、 \rho_{1}から \rho_{k-1}までは、ユール・ウォーカー方程式と \rho_{0} = 1 \rho_{k} = \rho_{-k}から p-1次の連立方程式を解いて求めることができます。

 AR(2)過程のコレログラムを図示します。

set.seed(1)
d3 <- arima.sim(n=100, model=list(order=c(2, 0, 0), ar=c(0.5, 0.35)), sd=1)
acf(d3, main="(a)  θ1=0.5, θ2=0.35")
set.seed(1)
d4 <- arima.sim(n=100, model=list(order=c(2, 0, 0), ar=c(0.1, 0.5)), sd=1)
acf(d4, main="(a)  θ1=0.1, θ2=0.5")
set.seed(1)
d5 <- arima.sim(n=100, model=list(order=c(2, 0, 0), ar=c(0.5, -0.8)), sd=1)
acf(d5, main="(a)  θ1=0.5, θ2=-0.8")
set.seed(1)
d6 <- arima.sim(n=100, model=list(order=c(2, 0, 0), ar=c(0.9, -0.8)), sd=1)
acf(d6, main="(a)  θ1=0.9, θ2=-0.8")

 AR(1)同様に自己相関の絶対値は指数的に減衰しています。パラメータの組み合わせによって、多様な自己相関構造が記述できます。(e)と(f)をみると、AR(2)が循環的な自己相関構造を記述できることがわかると思います。先ほどの

 \displaystyle 1 - \phi_{1} z - \phi_{2} z^{2} = 0

が共役な複素数 a \pm b \mathrm{i}を解にもつときに、自己相関は循環的になります。そして、その周期は

 \frac{2 \pi}{\cos^{-1} \left[ a / \sqrt{a^{2} + b^{2}}\right]}

となります。これは、AR(p)過程にも拡張でき、

 \displaystyle 1 - \phi_{1} z - \cdots - \phi_{p} z^{p} = 0

が共役な複素数を解にもつときにも同様の循環成分をもちます。AR(p)の自己相関は、最大 \frac{p}{2}個の循環成分を持つことができるのです。

時系列データ解析 05 ~ 移動平均過程

www.asakura.co.jp


 沖元先生の本の続きです。ここでは、移動平均モデルについての話からです。

 まず、1次MA過程(以下、MA(1)過程)はこのようにモデル化されます。

 \displaystyle y_{t} = \mu + \epsilon_{t} + \theta_{1} \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{2})

 移動平均過程は、ホワイトノイズの線形和になります。

 ホワイトノイズはすべての時点において期待値が  0 で、分散が一定の確率過程です。また、自己相関をもたないので、弱定常過程となります。

  y_{t} が、移動平均過程に従うことを、  y_{t} \sim MA(1) とあらわします。

 もっとも基本的な弱定常過程は

 \displaystyle y_{t} = \mu + \epsilon_{t}, \epsilon \sim W.N. (\sigma^{2} )

ですから、移動平均過程はこの過程に、  \theta_{1} \epsilon_{t - 1} という項が追加されたものになります。

 また、 t期のMA(1)過程と  t - 1 期のMA(1)を比較してみます。

 \displaystyle y_{t} = \mu + \epsilon_{t} + \theta_{1} \epsilon_{t - 1}
 \displaystyle y_{t - 1} = \mu + \epsilon_{t - 1} + \theta_{1} \epsilon_{t - 2}

 この二つのモデルは、  \epsilon_{t - 1} という項を共通にもちます。そのため、  y_{t} y_{t - 1} に相関が生じます。

 そして、MA(1)過程は、弱定常過程と比べて、 \theta_{1} というパラメータが多くなっています。このパラメータが、自己相関の強さを決定づけます。

 MA(1)過程の確率変動は、ホワイトノイズ  \epsilon_{t} によって決まります。まず、  \epsilon が決まり、続いて  y が決まっていきます。沖本先生の本では、具体例として、

 \displaystyle y_{t} = 1 + \epsilon_{t} + 0.5 \epsilon_{t - 1}, \epsilon_{t} \sim W.N.(1)

というMA(1)過程で、  \epsilon_{0} = 0.5 \epsilon_{1} = - 2.1 \epsilon_{2} = 0.9 とすれば、

 \displaystyle y_{1} = 1 + \epsilon_{1} + 0.5 \epsilon_{0} = 1 - 2.1 + 0.5 \cdot 0.5 = - 0.85

 \displaystyle y_{2} = 1 + \epsilon_{2} + 0.5 \epsilon_{1} = 1 + 0.9 + 0.5 \cdot ( - 2.1 ) = - 0.85

という順番で  y が決められることが示されています。

 ここで、  \epsilon を正規ホワイトノイズとして、それぞれのパラメータの組み合わせたMA(1)をグラフ化してみます。

 Rを使って、シミュレーションをしてみます。Rにはさまざまなツールが用意されていまして、ここではARIMAを例にMA(1)過程をみていきます。ちなみに、ARIMA過程はこのようなコードで記述します。ARIMA過程そのものについては次回以降に説明します。

d <- arima.sim(n=100, model=list(order=c(2, 0, 4), ar=c(0.1, 5), ma=c(-0.2, 0.3, 0.4, -0.5)), sd=1)

 arima.simの引数について説明します。n=100がシミュレーションの数です。今回の話の流れでは、時系列のデータを  T = 100 まで実験してみるということです。

 第2引数は、model=list()となっていますが、中身をみていきます。order=()の引数は、自己回帰過程、和分過程、移動平均過程の次数になります。自己回帰過程、和分過程は後述します。ar=()の引数は、係数で、ma=()も同様です。

 第3引数については、分散の大きさといいますか、上の例では分散も標準偏差(標準誤差)も  1です。

 それでは、シミュレーションしてみます。沖本先生の例にならって6パターンにします。

 \displaystyle \begin{align}
&(a) y_{t} = 0 + \epsilon_{t} + 0.8 \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{1}) \\
&(b) y_{t} = 2 + \epsilon_{t} + 0.5 \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{0.5}) \\
&(c) y_{t} = - 2 + \epsilon_{t} + 0.3 \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{2}) \\
&(d) y_{t} = 0 + \epsilon_{t} - 0.3 \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{1}) \\
&(e) y_{t} = 2 + \epsilon_{t} - 0.5 \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{0.5}) \\
&(f) y_{t} = - 2 + \epsilon_{t} - 0.8 \epsilon_{t - 1}, \epsilon \sim W.N. (\sigma^{2}) \end{align}

 ついでにコードも記述します。

set.seed(1)
d1 <- arima.sim(n=100, model=list(order=c(0, 0, 1), ma=c(0.2)), sd=1)
plot(d1, main="(a) μ=0, θ=0.8, σ=1")
set.seed(1)
d2 <- arima.sim(n=100, model=list(order=c(0, 0, 1), ma=c(0.5)), sd=0.5) + 2
plot(d2, main="(b) μ=2, θ=0.5, σ=0.5")
set.seed(1)
d3 <- arima.sim(n=100, model=list(order=c(0, 0, 1), ma=c(0.3)), sd=2) - 2
plot(d3, main="(c) μ=-2, θ=0.3, σ=2")
set.seed(1)
d4 <- arima.sim(n=100, model=list(order=c(0, 0, 1), ma=c(-0.3)), sd=1)
plot(d4, main="(d) μ=0, θ=-0.3, σ=1")
set.seed(1)
d5 <- arima.sim(n=100, model=list(order=c(0, 0, 1), ma=c(-0.5)), sd=0.5) + 2
plot(d5, main="(e) μ=2, θ=-0.5, σ=0.5")
set.seed(1)
d6 <- arima.sim(n=100, model=list(order=c(0, 0, 1), ma=c(-0.8)), sd=2) - 2
plot(d6, main="(f) μ=-2, θ=-0.8, σ=2")

 ar=()については省略しています。plot()はグラフに表示するコマンドです。main=""はグラフのタイトルのようなものです。set.seet(1)は、乱数種を指定して乱数を発生させる関数です。すべて同じ乱数を発生させてシミュレーションしています。

 グラフ化するとこうなります。



 グラフから確認できることは、系列が  \mu の周りを変動していることです。つまり、MA(1)過程の期待値は  \mu になります。

 \begin{align} E(y_{t}) &= E(\mu + \epsilon_{t} + \theta_{1} \epsilon_{t - 1}) \\
&= E(\mu) + E(\epsilon_{t}) + E(\theta_{1} \epsilon_{t - 1}) \\
&= E(\mu) + E(\epsilon_{t}) + \theta_{1} E(\epsilon_{t - 1}) \\
&= \mu \because E(\epsilon_{t}) = 0, E(\epsilon_{t - 1}) = 0)\end{align}

 次にMA(1)過程の分散は、撹乱項  \epsilon_{t} の分散  \sigma^{2} に等しいと既に説明しました。しかし、グラフを見ると分かるとおり、撹乱項の分散が同じでも若干変動幅が異なることが分かると思います。

 つまり、MA(1)過程分散は、撹乱項の分散よりも大きくなるのです。

 \displaystyle y_{t} - \mu = \epsilon_{t} - \theta_{1} \epsilon_{t - 1}

 \begin{align} (y_{t} - \mu)^{2} &= (\epsilon_{t} - \theta_{1} \epsilon_{t - 1})^{2} \\
&= \epsilon_{t}^{2} - 2 \theta_{1} \epsilon_{t} \epsilon_{t - 1} + \theta_{1}^{2} \epsilon_{t - 1}^{2} \end{align}

 \begin{align}
\gamma_{0} &= E \left[ \epsilon_{t}^{2} \right] - 2 E \left[ \theta_{1} \epsilon_{t} \epsilon_{t - 1} \right] + \theta_{1}^{2} E \left[ \epsilon_{t - 1}^{2} \right] \\
&= (1 + \theta_{1}^{2} ) \sigma^{2} \end{align}

 したがって、MA(1)過程の分散は \theta_{1}^{2} \sigma^{2} だけ、撹乱項の分散よりも大きくなります。

 また、グラフの滑らかさは  \theta_{1} が大きくなるにしたがって、より滑らかになります。(a)が一番滑らかであり、(f)が一番ギザギザしているます。これは、MA(1)過程の自己相関が  0 でないためです。MA(1)過程をもう一度見てみましょう。

 \displaystyle y_{t} = \mu + \epsilon_{t} + \theta_{1} \epsilon_{t - 1}, \epsilon
 \displaystyle y_{t - 1} = \mu + \epsilon_{t - 1} + \theta_{1} \epsilon_{t - 2}

  \theta_{1} が 正の場合、 y_{t} \theta_{1} \epsilon_{t-1} y_{t-1} \epsilon_{t-1} が同じ正の符号になるため、 y_{t} y_{t-1} が同じ方向に動く傾向をもちます。

 その結果、1次の正の自己相関が生じて、その自己相関は  \theta_{1} の値が  1 に近づくほど強くなります。

 逆に、 \theta_{1} が負の場合は、  y_{t} y_{t-1} は逆に動く傾向があり、1次の負の自己相関が生まれます。そのため、グラフはよりギザギザになります。

 次に、MA(1)過程の自己共分散を求めます。

 \displaystyle \begin{align} \gamma_{1} &= Cov( y_{t}, y_{t-1}) \\
&= Cov(\mu + \epsilon_{t} + \theta_{1} \epsilon_{t-1},  \mu + \epsilon_{t-1} + \theta_{1} \epsilon_{t-2} \\
&= Cov(\epsilon_{t},  \epsilon_{t-1}) + Cov(\epsilon_{t},  \theta_{1} \epsilon_{t-2} ) + Cov(\theta_{1} \epsilon_{t-1} , \epsilon_{t-1}) + Cov(\theta_{1} \epsilon_{t-1}, \theta_{t-1} \epsilon_{t-2}) \\
&= \theta_{1} Cov(\epsilon_{t-1}, \epsilon_{t-1}) \\
&= \theta_{1} \sigma_{2} \end{align}

 これは、ホワイトノイズ \epsilon k 次の自己共分散が  0 であり、分散が  0 であることから導けます。

 よって、MA(1)過程の1次自己相関を求めると、

 \displaystyle \rho_{1} = \frac{\gamma_{1}}{\gamma_{0}} = \frac{\theta_{1}}{1 + \theta_{1}^{2}}

となります。MA(1)過程の自己相関が  \mp 1 のとき、 \frac{1}{2} になります。したがいまして、1次の自己相関の絶対値が \frac{1}{2} より大きいMA(1)過程はモデル化できません。

 MA(1)過程の2次以降の自己共分散は、

 \displaystyle \begin{align} \gamma_{k} &= Cov(y_{t},  y_{t-k}) \\
&= Cov(\mu + \epsilon_{t} + \theta_{1} \epsilon_{t-1}, \mu + \epsilon_{t-k} + \theta_{1} \epsilon_{t-k-1}) \\
&=0 \end{align}

となります。つまり、MA(1)過程の2次以降の自己相関は  0 ということです。そのため、MA(1)過程は1次の自己相関をモデル化できますが、2次以降の自己相関を記述することはできません。

 以上のとおり、期待値も分散も  t によらず、定常性の性質を満たすことがわかりました。

 最後に、MA(1)過程のコレログラムを確認してみます。まずRのコードを記載しておきます。

set.seed(1)
d1 <- arima.sim(n=100, model=list(order=c(0, 0, 2), ma=c(0.2, 0)), sd=1)
acf(d1, main="(a)  θ1=0.8, θ2=0")
set.seed(1)
d2 <- arima.sim(n=100, model=list(order=c(0, 0, 2), ma=c(-0.8, 0)), sd=1)
acf(d2, main="(b) θ1=-0.8, θ2=0")
set.seed(1)
d3 <- arima.sim(n=100, model=list(order=c(0, 0, 2), ma=c(0.8, 0.3)), sd=1)
acf(d3, main="(c) θ1=0.8, θ2=0.3")
set.seed(1)
d4 <- arima.sim(n=100, model=list(order=c(0, 0, 2), ma=c(0.3, 0.8)), sd=1)
acf(d4, main="(d) θ1=0.3, θ2=0.8")
set.seed(1)
d5 <- arima.sim(n=100, model=list(order=c(0, 0, 2), ma=c(0.8, -0.3)), sd=1)
acf(d5, main="(e) θ1=0.8, θ2=-0.3")
set.seed(1)
d6 <- arima.sim(n=100, model=list(order=c(0, 0, 2), ma=c(-0.8, -0.3)), sd=1)
acf(d6, main="(f)  θ1=-0.8, θ2=-0.3")

 acf()は自己相関関数、つまりコレログラムを表しています。先ほどのarima.sim()の中身ですが、今回はMA(1)過程のオーダーを2にしていますので、ma=c()中に指定する係数も2つになっています。


 まず、それぞれの係数 [tex \theta] を見るとコレログラムが係数に応じて変化しているのがわかると思います。(a)、(b)については、1次のみ有意な自己相関があり、2次以降は有意な自己相関はありません。

 一般化した q移動平均過程は、

 y_{t} = \mu + \epsilon_{t} + \theta_{1} \epsilon_{t-1} + \theta_{2} \epsilon_{t-2} + \cdots + \theta_{q} \epsilon_{t-q}, \epsilon_{t} \sim W.N.(\sigma^{2})

と表現でき、MA(q)過程と書きます。MA(q)過程は、現在とq期間の過去のホワイトノイズの線形和に定数 \mu を加えたものになります。

 MA(q)過程の性質は、

 \displaystyle E(y_{t}) = \mu

 \displaystyle \gamma_{0} = Var(y_{t}) = (1 + \theta_{1}^{2} + \theta_{2}^{2} + \cdots + \theta_{q}^{2}) \sigma^{2}

 \displaystyle \gamma_{k} = \begin{cases}{} (\theta_{k} + \theta_{1} \theta_{k+1} + \cdots + \theta_{q-k} \theta_{q}) \sigma^{2}, &1 \leq k \leq q \\ 0, & k \geq q + 1 \end{cases}

 \displaystyle \rho_{k} = \begin{cases}{} \frac{\theta_{k} + \theta_{1} \theta_{k+1} + \cdots + \theta_{q-k} \theta_{q}}{1 + \theta_{1}^{2} + \theta_{2}^{2} + \cdots + \theta_{q}^{2}}, & 1 \leq k \leq q \\ 0, & k \geq q + 1 \end{cases}

であり、重要なことはMA過程は定常過程であり、MA(q)過程のq+1次以降の自己相関は  0 ということです。したがいまして、q次の自己相関構造をモデル化するには、q個のパラメータが必要になります。長期間の自己相関のモデル化はあまりに多くのパラメータが必要になるため、望ましいことではありません。

 次回は自己相関過程の話に移ります。

PhoneStateListenerの作成 03

 前回PhoneStateListenerをつかって、着信状態に応じてToast表示する方法を実装しました。

 今回はMainActivity.javaは省略して、PhoneReceiver.javaをこのように実装しなおしました。

 

 PhoneReceiver.java

import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class PhoneReceiver extends PhoneStateListener {
private Context context;
PhoneReceiver(Context context) {
this.context = context;
}

// 通話状態の変化に応じて表示を変更する
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);

switch (state) {
// 着信時の処理内容
case TelephonyManager.CALL_STATE_RINGING:

Cursor addressTable = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (addressTable != null) {
while (addressTable.moveToNext()) {
String phoneNumber = addressTable.getString(addressTable.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (PhoneNumberUtils.compare(phoneNumber, incomingNumber)) {
Toast.makeText(context, "OK!" + incomingNumber, Toast.LENGTH_LONG).show();
}
}
addressTable.close();
}
break;

// 通話時の処理内容
case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(context, "通話中!!" + incomingNumber, Toast.LENGTH_LONG).show();
break;
}
}
}

 実は更新をしていなかった間に、色々変更に変更を重ねて、このかたちになりました。

 当初、ContentResolverを用いて、データを抽出する作業をしていましたが、問題はデータの保存でして。

 SQLite、realmで保存方法を試行錯誤しましたが、どうしても納得できるかたちにできず。

 最終的に、Stack Overflowを参考にすると、PhoneNumberUtilsという便利なAPIを見つけることができました。

 日本語の解説がほぼなかったので、見つけるのに難儀しました。