マツリさんの日記

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

時系列データ解析 04 ~ 自己相関の検定

www.asakura.co.jp


 沖本先生の本の続きです。今回は自己相関の検定です。
 そもそも時系列データに自己相関構造がなければ、モデルを構築して、そのモデルから予測を行うことも難しくなります。
 そのため、自己相関の有無を調べることは、大変重要なことになります。

 定常過程での基本統計量は、時点に依存しないため、標本統計量からすぐに計算できます。

 期待値、自己共分散、自己相関係数は、それぞれ次のようになります。

 \displaystyle \overline{y} = \frac{1}{T} \sum^{T}_{t = 1} y_{t}

 \displaystyle \hat{\gamma}_{k} = \frac{1}{T} \sum^{T}_{t = k + 1} (y_{t} - \overline{y }) (y_{t-k} - \overline{y} ), k = 0, 1, 2, \cdots

 \displaystyle \hat{\rho}_{k} = \frac{\hat{\gamma}_{k}}{\hat{\gamma}_{0}}, k = 1, 2, 3, \cdots

 それぞれ、標本平均、標本自己共分散、標本自己相関係数と呼ばれます。

 この標本自己相関係数  \hat{\rho}_{k} を使って、仮説検定を行います。
 帰無仮説は  H_{0} : \rho_{k} = 0、つまり自己相関がないということです。
 対立仮説は、  H_{1} : \rho \neq 0で、自己相関が  0 ではないということです。

 特に、  y_{t} iid 系列の場合、標本自己相関係数  \hat{\rho}_{k} が、漸近的に平均  0 、分散  \frac{1}{T}正規分布に従うそうです。これについて、統計量を求めることになります。

 複数の  k に関して、自己相関係数  \rho_{k} 0 の場合を検定する場合は、帰無仮説は  H_{0} : \rho_{1} = \rho_{2} = \cdots = \rho_{m} = 0 となります。対立仮説は、  H_{1} : 少なくとも一つの  k \in \left[ 1, m \right]において  \rho_{k} = 0 となります。

 この検定は、かばん検定(portmanteau test)と呼ばれ、Ljung Box検定が有名です。
 Ljung Box検定の統計量は、

 \displaystyle Q(m) = T(T + 2) \sum^{m}_{k = 1} \frac{\hat{\rho}^{2}_{k}}{T - k} \sim \chi^{2} (m)

が一定の条件のもとで成立します。この検定では、小さい  m を選択すると高次の自己相関を見逃したり、大きい  m を選択すると検定の検出力が小さくなるなどの問題があります。
  m を選択する際の目安として、  m \approx log(T) がありますが、実務的には複数の  m に関して検定を行っているようです。

 今更ですが、正規分布確率密度関数

 \displaystyle f(x) = \frac{1}{\sqrt{2 \pi} \sigma} exp \left\{ - \frac{(x - \mu)^{2}}{2 \sigma^{2}} \right\}

と表現されます。
 これで第1章は終了です。

PhoneStateListenerの作成 02

 毎度、小出しにしていますが、PhoneStateListenerの続きです。

 前回の実装では、バックグラウンド時にはToastが表示されないという点で未完成でした。

 そこで、今回は他のサイトのBroadcastReceiver、IntentFilterを参考にして、バックグランドでも通話状態を捕捉するように実装しなおしました。

MainActivity.java

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class MainActivity extends AppCompatActivity {

// 各フィールドの設定
PhoneReceiver phoneStateListener;
TelephonyManager manager;

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

// PhoneReceiverインスタンスの生成
phoneStateListener = new PhoneReceiver(this);
// TelephonyManagerインスタンスの生成(Context.TELEPHONY_SERVICEを指定)
manager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE));
}

@Override
protected void onResume() {
super.onResume();
manager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}

PhoneReceiver.java

import android.content.Context;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class PhoneReceiver extends PhoneStateListener {
Context context;
public 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:
Toast.makeText(context, "着信中!!" + incomingNumber, Toast.LENGTH_LONG).show();
break;
// 通話時の処理内容
case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(context, "通話中!!" + incomingNumber, Toast.LENGTH_LONG).show();
break;
}
}
}

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;
}
}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ma2ri.telephonecontrol">

<!-- 電話番号、通話の状態などの端末情報を取得するためのパーミッション -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.example.ma2ri.telephonecontrol.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 着信などを制御するBroadcastReceiver -->
<receiver android:name=".PhoneStateReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>

</application>

</manifest>

 ここで、前回のマニフェストに登場したBroadcastReceiverがやっと日の目を見ました。

 BroadcastReceiverを実装することで、以前ではアプリの起動時しか通話状態の変化に対応できなかったものが、バックグラウンド時でも対応できるようになったという訳でございます。

 次は、Toast表示を別の物に変更するということと、電話帳へのアクセスあたりをやりたいと思います。

PhoneStateListenerの作成 01

 android studio 2.3がリリースされました。

 Cameraアプリは難しいので、PhoneStateListenerを使ったアプリの作成に移ります。

 まだまだ未完成ですが、一歩進めたのでMainActivityを載せておきます。

MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

// TelephonyManagerインスタンスを生成
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}

public PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
phoneCallEvent(state, incomingNumber);
}
};

// 通話状態に応じて、Toastを表示する
private void phoneCallEvent(int state, String incomingNumber) {
switch (state) {
// 着信時の表示内容
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(this, "着信中!" + incomingNumber, Toast.LENGTH_LONG).show();
break;
// 通話中の表示内容
case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(this, "通話中!" + incomingNumber, Toast.LENGTH_LONG).show();
break;
}
}
}

 要するに、端末に着信が入った時と、端末が通話中の時にToast表示されるという単純な仕組みです。

 当然、通話状態などの端末情報にアクセスするために、マニフェストをこのように書き換えます。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ma2ri.telephonecontrol">

<!-- 電話番号、通話の状態などの端末情報を取得するためのパーミッション -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.example.ma2ri.telephonecontrol.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 着信などを制御するBroadcastReceiver
<receiver android:name="com.example.ma2ri.telephonecontrol.MainActivity$InComingCall">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver> -->

</application>

</manifest>

 マニフェストの最後にBroadcastReceiverのコメントアウトがありますが、気にしないで下さい。まだまだ試行錯誤中です。

 ただ、この状態だと、待ち受けの状態にならないんです。そこを次に修正したいと思います。

時系列データ解析 03~ 復習

 少し駆け足でしたので、主に式の展開を詳しく説明します。

 まず、確率変数  x の期待値はこのように書けます。

 \displaystyle E ( x ) = \overline{ x } = \sum_{i = 1}^{n} x_{i} = \frac{x_{1} + x_{2} + \cdots + x_{n}}{n}

 分散はこのようになります。

 \displaystyle V ( x ) = E \left[ \left\{ x - E (x) \right\}^{2} \right] = \frac{(x_{1} - \overline{x}) + (x_{2} - \overline{x}) + \cdots + (x_{n} - \overline{x})}{n - 1}

 \displaystyle V ( x ) = E ( x^{2} ) - \left\{ E ( x ) \right\}^2

  i 番目の変数との偏差を  n - 1 あるいは  n で割ったものが分散です。分散の平方根標準偏差といいます。

 以上は  1 変数ですが、 2 変数の場合、分散は変数同士の関連具合を表す共分散となります。

 \displaystyle Cov ( x, y ) = E \left[ \left\{ x - E( x ) \right\} \left\{ y - E ( y ) \right\} \right] = E \left[ ( x - \overline{x} ) ( y - \overline{y} ) \right]

 \displaystyle Cov ( x, y ) = E( x y ) - E ( x ) E ( y )

 共分散は分散を一般化したもので、自己共分散は共分散の時系列データバージョンです。

 あらためて、 k 次の自己共分散  \gamma_{k} を記述します。

 \displaystyle \gamma_{k t} = Cov ( t_{t} , y_{t - k} ) = E \left[ ( y_{t} - \mu_{t} ) ( y_{t - k} - \mu_{t - k} ) \right]

 変数  y_{t} k 次の変数  y_{t - k}との関連具合を表しています。

 続いて、相関係数はこのように記述されます。

 \displaystyle \rho_{x y} = \frac{Cov (x, y)}{\sqrt{V(x)} \sqrt{V(y)}}

 相関係数(ピアソンの積率相関係数)は、共分散をそれぞれの標準偏差で基準化したものになります。もう少し詳しく書くとこうなります。

 \displaystyle \rho_{x y} = \frac{\sum ( x - \overline{x} ) ( y - \overline{y} )}{\sqrt{\sum( x - \overline{x} )^{2}}\sqrt{\sum ( y - \overline{y} )^{2}}}

 相関係数 \rho \leq 1 となりますが、それは上の式をコーシー・シュワルツの不等式( \mid (x, y ) \mid \leq \mid x \mid \cdot \mid y \mid)により展開することで確認できます。

 \displaystyle \mid \sum ( x - \overline{x} ) ( y - \overline{y} ) \mid \leq \sqrt{\sum( x - \overline{x} )^{2}}\sqrt{\sum ( y - \overline{y} )^{2}}

 \frac{\mid \sum ( x - \overline{x} ) ( y - \overline{y} ) \mid}{\sqrt{\sum( x - \overline{x} )^{2}}\sqrt{\sum ( y - \overline{y} )^{2}}} \leq 1

 ここで、 k 次の相関係数をあらためて記述します。

 \displaystyle \rho_{k t} = \frac{Cov ( y_{t}, y_{t - k} )} { \sqrt{V (y_{t}) \cdot V ( y_{t - k} )}}

 時系列データの自己相関係数相関係数と同じものであることが分かるのではないかと思います。

時系列データ解析 02 ~ 定常性

www.asakura.co.jp


 また、沖本先生の本の続きです。

 定常性は、時系列データの同時分布や基本統計量の時間不変性に関する性質です。


 まず、弱定常についてですが、任意の  t  k に対して、

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

 \displaystyle Cov ( y_{t}, y_{t - k} ) = E \left[ ( y_{t} - \mu ) ( y_{t - k} - \mu ) \right] = \rho_{k}

が成立する場合、その過程は弱定常(weak stationary)といいます。

 過程が弱定常のとき、自己相関係数はこのようになります。

 \displaystyle Corr ( y_{t} , y_{t - k} ) = \frac{\gamma_{k t}}{\sqrt{\gamma_{0 t} \gamma_{0, t - k }}} = \frac{\gamma_{k}}{\gamma_{0}} = \rho_{k}

 任意の  k に対して、 \gamma_{k} = \gamma_{- k} \rho_{k} = \rho_{-k}が成立します。

 弱定常性は、過程の期待値と自己共分散が時間を通じて一定になります。


 次に強定常性ですが、任意の  t  k に対して、  ( y_{t}, y_{t - 1}, . . . , y_{t - k} )^{'} の同時分布が同一となる場合、その過程は強定常性(strict stationary)といわれます。

 強定常過程の例として、iid系列があります。

 iid系列とは、各時点のデータが互いに独立でかつ同一の分布に従う系列のことです。

  y_{t} が期待値  \mu 、分散  \sigma^{2} のiid系列であることを  y_{t} \sim iid ( \mu, \sigma^{2} ) と表記します。

 iid系列より弱い仮定をおいたものがホワイトノイズになります。

 ホワイトノイズとは、すべての時点  t において、

 \displaystyle E ( \epsilon_{t} ) = 0

 \displaystyle \gamma_{ k } = E ( \epsilon_{t} \epsilon_{t - k} ) = \begin{cases}{} \sigma^{2}, & k = 0 \\ 0, & k \neq 0 \end{cases}

が成立するとき、 \epsilon_{t} はホワイトノイズ(white noise)といいます。

 ホワイトノイズはすべての時点において期待値が  0 で、かつ分散が一定であり、自己相関をもたないことが必要です。

時系列データ解析 01 ~ 自己共分散・自己相関係数

www.asakura.co.jp


 アプリの制作も少し疲れましたので、沖本先生の本のまとめも同時に行います。

 LaTexも大学以来なので覚束ないですね。


 時間の経過とととに観測されるデータを時系列データといいます。

 一時点に観測されるクロスセクションデータとは別のもののになります。

 クロスセクションデータと異なり、観測される順番に意味があります。

 時系列データにも基本統計量があります。

 期待値と分散は省略し、自己共分散(autocovariance)についてですが、これは同一の時系列データにおける異時点間の共分散になります。

 \displaystyle \gamma_{1 t} = Cov ( y_{t}, y_{t - 1} ) = E \left[ ( y_{t} - \mu_{t} ) ( y_{t - 1} - \mu_{t - 1} )\right]

 これは1次の自己共分散であり、2次以降の k 次に一般化するとこうなります。

 \displaystyle \gamma_{k t} = Cov ( y_{t}, y_{t - k} ) = E \left[ ( y_{t} - \mu_{t} ) ( y_{t - k} - \mu_{t - k} )\right]

この k 次自己共分散を k の関数と見たものを自己共分散関数といいます。

 そして、自己共分散を基準化した自己相関係数は、このようになります。
 \displaystyle \rho_{k t} = Corr ( y_{t}, y_{t - k} ) = \frac{Cov ( y_{t}, y_{t - k} )} { \sqrt{Var (y_{t}) \cdot Var ( y_{t - k} )}} = \frac{\gamma_{kt}}{\sqrt{\gamma_{0t}  \gamma_{0, t - k}}}

 自己相関係数も次数 k の関数とみたものを自己相関関数といいます。

 自己相関関数をグラフに書いたものはコレログラム(correlogram)です。

 子どもを迎えにいく時間ですのでこれくらいで。

Cameraアプリの実装01

 いよいよ、カメラアプリの実装に移ります。

 カメラアプリにはgoogleGitHubにサンプルコードを載せていますが、fragmentが使われていて、初心者の私にはなかなか手が出せません。

 したがいまして、様々なプログラマの方々が示してくださっているサンプルコードを参考にしながらやってみたいと思います。

 まず、結論ですが、カメラアプリ自体はコード上は様々なエラーを一つずつ取り除いてやっとコンパイルできました。

 しかし、実機でアプリが立ち上がらない・・・

 原因については現在勉強中ですが、setUpCameraOutputs()メソッドという端末のカメラの情報を取得する処理中で、cameraIdがnullになっているからみたいです。

 MainActivity.java

// 端末で利用できるカメラ情報の取得
private String setUpCameraOutputs(int width, int height) {
// getSystemServiceメソッドを使ってCameraManagerのインスタンスの取得(Context.CAMERA_SERVICEを指定)
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics
= manager.getCameraCharacteristics(cameraId);

// フロントカメラを利用しない
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
// ストリーム制御をサポートしていない場合、セットアップを中断する
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}

// 最大サイズでキャプチャする
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());

setUpPreview(map.getOutputSizes(SurfaceTexture.class),
width, height, largest);
configurePreviewTransform(width, height);

mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
ImageFormat.JPEG, /*maxImages*/2);
mImageReader.setOnImageAvailableListener(
new ImageReader.OnImageAvailableListener() {

@Override
public void onImageAvailable(ImageReader reader) {
File file = new File(getExternalFilesDir(null), "pic.jpg");
mThread.getHandler().post(new ImageStore(reader.acquireNextImage(), file));
}

}, mThread.getHandler());

return cameraId;
}
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (NullPointerException e) {
// Camera2 API未サポート
Log.e(TAG, "Camera Error:not support Camera2API");
}

return null;
}

 ちょっとこのあたりを修正していきたいと思いますが、なかなか時間もとれず悪戦苦闘しています。