今回は、Xamarin.Macへ対応するためのNuGetパッケージの定義ファイルの更新と、ReactivePropertyクラスのコンストラクタを変更して初期化時の性能を改善してみました。 そのため、結構がっつりとソースをいじってるのでPreリリースという形にしてみました。
もしよかったら試してみてください。
今回は、Xamarin.Macへ対応するためのNuGetパッケージの定義ファイルの更新と、ReactivePropertyクラスのコンストラクタを変更して初期化時の性能を改善してみました。 そのため、結構がっつりとソースをいじってるのでPreリリースという形にしてみました。
もしよかったら試してみてください。
のいえさんがコードを綺麗にしてくれたので、それを取り込んでテストしてOKだったのでそのままリリースしました。
とりあえず、現行正式版から初期化時間を1/4以下にしたので遅いって思ってる人は試してみてください。
.NET 4.6のバイナリないの?っていわれたので作りました。 ついでに依存してるNuGetのパッケージのアップデートも行いました。
FreetelさんがKATANA01を日本最速で出す!と言っていたら、YAMADA電機さんがEveryPhoneをそれより前に出すとしれっと出してきたかと思ったら、マウスコンピュータさんがMADOSMAに対して3000円でWindows 10 Mobileにアップデートしますよと言って来たり、ここ数日Windows 10 Mobileまわりは楽しい感じになってきてます。そんな中、私もMADOSMAをWindows 10 Mobileにしてきました。
壁紙がデフォルトでWindows 10と同じあたり統一感を感じます。Windows 10からは、同じOSという立ち位置のMobile版という雰囲気を醸し出しているのですが、壁紙1つとってもその雰囲気を感じ取ることができますね。スタート画面はこんな感じになります。壁紙を背景にして、前に半透明のタイルが置かれてるのがデフォルトの見た目になります。Windows Phone 8.1のころみたいに背景をタイルからのぞき窓のようにのぞくような見た目にも設定をいじることでできるようになっています。ここら辺は好みに合わせてカスタマイズ可能です。
Windows 10の目玉機能の1つであるコルタナさんも、日本語に対応した状態で登場してきました。Windows 10 Mobileが日本に上陸するタイミングでギリギリセーフでリリースされました。コルタナさんは、AndroidやiOSにもある音声によるパーソナルアシスタント機能で、色々なことをしてくれます。例えば「カレンダーに予定を入れたいんだけど」と話しかけると以下のような感じでカレンダーの予定を作ろうと色々訪ねてきてくれます。
その他に「歌ってください」とかいうと歌ってくれたりもします。
正直、まだSiriとかみたいにこなれていませんが、これからどんどん賢くなっていくと思うので色々話しかけてあげるといいと思います。
スマートフォンですること?Twitterに決まってるでしょう。ということで、結構な人がスマートフォンを使ってTwitterをしていると思います。ここでは、私のお勧めのTwitterクライアントを2つ紹介したいと思います。
まず最初は、Aristeaです。
マルチカラムのユーザーストリーム対応の高機能ツイッタークライアントです。流れるようなタイムラインがお好みの人にはとてもいいと思います。これを1つ買うだけで、パソコン版でも同じTwitterクライアントを使うことができるのでお得です。パソコン版はパソコン版でTwitterを楽しむための機能がよく作りこまれているクライアントになります。アカウント情報やタイムラインの設定などWindows 10とWindows 10 Mobile間で同期されるため、非常に使い勝手がよくできています。
Tweetiumは、Aristeaとは違いじっくりタイムラインを眺めるのに向いていると個人的には思います。Aristeaがタイムラインが流れていくようなイメージなのに対して、Tweetiumは、タイムラインを追いかけていくようなスタイルのTwitterクライアントになります。
こちらも、パソコン版と設定の同期などが可能になっていますが、個人的にはパソコン版の使い勝手は好きじゃないのでお勧めはそんなにしません…。Windows 10 Mobileのほうはとても使いやすいんですけどねぇ。
Tweetiumのいいところは抜群の安定感にあります。なかなか落ちない上に使いやすい。そんな素敵アプリケーションです。
一応公式クライアントもあります。まぁ一通りできるんでいいんじゃないんですかね。お勧めじゃぁないです。
話が突然マニアックな方向に行きますが、最近のモバイルの通信環境は7GBを超えると速度制限がかかるというのが一般的だと思います。そのため自分が今どれくらい使ってるのか把握したいという要望はよくあると思います。Windows 10 Mobileでは、この機能が非常にわかりにくいのですが結構いい感じに出してくれる機能があったりします。
「すべての設定」→「ネットワークとワイヤレス」のデータ使用状況を長押しするとスタート画面にピン留めできるようになります。(普通気づかないよ!)
編集のリンクをクリックすると、通信料の算出の開始日とか細かい設定ができるようになるので、ここで自分の状況に応じた設定をしておくと捗ります。
Windows 10 MobileではOfficeが使えます。最近は、Microsoftの姿勢が変わってきて、より多くの人に使ってもらうためにAndroid版とかiOS版も出てるのでWindows 10 MobileにOfficeがついているのは他のOSに比べて取り立てて強みだ!と言えるようなものではないですが、Word, PowerPoint, Excel, OneNoteが他のOSと同じ感覚で使えるのはとてもいいことだと思います。
特にPowerPointは、発表資料の脳内リハーサルや人の発表資料を閲覧出来るため、個人的にスマホのOfficeの中で一番よく使うものになります。再現性もかなり高く本当にパソコンとそん色なく閲覧ができます。ExcelとWordは、個人的にはあまり使わないかな?どういう用途があるのでしょうか。仕事なんかでExcelやWord文書をよく使う人は閲覧にいいかもしれません。MADOSMAではサポートされていませんが、Continuumで大きなディスプレイとマウスとキーボードと接続したときには、とても強力なツールになることは間違いないと思います。
OneNoteは、メモを取るのに結構便利なアプリケーションです。愛用しています。Windows 10とWindows 10 Mobileでデータを共有できるので必要だと思ったことなんかをPCとかでささっとメモっておいたり、出先でちょっとしたことをOneNoteにメモっておくなど色々ノートをとるのに便利です。私の場合は、Microsoft MVP Global Summit 2015でアメリカに行くために必要な情報なんかをOneNoteにまとめておいて管理するなどという使い方をしました。PCでWebページの重要な情報をOneNoteに張り付けて保存して、出先でWindows Phone(その当時は8.1でした)で確認するという使い方をしていました。Windows 10 Mobileでも同じような使い方ができると思います。
これは、パソコンでOneNoteに手書きで書きなぐったメモをWindows 10 MobileのOneNoteで見ているものです。こういうものもきちんと再現してくれるあたりすごい便利です。
ちょっとスクリーンショットを載せれないので文章だけになってしまいますが、メールとカレンダーのアプリもWindows 10 MobileとWindows 10で共通化されています。使い勝手は、必要最低限に達したという感じでこれからどんどん便利になっていくと思います。一緒に育っていく様子を見ていけるので個人的には楽しみにしています。
標準ブラウザはIEからEdgeになりました。Windows 10ではIEとEdgeという感じですが、Windows 10 MobileではEdge一本になりました。使い勝手は普通にブラウザなので割愛します。本当に普通です。
とりとめもなく、気づいたことを書いていきましたが、いよいよWindows 10 Mobileが日本に来ました。Windows Phone 8時代の暗い暗い時代から一転して、これからどんどんWindows 10 Mobileのデバイスが出てきて、どれを選ぶか悩んでしまうような状態になってしまっています。これはとてもうれしい悲鳴です。
Windows Phone界隈では有名なおでさんが、Windows 10 Mobileの端末を表にまとめてくれているので、どれを買うか悩んでる人は参考にするといいと思います。
ガチでした。YAMADA電機から「EveryPhone」が発売されます!od10z.wordpress.com
たぶん現状で最新の表があるのは、上の記事のはず…。
1つ罠として、LINEアプリが現状ではWindows 10 Mobileに対応していないためその点だけは気を付けてください。これは時間が解決してくれると思いますが現状LINEが必須な人は要注意です。
ReactivePropertyのv2.3をリリースしました。 今回の目玉は、パフォーマンスの改善です。
今までコンストラクタで色々やっていた関係で初期化が重いと一部で有名?でしたが、今回はコンストラクタを単純化したため軽くなってるはずです。100000回の初期化で4秒程度かかってたものが600msくらいにまで改善されました。
インストールは以下のNuGetから!
英語環境以外で、PCLで英語しかリソースの用意されていないライブラリを参照して使うとリソースを使ってるところで例外が出てアプリが落ちてしまう問題があります。まぁ修正はしようとしてるんでしょうけど、
因みにビルド時にこんな警告がいっぱい出ると、例外で落ちる可能性があるということになります。
1>MakePRI : warning 0xdef00522: Resources found for language(s) 'en,en-us' but no resources found for default language(s): 'ja'. Change the default language or qualify resources with the default language. http://go.microsoft.com/fwlink/?LinkId=231899 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/DelegateCommandDelegatesCannotBeNull'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/CannotRegisterCompositeCommandInItself'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/CannotRegisterSameCommandTwice'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/InvalidPropertyNameException'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/PropertySupport_ExpressionNotProperty_Exception'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/PropertySupport_StaticExpression_Exception'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/InvalidDelegateRerefenceTypeException'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/PropertySupport_NotMemberAccessExpression_Exception'. The application may throw an exception for certain user configurations when retrieving the resources. 1>MakePRI : warning 0xdef01051: No default or neutral resource given for 'Prism.Properties.Resources/DelegateCommandInvalidGenericPayloadType'. The application may throw an exception for certain user configurations when retrieving the resources.
この問題の回避方法が書かれたBlogを見つけました。
このBlogによると
<SkipIntermediatePriGenerationForResourceFiles>false</SkipIntermediatePriGenerationForResourceFiles>
でリリースビルドしても大丈夫になるみたいです。早くこのバグなおらないかなぁ。
MVVM + リアクティブプログラミングの組み合わせを快適にするためのライブラリのReactivePropertyのバージョン2.3を先日リリースしました。今回の目玉は、ReactivePropertyの初期化時のパフォーマンスの改善です。100000回初期化すると4秒近くかかっていたものを600ms程度に短縮しました。
以下のNuGetから入手できます。
ここらへんで、現バージョンのオーバービューを書いてみたいと思います。
ReactivePropertyは、MVVM + Rxを支援するための機能を有したライブラリです。以下のような機能を持っています。
このライブラリのコア機能です。変更通知プロパティを簡単に定義することが出来ます。その代りBinding時のPathが長くなるというデメリットもあります。例えばViewModelなどにNameという変更通知機能を持ったプロパティを定義するには以下のようにします。
publicclass PersonViewModel { public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>(); }
ReactivePropertyクラスは、Valueプロパティで値の設定と取得ができます。またINotifyPropertyChangedを実装しています。以下のように使うことが出来ます。
// Console Applicationでは自分でSynchronizationContextをセットしないといけない SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); var vm = new PersonViewModel(); vm.Name.PropertyChanged += (_, __) => Console.WriteLine($"Name changed {vm.Name.Value}"); vm.Name.Value = "tanaka"; Console.ReadKey(); vm.Name.Value = "kimura"; Console.ReadKey();
一行すると以下のようになります。
Name changed tanaka Name changed kimura
もともとMVVMパターンを補助するライブラリですので、XAML系プラットフォームで使います。Universal Windows PlatformやWPFやXamarinなどで使うことが出来ます。ここでは、UWPでの利用を説明したいと思います。
先ほどのPersonViewModelをバインドするために、コードビハインドにプロパティとして設定します。
publicsealedpartialclass MainPage : Page { public PersonViewModel ViewModel { get; } = new PersonViewModel(); public MainPage() { this.InitializeComponent(); } }
XAMLでは、以下のようにプロパティ名.Value(Valueをつけないといけない)でパスを指定します。
<Page x:Class="App1.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App1"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><TextBox Text="{x:Bind ViewModel.Name.Value, Mode=TwoWay}" /><TextBlock Text="{x:Bind ViewModel.Name.Value, Mode=OneWay}" /></StackPanel></Page>
実行すると、以下のようにTextBoxとTextBlockで値の同期がとれます。
このようにReactivePropertyを使うと、ViewModelの基本クラスを指定することなく簡単に変更通知機能を持ったプロパティを定義できます。この他にも、ReactivePropertyの名前の通りReactive Extensionsを使ってReactivePropertyを作成することが出来ます。
具体的にはIObservableに対してToReactivePropertyを呼ぶことでIObservableから値が発行されたら、それをValueに保持するというReactivePropertyが作成できます。
例えば以下のような感じになります。
publicclass PersonViewModel { public ReactiveProperty<string> Name { get; } = Observable.Interval(TimeSpan.FromSeconds(1)) .Select(x => $"tanaka {x}") .ToReactiveProperty(); }
1秒間隔で値を更新するReactivePropertyになります。実行すると1秒間隔で表示が更新されます。
ReactivePropertyがIObservableから生成可能なことはわかりました。さらに言うと、ReactivePropertyもIObservableとしての特性を持っているので、ReactivePropertyを加工してさらに別のReactivePropertyを作るといったことも可能です。
例えば以下のようになります。
publicclass PersonViewModel { public ReactiveProperty<string> Input { get; } = new ReactiveProperty<string>(""); public ReactiveProperty<string> Output { get; } public PersonViewModel() { this.Output = this.Input .Delay(TimeSpan.FromSeconds(1)) .Select(x => x.ToUpper()) .ToReactiveProperty(); } }
このようにしてXAMLからBindingします。x:BindではTextBoxのプロパティが変わったタイミングで値を反映することが出来ないのでここだけ、通常のBinidngをつかっています。
<Page x:Class="App1.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App1"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"x:Name="Page"mc:Ignorable="d"><StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><TextBox Text="{Binding ViewModel.Input.Value, ElementName=Page, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /><TextBlock Text="{x:Bind ViewModel.Output.Value, Mode=OneWay}" /></StackPanel></Page>
実行すると、1秒遅れで入力した文字が大文字になって出力されます。
先ほどの例のOutputプロパティのようにReactivePropertyのValueに値をセットされないほうが望ましいケースもあります。そういうときは、IObservableからToReadOnlyReactiveProperty()とすることで値をセットできないReadOnlyReactivePropertyが作成できます。
publicclass PersonViewModel { public ReactiveProperty<string> Input { get; } = new ReactiveProperty<string>(""); public ReadOnlyReactiveProperty<string> Output { get; } public PersonViewModel() { this.Output = this.Input .Delay(TimeSpan.FromSeconds(1)) .Select(x => x.ToUpper()) .ToReadOnlyReactiveProperty(); } }
値がセットできない以外は普通のReactivePropertyと、ほぼ同じように使うことができます。
ReactivePropertyは、コンストラクタやToReactivePropertyメソッドなどのファクトリメソッドでmodeを指定することが出来ます。modeにはReactivePropertyModeのEnum型が指定可能です。 以下のように定義されています。
[Flags] publicenum ReactivePropertyMode { None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2 }
DistinctUntilChangedを指定すると、同じ値が設定されてもOnNextやOnPropertyChangedを発行しなくなります。RaiseLatestValueOnSubscribeを設定すると、ReactivePropertyをSubscribeしたタイミングで現在の値をOnNextに発行するかどうかを制御できます。デフォルトでは、両方のmodeが設定されています。
mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueSubscribe;
このmodeを設定することで、ReactiveProeprtyのIObservableとしての特性を多少カスタマイズできます。
ReactivePropertyは、MVVMパターンのVMで使うことを主目的として作られています。(別にModelで使っても構いません)そのため、既存のPlaneなC#のModelのプロパティと同期をとるための便利な方法が用意されています。
例として、以下のようなINotifyPropertyChangedを実装したクラスがあるとします。
publicclass Person : INotifyPropertyChanged { publicevent PropertyChangedEventHandler PropertyChanged; privatestaticreadonly PropertyChangedEventArgs NamePropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(Name)); privatestring name; publicstring Name { get { returnthis.name; } set { if (this.name == value) { return; } this.name = value; this.PropertyChanged?.Invoke(this, NamePropertyChangedEventArgs); } } }
このNameプロパティの変更を監視して、値を更新するViewModelはReactivePropertyの機能を使わないで書くと通常以下のような感じに書きます。
publicclass PersonViewModel : IDisposable { private Person Model { get; set; } public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>(); public PersonViewModel(Person model) { this.Model = model; model.PropertyChanged += this.Model_PropertyChanged; } privatevoid Model_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(Person.Name): this.Name.Value = ((Person)sender).Name; break; } } publicvoid Dispose() { this.Model.PropertyChanged -= this.Model_PropertyChanged; this.Model = null; } }
非常にめんどくさいです。プロパティが増えたら大変なことになりそうです。ということで、ReactivePropertyでは、INotifyPropertyChangedを実装したクラスに対してObservePropertyというプロパティの変更をIObservableに変換する機能を提供しています。IObservableにしてしまえばToReactivePropertyメソッドを使ってReactivePropertyに変換できます。したがって上記のコードは以下のように書けます。
publicclass PersonViewModel : IDisposable { private CompositeDisposable Disposable { get; } = new CompositeDisposable(); public ReactiveProperty<string> Name { get; } public PersonViewModel(Person model) { this.Name = model.ObserveProperty(x => x.Name) .ToReactiveProperty() .AddTo(this.Disposable); } publicvoid Dispose() { this.Disposable.Dispose(); } }
ReactivePropertyのDisposeメソッドを呼び出すとModelのプロパティの変更監視を辞めるので、CompositeDisposableにReactivePropertyを集めておくことで、プロパティが増えてもあとで一括で購読を解除することが出来て便利です。
間にSelectメソッドなどを挟むことで、Modelの値を加工してViewModelに反映するということも簡単に書けるようになります。
public PersonViewModel(Person model) { this.Name = model.ObserveProperty(x => x.Name) .Select(x => $"{x}さん") .ToReactiveProperty() .AddTo(this.Disposable); }
上記の例はModelからViewModelへの単一方向の値の同期の仕方になります。この方法に加えて何か任意のタイミングで値をModelに書き戻すことでViewModelからModelへの値の更新も可能です。ただ、単純なViewModelのプロパティとModelのプロパティの双方向同期の方法も提供しています。INotifyPropertyChangedを実装したクラスに対してToReactivePropertyAsSynchronizedを呼び出すことで、双方向同期のReactivePropertyが生成されます。
public PersonViewModel(Person model) { this.Name = model.ToReactivePropertyAsSynchronized(x => x.Name) .AddTo(this.Disposable); }
convert引数とconvertBack引数にラムダ式を指定することで単純な値の変換処理を間に挟み込むことが出来ます。
public PersonViewModel(Person model) { // 名前にさんが入ってる人がいると破たんするロジックですが雰囲気をつかんでもらえれば…this.Name = model.ToReactivePropertyAsSynchronized(x => x.Name, convert: x => $"{x}さん", convertBack: x => x.Replace("さん", "")) .AddTo(this.Disposable); }
初期値としてModelの値をReactivePropertyに取り込み、ReactivePropertyの値に変更があったらModelに書き戻すだけの機能もあります。ModelがINotifyPropertyChangedを実装していないので、VMからMへの一方通行の値の同期です。
ReactiveProperty.FromObjectがその機能を提供します。以下のように使います。
public PersonViewModel(Person model) { this.Name = ReactiveProperty.FromObject(model, x => x.Name); }
ToReactivePropertyAsSynchronizedと同様にconvertとconvertBack引数を渡すことで間に値の変換をかませることが出来ます。
ReactivePropertyには、入力値の検証機能もあります。一番単純な方法はReactivePropertyの生成後にSetValidateNotifyErrorで検証ロジックを渡すことです。以下のようなコードで必須入力チェック機能付きのReactiveProeprtyを生成できます。
publicclass PersonViewModel { public ReactiveProperty<string> Name { get; } public PersonViewModel() { this.Name = new ReactiveProperty<string>() .SetValidateNotifyError(x => string.IsNullOrWhiteSpace(x) ? "Error!!" : null); } }
ReactivePropertyは、INotifyDataErrorInfoを実装しているためWPFではWPFの組み込みのバリデーションエラーメッセージの表示機能が使えます。UWPなどのINotifyDataErrorInfoによる検証エラーメッセージの無い環境では、ReactivePropertyのObserveErrorChangedをエラー情報にしてReadOnlyReactivePropertyなどに変換することでエラーメッセージを取得できます。
以下のような感じになります。
publicclass PersonViewModel { public ReactiveProperty<string> Name { get; } public ReadOnlyReactiveProperty<string> NameErrorMessage { get; } public PersonViewModel() { this.Name = new ReactiveProperty<string>() .SetValidateNotifyError(x => string.IsNullOrWhiteSpace(x) ? "Error!!" : null); this.NameErrorMessage = this.Name .ObserveErrorChanged .Select(x => x?.Cast<string>()?.FirstOrDefault()) .ToReadOnlyReactiveProperty(); } }
このViewModelを以下のように画面でバインドすることでエラーメッセージが表示されるようになります。
<Page x:Class="App1.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App1"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><TextBox Text="{x:Bind ViewModel.Name.Value, Mode=TwoWay}" /><TextBlock Text="{x:Bind ViewModel.NameErrorMessage.Value, Mode=OneWay}" /></StackPanel></Page>
実行すると以下のようになります。
DataAnnotationsをサポートしているプラットフォーム(Windows PhoneのSilverlight以外)では、DataAnnotationsを使った検証機能もサポートしています。SetValidateAttirbuteメソッドに、属性をつけたReactivePropertyを渡すことで設定可能です。必須入力の場合の例を以下に示します。
publicclass PersonViewModel { [Required(ErrorMessage = "Error!!")] public ReactiveProperty<string> Name { get; } public ReadOnlyReactiveProperty<string> NameErrorMessage { get; } public PersonViewModel() { this.Name = new ReactiveProperty<string>() .SetValidateAttribute(() => this.Name); this.NameErrorMessage = this.Name .ObserveErrorChanged .Select(x => x?.Cast<string>()?.FirstOrDefault()) .ToReadOnlyReactiveProperty(); } }
これで、先ほどと同じ結果になります。
Modelと同期するためのToReactivePropertyAsSynchronizedとFromObjectメソッドには、ignoreValidationErrorValueという引数があり、この引数にtrueを設定することで検証エラーがあるときには、Modelに値を書き込まないようにするオプションがあります。必須入力チェックを通過したときのみModelに値を書き戻す場合のコード例をいかに示します。
publicclass PersonViewModel { [Required(ErrorMessage = "Error!!")] public ReactiveProperty<string> Name { get; } public ReadOnlyReactiveProperty<string> NameErrorMessage { get; } public PersonViewModel(Person model) { this.Name = model.ToReactivePropertyAsSynchronized(x => x.Name, ignoreValidationErrorValue:true) .SetValidateAttribute(() => this.Name); this.NameErrorMessage = this.Name .ObserveErrorChanged .Select(x => x?.Cast<string>()?.FirstOrDefault()) .ToReadOnlyReactiveProperty(); } }
ReactivePropertyにはObserveHasErrorsプロパティがあり、このプロパティを通してReactivePropertyのエラーの有無を監視することが出来ます。ObserveHasErrorsプロパティはIObservable<bool>型なので、複数のReactivePropertyとCombineLatestで束ねることで、全てのReactivePropertyにエラーがないときなどの処理を簡単に記述できるようになっています。
publicclass PersonViewModel { [Required(ErrorMessage = "Error!!")] public ReactiveProperty<string> Name { get; } [Required(ErrorMessage = "Error!!")] [RegularExpression("[0-9]+", ErrorMessage = "Error!!")] public ReactiveProperty<string> Age { get; } public PersonViewModel(Person model) { this.Name = new ReactiveProperty<string>() .SetValidateAttribute(() => this.Name); this.Age = new ReactiveProperty<string>() .SetValidateAttribute(() => this.Age); new[] { this.Name.ObserveHasErrors, this.Age.ObserveHasErrors } .CombineLatest(x => x.All(y => !y)) .Where(x => x) .Subscribe(_ => { Debug.WriteLine("エラーの無い時の処理"); }); } }
ReactivePropertyには、MVVMパターンのICommandの実装としてReactiveCommandクラスを提供しています。ReactiveCommandの特徴は、IObservable<bool>から生成可能という点です。IObservable<bool&lgt;に対して、ToReactiveCommand拡張メソッドを呼び出すことでTrueが発行されたときだけ実行可能なコマンドが作成されます。自前でプロパティを監視して、コマンドの実行可否を判定する必要がなくなる点が特徴です。
コマンドが実行されたときの処理はSubscribeメソッドで指定します。
例えばすべてのプロパティの入力にエラーがなくなったら処理をしたいというコマンドは以下のように定義できます。
publicclass PersonViewModel { [Required(ErrorMessage = "Error!!")] public ReactiveProperty<string> Name { get; } [Required(ErrorMessage = "Error!!")] [RegularExpression("[0-9]+", ErrorMessage = "Error!!")] public ReactiveProperty<string> Age { get; } public ReactiveCommand CommitCommand { get; } public PersonViewModel() { this.Name = new ReactiveProperty<string>() .SetValidateAttribute(() => this.Name); this.Age = new ReactiveProperty<string>() .SetValidateAttribute(() => this.Age); this.CommitCommand = new[] { this.Name.ObserveHasErrors, this.Age.ObserveHasErrors } .CombineLatest(x => x.All(y => !y)) .ToReactiveCommand(); this.CommitCommand.Subscribe(async _ => await new MessageDialog("OK").ShowAsync()); } }
以下のようにXAMLにバインドできます。
<Page x:Class="App1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox Text="{x:Bind ViewModel.Name.Value, Mode=TwoWay}" /> <TextBox Text="{x:Bind ViewModel.Age.Value, Mode=TwoWay}" /> <Button Content="Commit" Command="{x:Bind ViewModel.CommitCommand}" /> </StackPanel> </Page>
入力エラー(Ageとバインドしてるところに数字じゃない文字列が入っている)があるとボタンが押せなくて
入力エラーがなくなるとボタンが押せるようになります。
ReactiveCommandには、CommandParameterを受け取るReactiveCommand<T>という型引数を受け取るバージョンのものも用意しています。
ReactiveCollectionクラスは、指定したスケジューラ上(何も指定しない場合はUIスレッド上)でコレクションの操作とイベントの発行を行う機能を追加したObservableCollectionクラスになります。また、IObservableからToReactiveCollectionを呼び出すことで、IObservableから値が発行されるたびにコレクションに要素を追加するという機能を持ったコレクションとして生成することも可能です。
例えば以下のようにすることで、1秒ごとに値を追加するコレクションが作成できます。
publicclass PersonViewModel { public ReactiveCollection<long> TimerCollection { get; } = Observable .Interval(TimeSpan.FromSeconds(1)) .ToReactiveCollection(); }
また、AddOnScheduler、RemoveOnSchedulerなどの追加削除などのメソッド名にOnSchedulerがついたメソッドを呼び出すことでReactiveCollectionが紐づいているSchedulerを使ってコレクション操作とイベントの発行を行うように指示することが出来ます。
publicclass PersonViewModel { public ReactiveCollection<long> SampleCollection { get; } = new ReactiveCollection<long>(); private Random Random { get; } = new Random(); public ReactiveCommand AddCommand { get; } = new ReactiveCommand(); public PersonViewModel() { this.AddCommand.Subscribe(async _ => await Task.Run(() => { // バックグランドのスレッドからUIスレッド上で要素を追加することが可能this.SampleCollection.AddOnScheduler(this.Random.Next()); })); } }
重たいバックグラウンド処理の結果をUIスレッド上で要素を追加したいときなどに便利です。コンストラクタでSchedulerを渡すことで、デフォルトのUIスレッド上以外でのコレクション操作に対応させることも可能です。
このクラスは、ObservableCollectionやReactiveCollectionと同期した読み取り専用のコレクションをとして機能します。ObservableCollectionやReactiveCollectionでToReadOnlyObservableCollectionメソッドを呼び出すことで生成できます。
その時に、Func<T, U>型の変換ロジックを渡すことで、ObservableCollectionの型を変換した結果のコレクションとして機能させることが出来ます。ちょうど、ModelのコレクションをViewModelのコレクションとして提供するといったケースで便利に使用できます。また、コレクションから値が削除されるタイミングで、IDisposable型の場合Disposeを自動で呼び出す機能もあります。
基本的な使い方を以下に示します。
publicclass MainPageViewModel { private PeopleManager Model { get; } = new PeopleManager(); public ReadOnlyReactiveCollection<PersonViewModel> People { get; } public MainPageViewModel() { // ModelのPersonのコレクションをPersonViewModelのコレクションにするthis.People = this.Model.People.ToReadOnlyReactiveCollection(x => new PersonViewModel(x)); } } publicclass PersonViewModel { //各種プロパティpublic PersonViewModel(Person model) { // modelとvmの接続処理 } } publicclass PeopleManager { public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>(); // Peopleを処理するコードなど } publicclass Person : INotifyPropertyChanged { publicevent PropertyChangedEventHandler PropertyChanged; // 各種プロパティ }
ReactivePropertyは、ReactivePropertyクラスやReactiveCommandクラスなどをIObservableから生成する関係上、色々なものをIObservableに変換するメソッドを提供しています。
PropertyChangedAsObservable拡張メソッドはINotifyPropertyChangedのイベントの発生をIObservable>NotifyPropertyChangedEventArgs<に変換します。
var p = new Person(); p.PropertyChangedAsObservable() .Subscribe(x => Debug.WriteLine($"{x.PropertyName}が変更されました"));
さらに、特定のプロパティの変更に特化したObservePropertyメソッドがあります。これは、変更後のプロパティの値が流れてきます。
var p = new Person(); p.ObserveProperty(x => x.Name) .Subscribe(x => Debug.WriteLine($"変更後のプロパティの値 {x}"));
CollectionChangedもPropertyChangedと同じようにIObservableに変換できます。
var col = new ObservableCollection<Person>(); col.CollectionChangedAsObservable() .Subscribe(x => Debug.WriteLine($"{x.Action}が実行されました"));
この他に、AddやRemoveなどに特化したObserveXXXChangedメソッドがあります。
var col = new ObservableCollection<Person>(); col.ObserveAddChanged() .Subscribe(x => Debug.WriteLine($"{x.Name}が追加されました"));
あるようでなかなか無い処理として、コレクションの中の要素のプロパティが変更されたかどうかを監視する拡張メソッドがあります。ObserveElementPropertyChangedでPropertyChangedの監視を行い、ObserveElementPropertyで指定したプロパティの変更を監視します。
var col = new ObservableCollection<Person>(); // プロパティの変更を監視する col.ObserveElementPropertyChanged() .Subscribe(x => Debug.WriteLine($"{x.EventArgs} {x.Sender}")); // 特定のプロパティの変更を監視する col.ObserveElementProperty(x => x.Name) .Subscribe(x => Debug.WriteLine($"{x.Instance} {x.Property} {x.Value}"));
ObserveElementPropertyのプロパティがReactivePropertyの場合のObserveElementObservablePropertyという拡張メソッドも提供しています。
この他にも各種拡張メソッドを提供しています。リトライやエラーを無視するものなど小物もあります。Extensions名前空間にこれらのメソッドが定義してあるので覗いてみてください。
ReactivePropertyには、IObservable>bool<, IObservable>CountChangedStatus<を便利に扱うためのBooleanNotifyerクラスとCountNotifierクラスがあります。さらに、指定した時間の後に、値を発行するScheduledNotifierというクラスを提供しています。
TrueとFalseを切り替えるだけのシンプルなクラスです。TurnOnでTrueにしてTurnOffでFalseにしてValueプロパティに直接値を設定することでTrue,Falseを設定できます。
var n = new BooleanNotifier(); n.Subscribe(x => Debug.WriteLine(x)); n.TurnOn(); // true n.TurnOff(); // false n.Value = true; // true n.Value = false; // false
ReactiveCommandのソースとして、単純にIObservable<bool>のソースとして使うことが出来ます。Subject<bool>との違いは、Valueで現在の状態をとれるという点です。
CountNotifierは、名前の通りカウンターです。数値のカウントが出来ます。Increment, Decrementメソッドで指定した数だけ数値を加算したり、減算したりできます。特徴的なのは、IncrementメソッドとDecrementメソッドが返すIDisposableをDisposeすることで、インクリメントやデクリメントの逆が実行されて値がもとに戻ります。CountNotifierは、CountChangedStatusのIObservableになります。CountChangedStatusは以下のように定義されているenumになります。
/// <summary>Event kind of CountNotifier.</summary>publicenum CountChangedStatus { /// <summary>Count incremented.</summary> Increment, /// <summary>Count decremented.</summary> Decrement, /// <summary>Count is zero.</summary> Empty, /// <summary>Count arrived max.</summary> Max }
こんな感じで使えます。
var c = new CountNotifier(); // 現在のカウンタの状態を出力 c.Subscribe(x => Debug.WriteLine(x)); // 現在のカウンタ値を出力 c.Select(_ => c.Count).Subscribe(x => Debug.WriteLine(x)); // インクリメントして var d = c.Increment(10); // インクリメントを取り消し d.Dispose(); // インクリメントしてデクリメント c.Increment(10); c.Decrement(5); // 現在の値を出力 Debug.WriteLine(c.Count);
出力結果は以下のようになります。
Increment 10 Decrement 0 Empty 0 Increment 10 Decrement 55
コンストラクタで、カウンタのMax値を指定するとその値以上にならないカウンタとかも作ることができます。
ScueduledNotifierクラスは、指定したScheduler上で指定した時間に値を発行するだけのシンプルなクラスです。デフォルトだと現在のスレッド上になります。スケジューラを差し替える場合はコンストラクタにスケジューラを指定します。
使い方は以下のような感じになります。
var n = new ScheduledNotifier<string>(); n.Subscribe(x => Debug.WriteLine(x)); // 即時実行 n.Report("Hello world"); // 2病後実行 n.Report("After 2 second.", TimeSpan.FromSeconds(2));
ReactivePropertyやReactiveCommandにViewで発生した"イベント"を渡す方法を提供しています。 EventToReactivePropertyとEventToReactiveCommandクラスがその機能を提供しています。このクラスはBehaviorのActionとして実装されていて、EventTrigger(WinRT系の場合はEventTriggerBehavior)などのTriggerに設定して使用します。EventToReactivePropertyクラスとEventToReactiveCommandクラスにはReactiveConverter<T, U>クラスを挟むことで非同期な処理を間に挟むことが出来ます。例えば以下のようなコンバーターを作成することでファイルを選択して選択したパスを返すことが出来ます(コンバータという名前が適当なのかというと自信がなくなってきますが…)
using Reactive.Bindings.Interactivity; using System; using System.Linq; using System.Reactive.Linq; using Windows.Storage.Pickers; using Windows.UI.Xaml; namespace App1 { publicclass FileOpenReactiveConverter : ReactiveConverter<RoutedEventArgs, string> { protectedoverride IObservable<string> OnConvert(IObservable<RoutedEventArgs> source) { return source.SelectMany(async _ => { var picker = new FileOpenPicker(); picker.FileTypeFilter.Add(".snippet"); var f = await picker.PickSingleFileAsync(); return f?.Path; }) .Where(x => x != null); } } }
このコンバータを使って以下のようにイベントをコマンドにバインドできます。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App1"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"xmlns:Core="using:Microsoft.Xaml.Interactions.Core"xmlns:Interactivity1="using:Reactive.Bindings.Interactivity"x:Class="App1.MainPage"mc:Ignorable="d"><StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Button Content="OpenFile..."><Interactivity:Interaction.Behaviors><Core:EventTriggerBehavior EventName="Click"><Interactivity1:EventToReactiveCommand Command="{x:Bind ViewModel.SelectFileCommand}"><local:FileOpenReactiveConverter /></Interactivity1:EventToReactiveCommand></Core:EventTriggerBehavior></Interactivity:Interaction.Behaviors></Button><TextBlock Text="{x:Bind ViewModel.FileName.Value, Mode=OneWay}" /></StackPanel></Page>
コードビハインドとViewModelは以下のようになっています。
using Reactive.Bindings; using Windows.UI.Xaml.Controls; namespace App1 { publicsealedpartialclass MainPage : Page { public MainPageViewModel ViewModel { get; } = new MainPageViewModel(); public MainPage() { this.InitializeComponent(); } } publicclass MainPageViewModel { public ReactiveCommand<string> SelectFileCommand { get; } public ReadOnlyReactiveProperty<string> FileName { get; } public MainPageViewModel() { this.SelectFileCommand = new ReactiveCommand<string>(); this.FileName = this.SelectFileCommand.ToReadOnlyReactiveProperty(); } } }
これでファイルを選択するとファイル名が表示されるようになります。EventToReactiveCommandとEventToReactivePropertyは、指定できるのがReactiveCommandかReactivePropertyかという違いだけなのでEventToReactivePropertyの使い方は割愛します。
Reactiveっぽくない機能ですが、プロパティの値をリアルタイムに監視してフィルタリングする機能を持ったコレクションです。コレクションに対して、ToFilteredReadOnlyObservableCollectionメソッドを呼び出すことで生成できます。 引数で、コレクションの対象にしたい条件を指定します。例えば以下のように使います。
var collection = new ObservableCollection<Person>(); // tanakaさん以外を含んだコレクション var filtered = collection.ToFilteredReadOnlyObservableCollection(x => x.Name.IndexOf("tanaka") == -1); collection.Add(new Person { Name = "okazuki1" }); collection.Add(new Person { Name = "okazuki2" }); collection.Add(new Person { Name = "okazuki3" }); collection.Add(new Person { Name = "tanaka1" }); // okazuki1, okazuki2, okazuki3 Console.WriteLine("---"); foreach (var p in filtered) { Console.WriteLine(p.Name); } collection[1].Name = "tanaka2"; // okazuki1, okazuki3 Console.WriteLine("---"); foreach (var p in filtered) { Console.WriteLine(p.Name); }
長々と書きましたが、ReactivePropertyの機能をなんとなく書き出してみました。 これでしばらくは、こういう記事はいいかな?
APIはとても簡単ですJumpListクラスとJumpListItemクラスを使うだけです。説明よりもコードが明瞭なくらい。以下のような感じになります。
var jumpList = await JumpList.LoadCurrentAsync(); jumpList.Items.Clear(); jumpList.Items.Add(JumpListItem.CreateWithArguments("arg1", "Item1")); jumpList.Items.Add(JumpListItem.CreateSeparator()); jumpList.Items.Add(JumpListItem.CreateWithArguments("arg2", "Item2")); await jumpList.SaveAsync();
起動時に以下の処理を追加しておくだけで、以下のような雰囲気のジャンプリストが出来上がります。
ジャンプリストからの起動時には、CreateWithArgumentsの第一引数に渡した値がOnLaunchedの引数のLaunchActivatedEventArgsにあるArgumentsに入ってくるので、それを見て処理をわけるといい感じになります。
これはお手軽でいい感じ。
merge関数を使う
> x <- data.frame(id = c(1,2,3), name = c("a", "b", "c")) > y <- data.frame(id = c(1,2,3,4), age = c(12, 10, 11, 9)) > merge(x, y, "id") id name age 1 1 a 12 2 2 b 10 3 3 c 11 > merge(x, y, "id", all = T) id name age 1 1 a 12 2 2 b 10 3 3 c 11 4 4 <NA> 9 >
参考
ということをしたかった。 例えば以下のような
という感じのファイルがあるとして、これを1つのdata.frameに突っ込みたい。
R初心者的にがんばったらこうなった。もっと簡単にRらしくかけるのだろうか
x <- lapply(list.files(getwd(), "namelist[0-9].csv"), read.csv) accum <- x[[1]] for (i in 2:length(x)) { accum <- rbind(accum, x[[i]]) }
先日書いた記事にReduce使うとRらしいよ!っていうコメントいただいたので試してみました。
x <- Reduce(rbind, lapply(list.files(pattern = "namelist[0-9].csv"), read.csv))
これで、ファイル名のリストをとってきて、data.frameに変換して結合してくれるとか素敵。
そういえばやってなかった。 print関数で出力できるっぽいです。
print("Hello world")
で以下のように出力されます。
[1] "Hello world"
Hello worldを出力するgreet関数は以下のような感じで定義できます。
greet <- function() { print("Hello world") } greet()
出力結果は当然同じです。関数の定義は関数を変数に代入(<-)するという形で定義するみたいですね。
名前指定してHello ○○!ってしてみたいですよね。ということでやってみました。
greet <- function(name) { print(sprintf("Hello %s!", name)) } greet("tanaka")
引数はこんな感じで定義できます。sprintfで書式付き文字列で文字列を組み立ててprintで表示してます。
実行結果は以下のような感じ
[1] "Hello tanaka!"
Rで関数を呼び出すときは、引数を順番通りに指定することができます。 そのほかに名前で指定することもできる。例えば以下のように。
greet <- function(name) { print(sprintf("Hello %s!", name)) } greet(name = "tanaka")
ちなみに、関数の引数はデフォルト値を持たせることもできる。
greet <- function(name = "default") { print(sprintf("Hello %s!", name)) } greet()
ふむ。便利。
R言語で関数に戻り値つけたかったらreturn()をすればいいみたい。
calc.add <- function(x, y) { return(x + y) } print(calc.add(10, 2))
これで、12と表示される。
if文とかもちゃんとあるんですね。
judge <- function(x) { if (x == 10) { print("ten!!!") } else if(x == 8.1) { print("update ten!!") } else { print("...") } } judge(10) judge(8.1) judge(7)
if (条件) { } else { }でさくっと書けます。実行結果は以下のような感じ。
[1] "ten!!!" [1] "update ten!!" [1] "..."
ExcelのIF文みたいに使えます。
ifelse(1==1, print("true case"), print("false case")) ifelse(1!=1, print("true case"), print("false case"))
実行すると以下のような感じ。
[1] "true case" [1] "false case"
ベクトルに対しても使える点が便利っぽいです。
val <- c(1,2,3,1,2,3,1,2,3) x <- ifelse(val==1, "true case", "false case") print(x)
実行すると以下のような結果になります。
[1] "true case" "false case" "false case" "true case" "false case" "false case" "true case" "false case" "false case"
同じ型をまとめて管理するものでc()関数で作るらしい。
x <- c(1,2,3,4,5) print(x)
出力結果
[1] 1 2 3 4 5
1~5みたいに規則性のあるものは、開始:終了といった感じでかけるみたい。
x <- 1:5 print(x)
出力結果は同じ。
Rはベクトルに対する演算が便利になってる。具体的にいうと単純に掛け算をするだけで、全要素に掛け算をしてくれる。ループいらない!
x <- 1:5 x <- x * 10 print(x)
出力結果
[1] 10 20 30 40 50
ベクトル同士の掛け算もOK。同じ長さなら単純に掛け合わせて、違う長さなら短い方を再利用しながら掛け算してくれる。
x <- c(1,2,3) y <- c(10,20,30) z <- x * y print(z)
出力結果
[1] 10 40 90
長さの違うもの同士の掛け算のケース
x <- c(1,2,3) y <- c(10,20,30, 100, 200, 300) z <- x * y print(z)
出力結果
[1] 10 40 90 100 400 900
要素へアクセスするには[]演算子を使います。1オリジンなのでC系言語の人は要注意。ベクトルを渡すことで、複数の値をベクトル形式で抜いてくることも可能です。
x <- c(1,2,3) print(x[2]) print(x[1:2]) print(x[c(1,3)])
実行結果
[1] 2 [1] 1 2 [1] 1 3
stringrパッケージにある、str_splitでできます。第一引数に分割したい文字列、patternに分割する区切りを表す正規表現を指定できます。カンマか-で分割したい場合は以下のような感じになります。
require(stringr) x <- "a,b,c-d-e-f" y <- str_split(x, pattern = ",|-") print(y)
実行結果
[1] "a" "b" "c" "d" "e" "f"
はぁ…数学苦手…。
R言語って統計解析がお得意なんですって。
すべての数字を合計して数で割った平均と、ソートして真ん中の値を取り出す中央値くらいは知ってるけどそれ以上はちょっと…という感じですね。
平均と中央値を出す関数はmeanとmedianという関数として用意されてます。
x <- 1:100 print(mean(x)) # 平均 print(median((x))) # 中央値
んで、次に出てくるのが分散ってやつらしいです。分散って名前からわかる通り、どれくらい値が散らばってるの?というものらしい。求め方は、各値から平均値を引いて2乗して、要素数で割る。R的に書くとこんな感じ。
x <- 1:100 v <- sum((x - mean(x))^2) / length(x) print(v)
実行するとこんな感じ。
[1] 833.25
個人的にはこれを見てもぱっとどれくらい分散してるのかわかんねーって思うのは修行が足りないからだろう。さて、この分散2乗したものを足し合わせてるんだから√とって2乗を打ち消してやろうという考えが出てくるみたいです。これを標準偏差というらしいです。
x <- 1:100 v <- sum((x - mean(x))^2) / length(x) print(v) s <- sqrt(v) print(s)
実行するとこんな感じ。
[1] 833.25 [1] 28.86607
標準偏差くらいになってくると、それっぽい雰囲気を感じますね。因みに平均+-標準偏差の範囲には大体68%のデータが入るみたいです。
因みに分散を求める関数はvarで標準偏差を求める関数はsdっていうみたいです。
x <- 1:100 v <- sum((x - mean(x))^2) / length(x) print(v) s <- sqrt(v) print(s) print(var(x)) print(sd(x))
実行するとこんな感じ。
[1] 833.25 [1] 28.86607 [1] 841.6667 [1] 29.01149
自分で計算したのと値が違うのは、自分で手計算したのが標本分散っていうのに対して、関数が計算してくれるのが不偏分散っていうものらしいです。不偏ってついてるのはlength(x)で割るんじゃなくて、その値-1で割ってるところが違う。扱ってる数字の集合がこれで全部なんだ!ってときは標本分散とかのほうを使うけど、集合の一部ってときには不偏分散とかを使うみたい。なんでだろうね?分散の値が異なるから、それを元に計算する標準偏差も値が違うってわけみたいです。ある集合のすべての数字を使うことって少ないので、不偏分散のほうが使うことが多いらしい。
さて、標準偏差っていうのが出てきたところで、数値を平均0、標準偏差1に慣らしてしまおうという発想が出てくるらしいですね。やり方は数値から平均を引いて、標準偏差で割る。
x <- 1:100 z <- (x - mean(x)) / sd(x) print(z) # z得点 # 平均と標準偏差を出してみる print(mean(z)) print(sd(z))
実行するとこんな感じ。
[1] -1.70622042 -1.67175132 -1.63728222 -1.60281312 -1.56834402 -1.53387492 -1.49940582 -1.46493672 -1.43046762 -1.39599852 -1.36152943 -1.32706033 -1.29259123 [14] -1.25812213 -1.22365303 -1.18918393 -1.15471483 -1.12024573 -1.08577663 -1.05130753 -1.01683843 -0.98236933 -0.94790023 -0.91343113 -0.87896203 -0.84449293 [27] -0.81002384 -0.77555474 -0.74108564 -0.70661654 -0.67214744 -0.63767834 -0.60320924 -0.56874014 -0.53427104 -0.49980194 -0.46533284 -0.43086374 -0.39639464 [40] -0.36192554 -0.32745644 -0.29298734 -0.25851825 -0.22404915 -0.18958005 -0.15511095 -0.12064185 -0.08617275 -0.05170365 -0.01723455 0.01723455 0.05170365 [53] 0.08617275 0.12064185 0.15511095 0.18958005 0.22404915 0.25851825 0.29298734 0.32745644 0.36192554 0.39639464 0.43086374 0.46533284 0.49980194 [66] 0.53427104 0.56874014 0.60320924 0.63767834 0.67214744 0.70661654 0.74108564 0.77555474 0.81002384 0.84449293 0.87896203 0.91343113 0.94790023 [79] 0.98236933 1.01683843 1.05130753 1.08577663 1.12024573 1.15471483 1.18918393 1.22365303 1.25812213 1.29259123 1.32706033 1.36152943 1.39599852 [92] 1.43046762 1.46493672 1.49940582 1.53387492 1.56834402 1.60281312 1.63728222 1.67175132 1.70622042 [1] 0 [1] 1
平均が0になって標準偏差が1になってるのがわかります。
偏差値って平均が50で標準偏差が10になるように慣らしたものらしいです。なのでz得点にちょちょいと細工すると偏差値になります。
x <- 1:100 z <- (x - mean(x)) / sd(x) s <- 50 + z * 10 print(s) print(mean(s)) print(sd(s))
実行するとこんな感じ。
[1] 32.93780 33.28249 33.62718 33.97187 34.31656 34.66125 35.00594 35.35063 35.69532 36.04001 36.38471 36.72940 37.07409 37.41878 37.76347 38.10816 38.45285 38.79754 [19] 39.14223 39.48692 39.83162 40.17631 40.52100 40.86569 41.21038 41.55507 41.89976 42.24445 42.58914 42.93383 43.27853 43.62322 43.96791 44.31260 44.65729 45.00198 [37] 45.34667 45.69136 46.03605 46.38074 46.72544 47.07013 47.41482 47.75951 48.10420 48.44889 48.79358 49.13827 49.48296 49.82765 50.17235 50.51704 50.86173 51.20642 [55] 51.55111 51.89580 52.24049 52.58518 52.92987 53.27456 53.61926 53.96395 54.30864 54.65333 54.99802 55.34271 55.68740 56.03209 56.37678 56.72147 57.06617 57.41086 [73] 57.75555 58.10024 58.44493 58.78962 59.13431 59.47900 59.82369 60.16838 60.51308 60.85777 61.20246 61.54715 61.89184 62.23653 62.58122 62.92591 63.27060 63.61529 [91] 63.95999 64.30468 64.64937 64.99406 65.33875 65.68344 66.02813 66.37282 66.71751 67.06220 [1] 50 [1] 10
ということでさっき平均+-標準偏差の間くらいには68%の人が入るって言ってたけど、偏差値40くらいでも68%の多数派なのでそんなに気にしなくてもいいのかしらとかというどうでもいいことが頭をよぎった。