TwitterクライアントをEchofonからMiniTwitterに切り替えようかと思ってMiniTwitterを複数アカウント対応にしてみていた。対応の仕方としてはEchofonのように複数のアカウント情報を保存&容易に切り替え可能という感じ、複数アカウントを同時に操作とかは要らない(それだったら複数のクライアントを使えばいい)。
きっかけは普段使いのWebブラウザをFirefoxからGoogle Chromeに変更しようかと考えたこと。面倒くさくなってきてもう止めるかもしれないから今のうちに書いておく。
ベースはChange Set 61673。これをやっている間にバージョン1.05系列がリリースされてた。
変更内容は次の通り。
メインウィンドウのupdateボタンの右に最後にログインしたユーザーを表示。
Echofonと同じようにこのログインユーザー表示をクリックすると切り替えるアカウントを選択するメニューがポップアップ。ここでアカウントを選択するとログインユーザーが切り替わる。
アカウント情報を登録したり編集したりするフォームは至ってノーマルのはず。
ログ保存機能は使う気がないから後回し。
もうちょっとやりたいことをやったら変更部分の扱いをどうするか考える。
MiniTwitter勝手に改造版のコードも見てみる。
この程度のことをしただけなのにXAMLとWPFについてけっこう調べて勉強になった。XAML/WPFおもしろそう。これからはWindowsフォームなんか使わないでWPFを使おう。
2009/11/22
VisualStudio 2008の単体テスト機能のカスタマイズ(VS2008 custom assertion example)
結構前にGallioのAssertEx.Thatの式ツリーの要素をキャプチャして出力できる点がすごいと思ってちょっといろいろいじっていた。Gallioは素晴らしいけどちょっと重い感じがするし、機能とか装備されているものが豊富なのはいいんだけど、もうちょっと簡単にいじれる大きすぎないフレームワークを使いたいというのが感想。
最近それをある程度の形にしようと思って、アレンジしてVisual Studio 2008の単体テスト機能で使えるようにしようとしてVisual Studio 2008の単体テスト機能のカスタマイズを調べてみた。
基本的には自前のassertionでテスト失敗の場合はAssertFailedExceptionをスローすればいいんだけど、そうするとスタックトレースに診断処理や例外をスローする処理のメソッドが含まれてしまう。
これはDebuggerHiddenAttributeやDebuggerNonUserCodeAttributeを使っても避けられない。デバッガとスタックトレースは別。
スタックトレースに含まれないように回避するための属性はないみたいなので、スタックトレースを操作する方向で行こうとすると例外のスタックトレース(Exception.StackTraceプロパティとか)はStackTrace型ではなくて文字列だったりする上にSetterがない。
どうすればいいの?例外のスタックトレースは変更できないの?と思ったけどよく見るとException.StackTraceプロパティはvirtual宣言されているから派生させてオーバーライドすれば、このプロパティで返される値は変更できる。
それでこんなコードでスタックトレースに含められないようにできそう。
Visual Studioの単体テスト機能でテストを失敗させた場合のスタックトレースにExpressionAssertクラスの処理が含まれない。
Visual Studioに統合された単体テストの機能以外で試してもいないし、例外クラスがAssertFailedExceptionではないという点が気になるけど、とりあえず手軽にするならこの方法でもいいかもしれない。
手間は増えるだろうけどInternalPreserveStackTraceを使ったやり方のほうがちゃんとしていそう。
最近それをある程度の形にしようと思って、アレンジしてVisual Studio 2008の単体テスト機能で使えるようにしようとしてVisual Studio 2008の単体テスト機能のカスタマイズを調べてみた。
基本的には自前のassertionでテスト失敗の場合はAssertFailedExceptionをスローすればいいんだけど、そうするとスタックトレースに診断処理や例外をスローする処理のメソッドが含まれてしまう。
これはDebuggerHiddenAttributeやDebuggerNonUserCodeAttributeを使っても避けられない。デバッガとスタックトレースは別。
スタックトレースに含まれないように回避するための属性はないみたいなので、スタックトレースを操作する方向で行こうとすると例外のスタックトレース(Exception.StackTraceプロパティとか)はStackTrace型ではなくて文字列だったりする上にSetterがない。
どうすればいいの?例外のスタックトレースは変更できないの?と思ったけどよく見るとException.StackTraceプロパティはvirtual宣言されているから派生させてオーバーライドすれば、このプロパティで返される値は変更できる。
それでこんなコードでスタックトレースに含められないようにできそう。
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace MSTestCustomAssertion { public class ExpressionAssert { public static void IsTrue(Expression<Func<bool>> expr) { var result = expr.Compile().Invoke(); if (!result) throw new FailedException("ExpressionAssert.IsTrue failed"); } public class FailedException : AssertFailedException { public FailedException() : base() { } public FailedException(string msg) : base(msg) { } public override string StackTrace { get { var lines = base.StackTrace.Split(new string[] { Environment.NewLine, }, StringSplitOptions.None); var index = Array.FindIndex(lines, x => !x.Contains("ExpressionAssert.")); var st = string.Join(Environment.NewLine, lines, index, lines.Length - index); return st; } } } } }ksksts / junk / source — bitbucket.org
Visual Studioの単体テスト機能でテストを失敗させた場合のスタックトレースにExpressionAssertクラスの処理が含まれない。
Visual Studioに統合された単体テストの機能以外で試してもいないし、例外クラスがAssertFailedExceptionではないという点が気になるけど、とりあえず手軽にするならこの方法でもいいかもしれない。
手間は増えるだろうけどInternalPreserveStackTraceを使ったやり方のほうがちゃんとしていそう。
2009/11/15
配列やList<T>をIList<T>型のインデクサでアクセスするとパフォーマンスが悪い(don't use IList<T>'s indexer)
C#でのソートアルゴリズムのまとめ(sort algorithms in C#)とかIList<T>の走査処理の比較(インデクサ/GetEnumerator/foreach)で「配列の添字アクセスが遅い」とか書いたけどこれは間違い。「配列をIList<T>型にキャストしてインデクサでアクセスすると遅い」というのが正しい。
実際には配列をそのまま添字でアクセスする処理はList<T>のインデクサアクセスよりも速い。
それにList<T>もIList<T>型にキャストしてインデクサでアクセスすのは(List<T>のインデクサよりも)遅い。
インターフェースってこんなに影響するものなのか。配列やList<T>の特殊な最適化処理があったりしてそれが適用されなくなったりした影響なのかな。
測定した結果は次の通り。
int[]に対する配列の添字アクセスが最も速くてIList<T>のインデクサを通すとなぜか10倍弱の時間がかかるようになる。
List<T>のインデクサアクセスは配列の添字アクセスより遅いらしい。これもIList<T>のインデクサを通すと配列ほどじゃないけど遅くなる。
IListArrayWrapperは値型の配列をラップしてポインタを使ったunsafeなインデクサを実装したクラス。
ジェネリックに実装しようとしたけどジェネリック引数の型のポインタを取得する方法が分からなかった。"where T : struct"の指定があればOKにしてもいいんじゃないかと思うけど、とりあえずできなかったからint型用のコードにした。
ダメだったコード:
まとめとしては、配列とIList<T>を実装したランダムアクセス可能なコレクションの両方を処理する&パフォーマンスを気にするコードを書く場合は分けたほうがいいみたい。
実際には配列をそのまま添字でアクセスする処理はList<T>のインデクサアクセスよりも速い。
それにList<T>もIList<T>型にキャストしてインデクサでアクセスすのは(List<T>のインデクサよりも)遅い。
インターフェースってこんなに影響するものなのか。配列やList<T>の特殊な最適化処理があったりしてそれが適用されなくなったりした影響なのかな。
測定した結果は次の通り。
int[]に対する配列の添字アクセスが最も速くてIList<T>のインデクサを通すとなぜか10倍弱の時間がかかるようになる。
List<T>のインデクサアクセスは配列の添字アクセスより遅いらしい。これもIList<T>のインデクサを通すと配列ほどじゃないけど遅くなる。
array: 662 (1.00) (IList<T>)array: 6259 (9.45) list: 1325 (2.00) (IList<T>)list: 1592 (2.40) IListArrayWrapper: 1166 (1.76)ksksts / junk / source — bitbucket.org
IListArrayWrapperは値型の配列をラップしてポインタを使ったunsafeなインデクサを実装したクラス。
ジェネリックに実装しようとしたけどジェネリック引数の型のポインタを取得する方法が分からなかった。"where T : struct"の指定があればOKにしてもいいんじゃないかと思うけど、とりあえずできなかったからint型用のコードにした。
ダメだったコード:
public class IListArrayWrapper<T> where T : struct { public T[] Source { get; set; } unsafe public T this[int index] { get { fixed (T* p = &Source[0]) return *(p + index); } set { fixed (T* p = &Source[0]) *(p + index) = value; } } public IListArrayWrapper(IList<T> source) { Source = (T[])source; } }ksksts / junk / source — bitbucket.org
まとめとしては、配列とIList<T>を実装したランダムアクセス可能なコレクションの両方を処理する&パフォーマンスを気にするコードを書く場合は分けたほうがいいみたい。
C#でのソートアルゴリズムのまとめ(sort algorithms in C#)
stereopsis : graphics : radix tricksやSTLportのソースをパクったりしたC#でのソートのまとめ。
それぞれのアルゴリズムとか関連エントリとか。
配列に対するソートとList<T>に対するソートに対するソートとの差やEnumerable.OrderBy メソッド (System.Linq)とかも含めた測定結果は次の通り。
まとめ:
それぞれのアルゴリズムとか関連エントリとか。
- ksksts's blog: C#で実装したradix sortとArray.Sortのquick sortとの比較
- ksksts's blog: C#で符号付整数/浮動小数点数対応の基数ソート(radix sort)
- ksksts's blog: C#でin place merge sortを書いてみたらかなり遅かった
- ksksts's blog: バッファを使用するマージソートも書いてみた(merge sort with buffer in C#)
- ksksts's blog: イントロソートも書いてみた(introsort in C#)
配列に対するソートとList<T>に対するソートに対するソートとの差やEnumerable.OrderBy メソッド (System.Linq)とかも含めた測定結果は次の通り。
size: 1000 Array.Sort: 1186 (1.00) List.Sort: 1050 (0.89) Array.OrderBy: 6592 (5.56) List.OrderBy: 3305 (2.78) CombSort Array: 31971 (26.94) CombSort List: 6909 (5.82) NumberRadixSorter.Sort Array: 4943 (4.17) NumberRadixSorter.Sort List: 2291 (1.93) InPlaceMergeSorter.Sort Array: 40326 (33.98) InPlaceMergeSorter.Sort List: 12642 (10.65) MergeSorter.Sort Array: 24463 (20.61) MergeSorter.Sort List: 9604 (8.09) IntroSorter.Sort Array: 22909 (19.30) IntroSorter.Sort List: 4158 (3.50) size: 10000 Array.Sort: 12771 (1.00) List.Sort: 12497 (0.98) Array.OrderBy: 41599 (3.26) List.OrderBy: 41126 (3.22) CombSort Array: 465802 (36.47) CombSort List: 101879 (7.98) NumberRadixSorter.Sort Array: 27803 (2.18) NumberRadixSorter.Sort List: 22234 (1.74) InPlaceMergeSorter.Sort Array: 553976 (43.38) InPlaceMergeSorter.Sort List: 202408 (15.85) MergeSorter.Sort Array: 224039 (17.54) MergeSorter.Sort List: 128939 (10.10) IntroSorter.Sort Array: 158357 (12.40) IntroSorter.Sort List: 52881 (4.14) size: 100000 Array.Sort: 146888 (1.00) List.Sort: 145938 (0.99) Array.OrderBy: 520984 (3.55) List.OrderBy: 518649 (3.53) CombSort Array: 6044506 (41.15) CombSort List: 1311650 (8.93) NumberRadixSorter.Sort Array: 279857 (1.91) NumberRadixSorter.Sort List: 218608 (1.49) InPlaceMergeSorter.Sort Array: 8215845 (55.93) InPlaceMergeSorter.Sort List: 2860999 (19.48) MergeSorter.Sort Array: 2931658 (19.96) MergeSorter.Sort List: 1869126 (12.72) IntroSorter.Sort Array: 1945971 (13.25) IntroSorter.Sort List: 658093 (4.48) size: 1000000 Array.Sort: 1666666 (1.00) List.Sort: 1668472 (1.00) Array.OrderBy: 6961424 (4.18) List.OrderBy: 6901036 (4.14) CombSort Array: 76084478 (45.65) CombSort List: 16674472 (10.00) NumberRadixSorter.Sort Array: 2864637 (1.72) NumberRadixSorter.Sort List: 2245710 (1.35) InPlaceMergeSorter.Sort Array: 110817348 (66.49) InPlaceMergeSorter.Sort List: 38880094 (23.33) MergeSorter.Sort Array: 35271234 (21.16) MergeSorter.Sort List: 20756358 (12.45) IntroSorter.Sort Array: 24093398 (14.46) IntroSorter.Sort List: 8047601 (4.83)ksksts / junk / source — bitbucket.org
まとめ:
- 安定でないソートならArray.Sort メソッド (System)かList(T).Sort メソッド (System.Collections.Generic)
- 安定なソートならEnumerable.OrderBy メソッド (System.Linq)
- STLportのソースをパクった結果としては速い順にIntroSort(STLのstd::sort), InPlaceMergeSort(STLのstd::stable_sortのバッファを使わない版), MergeSort(STLのstd::stable_sortのバッファを使う版)。そしてRadixSort(基数ソート)はこいつらより速い。
配列の添字でのランダムアクセスはList<T>のインデクサでのアクセスより遅い?(これはIList<T>を経由しているのが原因だったみたい ksksts's blog: 配列やList<T>をIList<T>型のインデクサでアクセスするとパフォーマンスが悪い(don't use IList<T>'s indexer))
2009/11/14
イントロソートも書いてみた(introsort in C#)
バッファを使わないマージソートとバッファを使うマージソートのついでにイントロソートもSTLportからパクってみた。
STLportの_algo.c, _heap.h, _heap.cの__introsort_loop, __unguarded_partition, __final_insertion_sort, __unguarded_linear_insert, __partial_sort, __make_heap, __adjust_heap, __push_heap_aux, __pop_heap_aux, sort_heapあたりをC#で書いただけ。
かなり手抜きだと思う。マージソートとの速度差をみたいだけだから。
結果は次の通り。
Array.Sortの十数倍の時間がかかっている。バッファを使用するマージソートよりは多少速い。
ソート対象を配列からList<T>に変更するだけで数倍良くなるはずだから後でまとめて試してみる。
今になって基数ソート(radix sort)がかなり速いということを実感。
STLportの_algo.c, _heap.h, _heap.cの__introsort_loop, __unguarded_partition, __final_insertion_sort, __unguarded_linear_insert, __partial_sort, __make_heap, __adjust_heap, __push_heap_aux, __pop_heap_aux, sort_heapあたりをC#で書いただけ。
かなり手抜きだと思う。マージソートとの速度差をみたいだけだから。
結果は次の通り。
Array.Sortの十数倍の時間がかかっている。バッファを使用するマージソートよりは多少速い。
ソート対象を配列からList<T>に変更するだけで数倍良くなるはずだから後でまとめて試してみる。
今になって基数ソート(radix sort)がかなり速いということを実感。
size: 1000 IntroSorter.Sort: 20622 (18.78) Array.Sort: 1098 (1.00) size: 10000 IntroSorter.Sort: 139073 (10.98) Array.Sort: 12667 (1.00) size: 100000 IntroSorter.Sort: 1771360 (12.22) Array.Sort: 144995 (1.00) size: 1000000 IntroSorter.Sort: 21275858 (12.85) Array.Sort: 1656005 (1.00)ksksts / junk / source — bitbucket.org
2009/11/12
バッファを使用するマージソートも書いてみた(merge sort with buffer in C#)
in-place merge sortをEQUATEC Profilerでプロファイルしてみたけど特におかしいと感じるところは見つからず、結局マージ回数が多すぎじゃないかと思う(あとお手玉Rotatenってやっぱり速いのかなと)。
in-placeじゃないアルゴリズムとの比較をするためにSTLportの_algo.cのstable_sortから__stable_sort_adaptive, __merge_sort_with_buffer, __chunk_insertion_sort, __merge_sort_loop, mergeあたりを辿って、ソート対象の要素個数分のバッファを使用するマージソートをC#で書いてみた。
結果は下記の通り。in-place merge sortよりは良い。でもまだArray.Sortの十数倍の時間がかかっている。
いろいろ小賢しいことをやっているからもっとシンプルな素直なコードを書いてみたほうがいいかもしれない。
in-placeじゃないアルゴリズムとの比較をするためにSTLportの_algo.cのstable_sortから__stable_sort_adaptive, __merge_sort_with_buffer, __chunk_insertion_sort, __merge_sort_loop, mergeあたりを辿って、ソート対象の要素個数分のバッファを使用するマージソートをC#で書いてみた。
結果は下記の通り。in-place merge sortよりは良い。でもまだArray.Sortの十数倍の時間がかかっている。
いろいろ小賢しいことをやっているからもっとシンプルな素直なコードを書いてみたほうがいいかもしれない。
size: 1000 InPlaceMergeSorter.Sort: 37314 (33.24) MergeSorter.Sort: 21869 (19.48) Array.Sort: 1122 (1.00) size: 10000 InPlaceMergeSorter.Sort: 502465 (39.24) MergeSorter.Sort: 198861 (15.53) Array.Sort: 12805 (1.00) size: 100000 InPlaceMergeSorter.Sort: 7425488 (47.00) MergeSorter.Sort: 2556782 (16.18) Array.Sort: 157988 (1.00) size: 1000000 InPlaceMergeSorter.Sort: 98928451 (58.09) MergeSorter.Sort: 31298754 (18.38) Array.Sort: 1703143 (1.00)ksksts / junk / source — bitbucket.org
2009/11/10
C#でin place merge sortを書いてみたらかなり遅かった
C#で簡単に使いまわせる安定なソートを用意しておこうと思ってIn place stable Sort (merge sort)を参考に書いてみたらかなり遅かった。
In place stable Sort (merge sort)のソースの不自然さが気になったからSTLportの_algo.cとか_algobase.cとかも参考にした。
Array.Sortの数十倍時間がかかるとか、これじゃあまり使う気にならない。
何かミスっているのかもしれないし、in placeでなければもうちょっとましかもしれない。明日もう少し調べてみる。
In place stable Sort (merge sort)のソースの不自然さが気になったからSTLportの_algo.cとか_algobase.cとかも参考にした。
Array.Sortの数十倍時間がかかるとか、これじゃあまり使う気にならない。
何かミスっているのかもしれないし、in placeでなければもうちょっとましかもしれない。明日もう少し調べてみる。
size: 1000 merge sort: 37580 (30.34) Array.Sort: 1238 (1.00) size: 10000 merge sort: 506318 (41.50) Array.Sort: 12199 (1.00) size: 100000 merge sort: 7170923 (50.21) Array.Sort: 142813 (1.00) size: 1000000 merge sort: 97304833 (60.94) Array.Sort: 1596662 (1.00)ksksts / junk / source — bitbucket.org
2009/11/07
C#で符号付整数/浮動小数点数対応の基数ソート(radix sort)
Radium Software Development経由でRadix Sort Revisitedを見て、さらに2006-07-23 - togeの日記経由でstereopsis : graphics : radix tricksも読んだから書いてみた。
ksksts's blog: C#で実装したradix sortとArray.Sortのquick sortとの比較で実行速度で.NET FrameworkのArray.Sort メソッド (System)とかを上回るのは難しいと感じた時点で目的を実装例を示すだけに変更。Decimal型やString型に対応しようと思っていたけど、面倒くさくなったから符号なし整数型、符号付整数型、浮動小数点数型で止めとく。
stereopsis : graphics : radix tricksのコードを参考にして書いた。
おおまかな処理の流れは次の通り。
インターフェースはこんな感じ。
RadixSorter.cs
ソートに使用するキーを指定しながらソートできるのがおもしろい。
あとは書いてみて思ったこととか。
ヒストグラムをずらして作っておくと加算する処理が簡単になること、降順にソートする場合はヒストグラムの加算の処理で対応できること。
NumberRadixSorter.csのSort(IList list, SortOrder sortOrder)
符号付整数型でも同様。ただし符号ビットを含む部分だけ工夫する。
NumberRadixSorter.csのSort(IList list, SortOrder sortOrder)
浮動小数点数の場合は、要素が数値でそれを変更できる場合はヒストグラムを作るときにビットをflipして、ソート後に元に戻すとかできる(stereopsis : graphics : radix tricksのコードでやっている)。
NumberRadixSorter.csのSort(IList list, SortOrder sortOrder)
ksksts / junk / source — bitbucket.org
ksksts's blog: C#で実装したradix sortとArray.Sortのquick sortとの比較で実行速度で.NET FrameworkのArray.Sort メソッド (System)とかを上回るのは難しいと感じた時点で目的を実装例を示すだけに変更。Decimal型やString型に対応しようと思っていたけど、面倒くさくなったから符号なし整数型、符号付整数型、浮動小数点数型で止めとく。
stereopsis : graphics : radix tricksのコードを参考にして書いた。
おおまかな処理の流れは次の通り。
- ヒストグラムをまとめて作成(histgramming)
- ヒストグラムの値を加算(sum the histgrams)
- 要素の並べ替え(read/write histgram, copy)
インターフェースはこんな感じ。
RadixSorter.cs
public static void Sort<T>(IList<T> list, Func<T, UInt32> converter, SortOrder sortOrder)
ソートに使用するキーを指定しながらソートできるのがおもしろい。
struct Pair<TFirst, TSecond> { public TFirst First { get; set; } public TSecond Second { get; set; } }このPair型に対して、
RadixSorter.Sort(result, x => x.Second, RadixSorter.SortOrder.Ascending); RadixSorter.Sort(result, x => x.First, RadixSorter.SortOrder.Ascending);とすると、次のCompareTo(Firstで比較&Firstが等しい場合はSecondで比較)でソートした場合と同じ結果が得られる。
public int CompareTo(Pair<TFirst, TSecond> x) { var result = First.CompareTo(x.First); if (result == 0) result = Second.CompareTo(x.Second); return result; }
あとは書いてみて思ったこととか。
ヒストグラムをずらして作っておくと加算する処理が簡単になること、降順にソートする場合はヒストグラムの加算の処理で対応できること。
NumberRadixSorter.csのSort(IList
// histgramming var histgramOffset = ascending ? 1 : -1; foreach (var x in list) { var y = x; for (var p = 0; p < tables.Length; p++) { tables[p][(y + histgramOffset) & 0xFF]++; y = y >> 8; } } // sum the histgrams if (ascending) { foreach (var table in tables) { table[0] = 0; for (var n = 1; n < table.Length; n++) table[n] += table[n - 1]; } } else { foreach (var table in tables) { table[0xFF] = 0; for (var n = 0xFE; n >= 0x00; n--) table[n] += table[n + 1]; } }
符号付整数型でも同様。ただし符号ビットを含む部分だけ工夫する。
NumberRadixSorter.csのSort(IList
// sum the histgrams if (ascending) { for (var p = 0; p < tables.Length - 1; p++) { var table = tables[p]; table[0] = 0; for (var n = 1; n < table.Length; n++) table[n] += table[n - 1]; } { var table = tables[tables.Length - 1]; table[0x80] = 0; for (var n = 0x81; n <= 0xFF; n++) table[n] += table[n - 1]; table[0x00] += table[0xFF]; for (var n = 0x01; n < 0x80; n++) table[n] += table[n - 1]; } } else { for (var p = 0; p < tables.Length - 1; p++) { var table = tables[p]; table[0xFF] = 0; for (var n = 0xFE; n >= 0x00; n--) table[n] += table[n + 1]; } { var table = tables[tables.Length - 1]; table[0x7F] = 0; for (var n = 0x7E; n >= 0x00; n--) table[n] += table[n + 1]; table[0xFF] += table[0x00]; for (var n = 0xFE; n >= 0x80; n--) table[n] += table[n + 1]; } }
浮動小数点数の場合は、要素が数値でそれを変更できる場合はヒストグラムを作るときにビットをflipして、ソート後に元に戻すとかできる(stereopsis : graphics : radix tricksのコードでやっている)。
NumberRadixSorter.csのSort(IList
// histgramming var histgramOffset = ascending ? 1 : -1; for (var n = 0; n < list.Count; n++) { var y = BitConverter.DoubleToInt64Bits(list[n]); y ^= -(Int64)((UInt64)y >> 63) | unchecked((Int64)0x8000000000000000); // flip list[n] = BitConverter.Int64BitsToDouble(y); for (var p = 0; p < tables.Length; p++) { tables[p][(y + histgramOffset) & 0xFF]++; y = y >> 8; } }
// read/write histgram, copy IList<Double> array = new Double[list.Count]; Action swap = () => { var temp = list; list = array; array = temp; }; for (var p = 0; p < tables.Length - 1; p++) { var table = tables[p]; foreach (var x in list) { var y = BitConverter.DoubleToInt64Bits(x); y = (y >> p * 8) & 0xFF; array[table[y]] = x; table[y]++; } swap(); } { var p = tables.Length - 1; var table = tables[p]; foreach (var x in list) { var w = BitConverter.DoubleToInt64Bits(x); var y = (w >> p * 8) & 0xFF; w ^= (Int64)((UInt64)w >> 63) - 1 | unchecked((Int64)0x8000000000000000); // flip back array[table[y]] = BitConverter.Int64BitsToDouble(w); table[y]++; } swap(); }
ksksts / junk / source — bitbucket.org
2009/11/06
Decimal型のフォーマット
Decimal.GetBits メソッド (System)で解説されている。
指数部が10の累乗なんだからSingle/Doubleとは違う分かりやすい値に変えた。
結果は次の通り。0.0Mと-0.0Mの指数部は1になるみたい。
Decimal 数値のバイナリ表現は、1 ビットの符号、96 ビットの整数、および整数値を除算し、小数部を指定するために使用するスケール ファクタから構成されます。スケール ファクタは黙示的に数値 10 になり、0 から 28 の範囲の指数で累乗されます。Decimal.GetBits メソッドの戻り値はint[]でいまいち分かりにくいから確認用のコードを書いた(下手なコードだと思うけど良い方法を思いつけなかった)。
戻り値は、4 要素の 32 ビット符号付き整数配列です。
この配列の 1 ~ 3 番目の要素は、96 ビット整数の下位 32 ビット、中位 32 ビット、および上位 32 ビットをそれぞれ格納しています。
4 番目の要素は、スケール ファクタと符号を格納しています。この要素は、次に示す部分から構成されています。
ビット 0 ~ 15 の下位ワードは未使用で、0 である必要があります。
ビット 16 ~ 23 には、0 から 28 までの範囲の指数部を格納する必要があります。この指数部は、整数を除算する 10 の累乗を示します。
ビット 24 ~ 30 は未使用で、0 である必要があります。
ビット 31 は符号を格納している必要があります。0 は正、1 は負を表します。
ビット形式では負の 0 と正の 0 が区別されます。これらの値はすべての演算で等値として扱われます。
指数部が10の累乗なんだからSingle/Doubleとは違う分かりやすい値に変えた。
unsafe private static void PrintDecimal() { Func<Decimal, byte[]> toBytes = x => { var p = (byte*)&x; var bytes = new byte[sizeof(Decimal)]; for (var n = 0; n < bytes.Length; n++) bytes[n] = *p++; return bytes; }; WriteLine("Decimal:"); WriteLine("0.0M", toBytes(0.0M)); WriteLine("-0.0M", toBytes(-0.0M)); WriteLine("0.12M", toBytes(0.12M)); WriteLine("1.2M ", toBytes(1.2M)); WriteLine("12M ", toBytes(12M)); WriteLine("120M ", toBytes(120M)); WriteLine("1200M", toBytes(1200M)); WriteLine("-0.12M", toBytes(-0.12M)); WriteLine("-1.2M ", toBytes(-1.2M)); WriteLine("-12M ", toBytes(-12M)); WriteLine("-120M ", toBytes(-120M)); WriteLine("-1200M", toBytes(-1200M)); WriteLine("Decimal.MinValue", toBytes(Decimal.MinValue)); WriteLine("Decimal.MaxValue", toBytes(Decimal.MaxValue)); WriteLine(""); } private static void WriteLine(string value) { Console.WriteLine(value); } private static void WriteLine(string label, byte[] bytes) { Console.Write("{0}: 0x", label); var bs = (byte[])bytes.Clone(); Array.Reverse(bs); foreach (var x in bs) Console.Write("{0:X2}", x); Console.WriteLine(); }ksksts / junk / source — bitbucket.org
結果は次の通り。0.0Mと-0.0Mの指数部は1になるみたい。
Decimal: 0.0M: 0x00000000000000000000000000010000 -0.0M: 0x00000000000000000000000080010000 0.12M: 0x000000000000000C0000000000020000 1.2M : 0x000000000000000C0000000000010000 12M : 0x000000000000000C0000000000000000 120M : 0x00000000000000780000000000000000 1200M: 0x00000000000004B00000000000000000 -0.12M: 0x000000000000000C0000000080020000 -1.2M : 0x000000000000000C0000000080010000 -12M : 0x000000000000000C0000000080000000 -120M : 0x00000000000000780000000080000000 -1200M: 0x00000000000004B00000000080000000 Decimal.MinValue: 0xFFFFFFFFFFFFFFFFFFFFFFFF80000000 Decimal.MaxValue: 0xFFFFFFFFFFFFFFFFFFFFFFFF00000000
2009/11/04
IList<T>の走査処理の比較(インデクサ/GetEnumerator/foreach)
twitterで「IList<t>を走査する場合でforとforeachとで意外に差が出た」とか書いたら「IListの内部実装にもよりますけど、結構変わったりしますよねー」とRTがあった。
それで処理速度を計測するコードを書いてみたらよく分からない結果になった。
結果は次の通り。
配列ではインデクサでのアクセスがforeachより遅いのにList<t>では逆になるのが意味が分からない。あと両方でGetEnumeratorでのアクセスがforeachよりも速いのも意味が分からない(foreachの方が最適化する余地があるんじゃないかと思う)。
こんな処理にフォーカスした速度なんかほとんどのケースで無視していいだろうから、インデクサでのアクセスが必要じゃない処理では常にforeachを使うけど。
書きながらList<t>.ForEachを思い出した(IList<t>には無い)。使える場合ならforeachよりもそれを使うと思う。ILとかコンパイル後のコードはたぶん見ない。
それで処理速度を計測するコードを書いてみたらよく分からない結果になった。
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace IListEnumerationBenchmark { class Program { static void Main(string[] args) { var size = 1000000; var count = 10; var sw = new Stopwatch(); Action<IList<int>> run = list => { var useIndexerTicks = 0L; var useEnumeratorTicks = 0L; var useForeachTicks = 0L; for (var c = 0; c < count; c++) { var random = new Random(c); for (var n = 0; n < list.Count; n++) list[n] = random.Next(int.MinValue, int.MaxValue); // use indexer sw.Reset(); sw.Start(); var useIndexerResult = UseIndexer(list); sw.Stop(); useIndexerTicks += sw.ElapsedTicks; // use enumerator sw.Reset(); sw.Start(); var useEnumeratorResult = UseEnumerator(list); sw.Stop(); useEnumeratorTicks += sw.ElapsedTicks; // use foreach sw.Reset(); sw.Start(); var useForeachResult = UseForeach(list); sw.Stop(); useForeachTicks += sw.ElapsedTicks; // correctness if (useIndexerResult != useForeachResult) Console.WriteLine("useIndexerResult: {0}, useForeachResult: {1}", useIndexerResult, useForeachResult); if (useEnumeratorResult != useForeachResult) Console.WriteLine("useEnumeratorResult: {0}, useForeachResult: {1}", useEnumeratorResult, useForeachResult); } Console.WriteLine("use indexer: {0} ({1:F})", useIndexerTicks / count, useIndexerTicks / (double)useForeachTicks); Console.WriteLine("use enumerator: {0} ({1:F})", useEnumeratorTicks / count, useEnumeratorTicks / (double)useForeachTicks); Console.WriteLine("use foreach: {0} ({1:F})", useForeachTicks / count, useForeachTicks / (double)useForeachTicks); Console.WriteLine(); }; // int[] { Console.WriteLine("int[]: "); var array = new int[size]; run(array); } // List<int> { Console.WriteLine("List<int>: "); var list = new List<int>(); for (var n = 0; n < size; n++) list.Add(0); run(list); } } static int UseIndexer(IList<int> list) { var result = 0; for (var n = 0; n < list.Count; n++) result = unchecked(result + list[n]); return result; } static int UseEnumerator(IList<int> list) { var result = 0; var e = list.GetEnumerator(); while (e.MoveNext()) result = unchecked(result + e.Current); return result; } static int UseForeach(IList<int> list) { var result = 0; foreach (var x in list) result = unchecked(result + x); return result; } } }ksksts / junk / source — bitbucket.org
結果は次の通り。
配列ではインデクサでのアクセスがforeachより遅いのにList<t>では逆になるのが意味が分からない。あと両方でGetEnumeratorでのアクセスがforeachよりも速いのも意味が分からない(foreachの方が最適化する余地があるんじゃないかと思う)。
こんな処理にフォーカスした速度なんかほとんどのケースで無視していいだろうから、インデクサでのアクセスが必要じゃない処理では常にforeachを使うけど。
書きながらList<t>.ForEachを思い出した(IList<t>には無い)。使える場合ならforeachよりもそれを使うと思う。ILとかコンパイル後のコードはたぶん見ない。
int[]: use indexer: 578500 (5.57) use enumerator: 87133 (0.84) use foreach: 103895 (1.00) List<int>: use indexer: 132922 (0.76) use enumerator: 154705 (0.89) use foreach: 174277 (1.00)追記: 上の結果はIList<T>を通した結果だから不正確。ksksts's blog: 配列やList<T>をIList<T>型のインデクサでアクセスするとパフォーマンスが悪い(don't use IList<T>'s indexer)を参照。
2009/11/03
C#で実装したradix sortとArray.Sortのquick sortとの比較
radix sort(基数ソート)のコードを書いてて、適当に処理速度を比較してみたら遅かったから簡単なベンチマークを書いてみた。
比較するのは次の3つ。
結果は次の通り。
適当に比較したときは処理時間がArray.Sortの2倍以上で話にならないと思っていたけど、要素数が膨大になるにつれてそれなりに差が小さくなってきて少し嬉しい。
comb sortは遅いけどバブルソートの改良版と考えるとそれなりに良いような気がする。
比較するのは次の3つ。
- radix sort(WikipediaのRadix sortとstereopsisのradix tricksを参考にして書いたコード)
- comb sort(WikipediaのComb sortを参考に書いたコード、というかほぼそのまま)
- quick sort(.NET Framework 3.5のArray.Sort)
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace SortBenchmark { class Program { static void Main(string[] args) { for (var size = 1000; size <= 1000000; size *= 10) Run(size); } static void Run(int size) { Console.WriteLine("size: {0}", size); var count = 10; var sw = new Stopwatch(); var radixSortTicks = 0L; var combSortTicks = 0L; var arraySortTicks = 0L; for (var c = 0; c < count; c++) { var random = new Random(c); var list = new UInt32[size]; for (var n = 0; n < list.Length; n++) list[n] = (UInt32)random.Next(Int32.MinValue, Int32.MaxValue); // radix sort var radixSortResult = (IList<UInt32>)list.Clone(); sw.Reset(); sw.Start(); RadixSort(radixSortResult); sw.Stop(); radixSortTicks += sw.ElapsedTicks; // comb sort var combSortResult = (IList<UInt32>)list.Clone(); sw.Reset(); sw.Start(); CombSort(combSortResult); sw.Stop(); combSortTicks += sw.ElapsedTicks; // Array.Sort var arraySortResult = list.ToArray(); sw.Reset(); sw.Start(); Array.Sort(arraySortResult); sw.Stop(); arraySortTicks += sw.ElapsedTicks; // correctness for (var n = 0; n < size; n++) { if (radixSortResult[n] != arraySortResult[n]) Console.WriteLine("radixSortResult[{0}]: {1}, arraySortResult[{0}]: {2}", n, radixSortResult[n], arraySortResult[n]); if (combSortResult[n] != arraySortResult[n]) Console.WriteLine("combSortResult[{0}]: {1}, arraySortResult[{0}]: {2}", n, combSortResult[n], arraySortResult[n]); } } Console.WriteLine("radix sort: {0} ({1:F})", radixSortTicks / count, radixSortTicks / (double)arraySortTicks); Console.WriteLine("comb sort: {0} ({1:F})", combSortTicks / count, combSortTicks / (double)arraySortTicks); Console.WriteLine("Array.Sort: {0} ({1:F})", arraySortTicks / count, arraySortTicks / (double)arraySortTicks); } static void RadixSort(IList<UInt32> list) { var tables = new int[sizeof(UInt32)][]; for (var p = 0; p < tables.Length; p++) tables[p] = new int[0xFF + 1]; // histgramming foreach (var x in list) { var y = x; for (var p = 0; p < tables.Length; p++) { tables[p][(y + 1) & 0xFF]++; y = y >> 8; } } // sum the histgrams foreach (var table in tables) { table[0] = 0; for (var n = 1; n < table.Length; n++) table[n] += table[n - 1]; } // read/write histgram, copy IList<UInt32> array = new UInt32[list.Count]; Action swap = () => { var temp = list; list = array; array = temp; }; for (var p = 0; p < tables.Length; p++) { var table = tables[p]; foreach (var x in list) { var y = (x >> p * 8) & 0xFF; array[table[y]] = x; table[y]++; } swap(); } } static void CombSort(IList<UInt32> list) { const double shrinkFactor = 1.247330950103979; // 1.0 / (1.0 - 1.0 / Math.Pow(Math.E, Math.PI)); var gap = list.Count; var swapped = true; while (gap > 1 || swapped) { if (gap > 1) gap = (int)(gap / shrinkFactor); swapped = false; for (int i = 0; i + gap < list.Count; i++) { if (list[i].CompareTo(list[i + gap]) > 0) { var t = list[i]; list[i] = list[i + gap]; list[i + gap] = t; swapped = true; } } } } } }ksksts / junk / source — bitbucket.org
結果は次の通り。
適当に比較したときは処理時間がArray.Sortの2倍以上で話にならないと思っていたけど、要素数が膨大になるにつれてそれなりに差が小さくなってきて少し嬉しい。
comb sortは遅いけどバブルソートの改良版と考えるとそれなりに良いような気がする。
size: 1000 radix sort: 3541 (2.88) comb sort: 27689 (22.50) Array.Sort: 1230 (1.00) size: 10000 radix sort: 20451 (1.68) comb sort: 417744 (34.33) Array.Sort: 12167 (1.00) size: 100000 radix sort: 207950 (1.45) comb sort: 5288921 (36.95) Array.Sort: 143143 (1.00) size: 1000000 radix sort: 2081360 (1.30) comb sort: 65056745 (40.50) Array.Sort: 1606207 (1.00)
2009/11/01
C#で浮動小数点数のバイナリ表現(ビットパターン)を取得する
double型の値のバイト表現をlong型で得るために、BitConverter.DoubleToInt64Bitsを使う場合とunsafeで無理やりキャストするのではどちらが速いのか比較してみた。
手元の環境ではBitConverter.DoubleToInt64Bitsの方が速かった(経過時間が50%)けど、ほとんどのケースでは相対的に十分軽い処理になるだろうからどっちでもいい。ただキャストを使うやり方はunsafeの指定が必要だし、見た目もアレだからfloat型を相手にする場合以外は使わないと思う(BitConverter.FloatToInt32Bitsは存在しない)。ストップウォッチのリセットを忘れてた。処理速度に差はない。
そもそもdouble型と整数型との&演算子があればこんなことしなくていいのが残念。
そもそもdouble型と整数型との&演算子があればこんなことしなくていいのが残念。
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace BinaryRepresentation { class Program { static void Main(string[] args) { var sw = new Stopwatch(); var random = new Random(0); var num = 1000000; var vals = new double[num]; for (var n = 0; n < vals.Length; n++) vals[n] = random.NextDouble(); // BitConverter.DoubleToInt64Bits var bc = new long[vals.Length]; { sw.Reset(); sw.Start(); for (var n = 0; n < vals.Length; n++) bc[n] = BitConverter.DoubleToInt64Bits(vals[n]); sw.Stop(); Console.WriteLine("BitConverter.DoubleToInt64Bits: {0}", sw.ElapsedMilliseconds); } // unsafe cast var uc = new long[vals.Length]; unsafe { sw.Reset(); sw.Start(); for (var n = 0; n < vals.Length; n++) { var val = vals[n]; uc[n] = *(long*)&val; } sw.Stop(); Console.WriteLine("unsafe cast: {0}", sw.ElapsedMilliseconds); } // correctness for (var n = 0; n < vals.Length; n++) if (bc[n] != uc[n]) Console.WriteLine("bc[{0}]: {1:X}, uc[{0}]: {2:X}", n, bc[n], uc[n]); } } }ksksts / junk / source — bitbucket.org
2009/10/31
google-code-prettifyを使えるようにしてみた
class Voila { public: // Voila static const string VOILA = "Voila"; // will not interfere with embedded tags. }
はじめにgoogle-code-prettifyプロジェクトでドキュメントを眺める。
レンタルサーバにgoogle-code-prettifyのファイルを置こうかと思ったけど、 Learning Log Book: Bloggerでシンタックス・ハイライト で紹介されていたHTML/JavaScriptガジェットを使う方法でやってみた。
やり方は、HTML/JavaScriptガジェットを追加して、タイトルは空欄、コンテンツにJavaScriptのソース(prettify.jsと必要ならlang-xxx.jsとかも)とCSS(prettify.css)を貼り付けるというだけ。HTML/JavaScriptガジェットをこんな風に使うのかと感心。
あとはページのHTMLの編集でbody要素にonload="prettyPrint()"を追加すると、<pre class="prettyprint">...</pre>や<code class="prettyprint">...</code>のコードが色付けされて表示される。
"<"と">"は実体参照で書かなきゃいけないことに注意(というか面倒くさい、使わないかもという気がしてくる)。
あなたのソースコードを彩る、Syntax Highlighterまとめ | Blog.37to.net がgoogle-code-prettify以外にもいろいろと紹介していて参考になった。
その中でSyntaxHighlighterは対応言語が多くて良さそう。特に、Usageによるとバージョン2.1では<script type="syntaxhighlighter" class="brush: js"><![CDATA[...]]></script>というようにscript要素+CDATAを使うからコードをそのまま貼り付けることができることと、Hostingで「http://alexgorbatchev.com/pub/sh/にファイルを置いておくからこれを使っていいよ」とホストすることをはっきり書いてくれていること。
というのも、google-code-prettifyのSVNリポジトリのソースを読み込む方法を紹介しているページを見かけてそれってどうかのかなと思った。
すくなくともgoogle-code-prettifyのREADMEでは「you will need to make sure the css and js file are on your server」と書かれている。そのあたりの議論があった/あるのか知らないし、Project Hosting on Google Code がOKを出しているかもしれないけど。
SyntaxHighlighterみたいにライブラリの開発者がホストすると表明してくれると使いやすいし、よく使われるようになりやすいかも。
GoogleやYahooのAJAXライブラリのホスティング(AJAX Libraries API - Google Code, The YUI Configurator)に追加されたら最高だろうな。
2009/10/27
チャート(グラフ)の見え方と印象
tumblrですこしおもしろい見せ方をしているチャートが流れていたからいじってみた。
流れてきた経路は 独り52-いじめられるサラリーマン の「図1 男女別平均年収の推移」が 日本人は世界で4番目に貧しい国 年収200万円以下が1000万人もいる! | 無題ブログ の643に貼られて、それを the Emitter がtumblrにポストしたのが Windsock - Online trend following - theemitter: plasticdreams: 643 名前:.. 経由でdashboardに表示されていた、という感じ(たぶん)。
おもしろいと思ったのは男女の平均年収に大きな差があるけど、男性の平均年収額を左縦軸、女性の平均年収額を右縦軸にとって、両方の縦軸の目盛り間隔を合わせていたことと1995年時のプロット位置を一致させていたことの2点。
前者は、はじめは年収400万円台と200万円台では10万円前後の増減による効用は違うだろうと思ったけど、収入が200万円でも400万円でも10万円前後の金額の捉え方は同じようなものに思えたこと(平均を個々のケースにあてはめて考えるのはおかしいけど)。
後者についてはわざわざ元データの数値を合わせないで増減額か増減率を示したほうがいいんじゃないかと思ったから。
チャートを作るためにデータを探してみる。
民間給与実態統計調査結果|長期時系列データ|国税庁 の M03.xls の「3-1」シートの「1年勤続者」-「給料・手当(1)」が同じ数値っぽい(賞与抜きの額面の金額)。
いくつか作ってみたチャートを貼ってみる。
共通なことは、横軸は1995年から2007年まで、縦軸は金額または増減率、金額の場合の単位は千円。
そのまんまな平均年収の折れ線のチャートA。
男女ともに横ばい(差は縮まらない)。
チャートAの縦軸の下限を200万円に変更したチャートB。
男女間の差額とそれが変わらないことを強調する場合はこれがいいかも。
男性の平均年収を左軸、女性の平均年数を右軸に割り当てたチャートC。
目盛り間隔はExcel2007におまかせ。左軸の5万円が右軸の4万円に対応している。微妙な差を気付かれにくくするにはこれみたいのがいいかも。
チャートCの左右の縦軸のスケールを一致させたチャートD。
流れてきた元のチャートはこんな感じ。「1995年から2000年までは男女の平均年収が約10万円増加したけど、その後、男性の平均年収は下がって1995年と同水準に戻ったね。それに対して女性の平均年収は2000年から横ばいだね。(※左右の縦軸に注意)」。
そもそも増減額を示せばいいじゃない?ということで1995年を基準とした増減額のチャートE。
増減額は分かるけど実際の年収額が分からない。「女性の平均年収は男性と比較すると堅調です」みたいなことを伝えたい場合はいいかも。
タイトルが適切じゃないけど直すのが面倒だからこのままで。
増減額じゃなくて、1995年を1.0とした場合の増減率のチャートF。
増減率なので分母の影響が出てしまう。チャートEよりも「女性の平均年収は男性と比較すると堅調です」を強く伝えられるはずなんだけど、男性の2001年からの下落具合がチャートEよりも弱くなる点に注意。
これもタイトルが適切じゃないけど直すのが面倒だからこのままで。
チャートE/Fに実際の平均年収額をラベルで付けたのがチャートH/I。
チャートGが抜けているのはただのミス、飛ばしてしまったみたい。
これもタイトルが適切じゃないけど直すのが面倒だからこのままで。あと凡例は不要だった。
独り52-いじめられるサラリーマン や 日本人は世界で4番目に貧しい国 年収200万円以下が1000万人もいる! | 無題ブログ が何を伝えようとしているのか知らないし、チャートがおかしいとか言うつもりは全くない(読んでいないし読む気もない)。
単に見せ方を変えると印象が変わりそうなデータだと思ったからやってみた。
自分なら特に強い意図がなければI/H、A/Bあたりを使うと思う。
捏造棒グラフ - Google 検索 とか Amazon.co.jp: マッキンゼー流図解の技術: ジーン ゼラズニー, 数江 良一, 管野 誠二, 大崎 朋子: 本 とか思い出した。あと1冊コンサルタント系の人の本も記憶にあるけど名前が思い出せない。
一応データはbitbucketに入れておいた。
ksksts / junk / source — bitbucket.org
流れてきた経路は 独り52-いじめられるサラリーマン の「図1 男女別平均年収の推移」が 日本人は世界で4番目に貧しい国 年収200万円以下が1000万人もいる! | 無題ブログ の643に貼られて、それを the Emitter がtumblrにポストしたのが Windsock - Online trend following - theemitter: plasticdreams: 643 名前:.. 経由でdashboardに表示されていた、という感じ(たぶん)。
おもしろいと思ったのは男女の平均年収に大きな差があるけど、男性の平均年収額を左縦軸、女性の平均年収額を右縦軸にとって、両方の縦軸の目盛り間隔を合わせていたことと1995年時のプロット位置を一致させていたことの2点。
前者は、はじめは年収400万円台と200万円台では10万円前後の増減による効用は違うだろうと思ったけど、収入が200万円でも400万円でも10万円前後の金額の捉え方は同じようなものに思えたこと(平均を個々のケースにあてはめて考えるのはおかしいけど)。
後者についてはわざわざ元データの数値を合わせないで増減額か増減率を示したほうがいいんじゃないかと思ったから。
チャートを作るためにデータを探してみる。
民間給与実態統計調査結果|長期時系列データ|国税庁 の M03.xls の「3-1」シートの「1年勤続者」-「給料・手当(1)」が同じ数値っぽい(賞与抜きの額面の金額)。
いくつか作ってみたチャートを貼ってみる。
共通なことは、横軸は1995年から2007年まで、縦軸は金額または増減率、金額の場合の単位は千円。
そのまんまな平均年収の折れ線のチャートA。
男女ともに横ばい(差は縮まらない)。
チャートAの縦軸の下限を200万円に変更したチャートB。
男女間の差額とそれが変わらないことを強調する場合はこれがいいかも。
男性の平均年収を左軸、女性の平均年数を右軸に割り当てたチャートC。
目盛り間隔はExcel2007におまかせ。左軸の5万円が右軸の4万円に対応している。微妙な差を気付かれにくくするにはこれみたいのがいいかも。
チャートCの左右の縦軸のスケールを一致させたチャートD。
流れてきた元のチャートはこんな感じ。「1995年から2000年までは男女の平均年収が約10万円増加したけど、その後、男性の平均年収は下がって1995年と同水準に戻ったね。それに対して女性の平均年収は2000年から横ばいだね。(※左右の縦軸に注意)」。
そもそも増減額を示せばいいじゃない?ということで1995年を基準とした増減額のチャートE。
増減額は分かるけど実際の年収額が分からない。「女性の平均年収は男性と比較すると堅調です」みたいなことを伝えたい場合はいいかも。
タイトルが適切じゃないけど直すのが面倒だからこのままで。
増減額じゃなくて、1995年を1.0とした場合の増減率のチャートF。
増減率なので分母の影響が出てしまう。チャートEよりも「女性の平均年収は男性と比較すると堅調です」を強く伝えられるはずなんだけど、男性の2001年からの下落具合がチャートEよりも弱くなる点に注意。
これもタイトルが適切じゃないけど直すのが面倒だからこのままで。
チャートE/Fに実際の平均年収額をラベルで付けたのがチャートH/I。
チャートGが抜けているのはただのミス、飛ばしてしまったみたい。
これもタイトルが適切じゃないけど直すのが面倒だからこのままで。あと凡例は不要だった。
独り52-いじめられるサラリーマン や 日本人は世界で4番目に貧しい国 年収200万円以下が1000万人もいる! | 無題ブログ が何を伝えようとしているのか知らないし、チャートがおかしいとか言うつもりは全くない(読んでいないし読む気もない)。
単に見せ方を変えると印象が変わりそうなデータだと思ったからやってみた。
自分なら特に強い意図がなければI/H、A/Bあたりを使うと思う。
捏造棒グラフ - Google 検索 とか Amazon.co.jp: マッキンゼー流図解の技術: ジーン ゼラズニー, 数江 良一, 管野 誠二, 大崎 朋子: 本 とか思い出した。あと1冊コンサルタント系の人の本も記憶にあるけど名前が思い出せない。
一応データはbitbucketに入れておいた。
ksksts / junk / source — bitbucket.org
2009/10/09
リクナビNEXTの求人件数の推移
毎水曜日に受信するリクナビNEXTからのメールの新着求人数を集計してみた。
半分は興味でもう半分はPythonの練習。
メールを受信しているのはGmailアカウントだけどそこからfetchしてとかまでは面倒くさいから、Beckyで受信&保存されたメールをmbox形式でエクスポートしてそれを処理するというやり方。
Beckyからメールを抽出する時の条件は、検索文字列「すべての新着・更新求人: 件の求人が今週掲載開始。 ▼新着・更新求人:」の「いずれかを含む」で。
エクスポートしたmbox形式のファイルをPythonのmailbox.UnixMailboxでparseして、Messageオブジェクトのdateヘッダの値とpayloadに含まれる新着&更新件数を正規表現で取得してタブ区切りテキストで出力。
それをExcelで開いて移動平均を追加してチャートを作るとこんな感じ。
2007年から減少が始まって2008年以降はペースがちょっと加速して最近底を打ったように見える。まあ底を打ったらすぐに前と同じ水準に反発するわけじゃないし、雇用関係は遅行指標だし、しばらくはこのあたりの水準なのかな。
PythonのスクリプトとExcelブックファイルはBitBucketに入れておいた。
ksksts / junk / source — bitbucket.org
半分は興味でもう半分はPythonの練習。
メールを受信しているのはGmailアカウントだけどそこからfetchしてとかまでは面倒くさいから、Beckyで受信&保存されたメールをmbox形式でエクスポートしてそれを処理するというやり方。
Beckyからメールを抽出する時の条件は、検索文字列「すべての新着・更新求人: 件の求人が今週掲載開始。 ▼新着・更新求人:」の「いずれかを含む」で。
エクスポートしたmbox形式のファイルをPythonのmailbox.UnixMailboxでparseして、Messageオブジェクトのdateヘッダの値とpayloadに含まれる新着&更新件数を正規表現で取得してタブ区切りテキストで出力。
それをExcelで開いて移動平均を追加してチャートを作るとこんな感じ。
2007年から減少が始まって2008年以降はペースがちょっと加速して最近底を打ったように見える。まあ底を打ったらすぐに前と同じ水準に反発するわけじゃないし、雇用関係は遅行指標だし、しばらくはこのあたりの水準なのかな。
PythonのスクリプトとExcelブックファイルはBitBucketに入れておいた。
ksksts / junk / source — bitbucket.org
2009/09/26
Abbreviation Scoring(LiquidMetal)をC#で書いてみた
steps to phantasien(2009-09-12)を読んでおもしろそうだったからC#で書いてみた。
といってもObjective-CもRubyも知らない(読めない)ので元のNSString_BLTRExtensions.mのJavaScript版であるLiquidMetalをC#で書き直しただけ。誰でもできる簡単なお仕事。
ソースコードはBitBucketのリポジトリksksts / junk / source — bitbucket.orgに。
あとサンプル(デモ)をよくわかんないけどSilverlightで書いてみた。
StringsとAbbreviationの変更に応じてインクリメンタルにスコアを再計算してResultsを更新するだけ。
->AbbreviationScoreSample
こんなのJavaScriptでもできるからわざわざSilverlightでする意味もないけど、C#で書いたものを簡単に動かして見せることができるのは便利かも、という感想。
ただ一般のクラスライブラリとSilverlightのクラスライブラリのプロジェクトが別っぽいのが面倒。両方で使いたい場合はどうすればいいんだろう?ソースは共通でプロジェクトを別に(2つ)作るのかな。
といってもObjective-CもRubyも知らない(読めない)ので元のNSString_BLTRExtensions.mのJavaScript版であるLiquidMetalをC#で書き直しただけ。誰でもできる簡単なお仕事。
ソースコードはBitBucketのリポジトリksksts / junk / source — bitbucket.orgに。
あとサンプル(デモ)をよくわかんないけどSilverlightで書いてみた。
StringsとAbbreviationの変更に応じてインクリメンタルにスコアを再計算してResultsを更新するだけ。
->AbbreviationScoreSample
こんなのJavaScriptでもできるからわざわざSilverlightでする意味もないけど、C#で書いたものを簡単に動かして見せることができるのは便利かも、という感想。
ただ一般のクラスライブラリとSilverlightのクラスライブラリのプロジェクトが別っぽいのが面倒。両方で使いたい場合はどうすればいいんだろう?ソースは共通でプロジェクトを別に(2つ)作るのかな。
2009/09/15
リモートデスクトップ接続先PCの設定
ソフトの試用とかテストとか特定の処理用にVMware Serverで動かしっぱなしにしているWindows XP仮想マシン用。
色数以外はローカルセキュリティポリシーでも設定できるけど、グループポリシー(gpedit.msc) でまとめて設定する。
ブルートフォースアタックをされてもイヤなので一応アカウントロックを設定しておく。
少なくともログオン失敗(または成功も)を記録するように監査ポリシーを設定。
administratorsグループ以外のユーザーアカウントでも強制シャットダウンを可能にしておく。スタートメニューにシャットダウンメニューを追加する設定があったかもしれないけどどうでもいい。shutdownコマンドのオプションは単純だしシャットダウンする機会も少ないから。
スクリーンショットを32bitカラーで撮りたいときがあるから「色の解像度の制限」は「クライアント互換」(クライアントで指定した色数を使用する)に設定する。WindowsXPの場合はデフォルトで16bitカラー上限に制限されている(「ターミナルサービス」が存在しない場合は「管理用テンプレート」を選択してメニュー「操作」-「テンプレートの追加と削除」からsystem.admを追加する)。
色数以外はローカルセキュリティポリシーでも設定できるけど、グループポリシー(gpedit.msc) でまとめて設定する。
ブルートフォースアタックをされてもイヤなので一応アカウントロックを設定しておく。
少なくともログオン失敗(または成功も)を記録するように監査ポリシーを設定。
administratorsグループ以外のユーザーアカウントでも強制シャットダウンを可能にしておく。スタートメニューにシャットダウンメニューを追加する設定があったかもしれないけどどうでもいい。shutdownコマンドのオプションは単純だしシャットダウンする機会も少ないから。
スクリーンショットを32bitカラーで撮りたいときがあるから「色の解像度の制限」は「クライアント互換」(クライアントで指定した色数を使用する)に設定する。WindowsXPの場合はデフォルトで16bitカラー上限に制限されている(「ターミナルサービス」が存在しない場合は「管理用テンプレート」を選択してメニュー「操作」-「テンプレートの追加と削除」からsystem.admを追加する)。
2009/09/09
ニューズウィーク日本版(newsweekjapan.jp)のリンクを書き換えるGreasemonkeyスクリプトを書いてみた
ニュースウィーク日本版のトップページなどからの記事へのリンクを書き換えるGreasemonkeysスクリプトを書いてみた。
分量がちょうど良い感じで最近ちょくちょく読んでいるんだけど、記事へのリンクがonclickイベントハンドラでcommon.jsのclickLink関数を呼び出すようになっているのが気に入らなかった。これだとCtrl+左クリックで現在のタブでも記事へのリンクが開かれてしまって、見出しを眺めながら読みたい記事を別のタブに開いてからまとめて読むということができないから。
元のニューズウィーク日本版のページでは次の画像の枠内でクリックするとjavascriptで書かれたclickLink関数で記事のページが開くようになっている。
これを、onclickイベントハンドラでclickLink関数を呼び出さないようにして、見出しやその画像に記事へのリンク(HTMLのa要素)を付加するようにした。
次の画像の枠内がリンク適用箇所。
これでCtrl+左クリックで新しいタブに記事のページを開けるようになった。
ただし特集のページは調べるのが面倒になったので放置。
ソースはksksts / junk / source — bitbucket.org。インストールする場合はrawから。
cho45さんの$X関数(Nov 17 2007 :: New version of $X / nulog, NULL::something : out of the washer)と$N関数(まるごとJavaScript & Ajax ! Vol.1)を使っています。
分量がちょうど良い感じで最近ちょくちょく読んでいるんだけど、記事へのリンクがonclickイベントハンドラでcommon.jsのclickLink関数を呼び出すようになっているのが気に入らなかった。これだとCtrl+左クリックで現在のタブでも記事へのリンクが開かれてしまって、見出しを眺めながら読みたい記事を別のタブに開いてからまとめて読むということができないから。
元のニューズウィーク日本版のページでは次の画像の枠内でクリックするとjavascriptで書かれたclickLink関数で記事のページが開くようになっている。
これを、onclickイベントハンドラでclickLink関数を呼び出さないようにして、見出しやその画像に記事へのリンク(HTMLのa要素)を付加するようにした。
次の画像の枠内がリンク適用箇所。
これでCtrl+左クリックで新しいタブに記事のページを開けるようになった。
ただし特集のページは調べるのが面倒になったので放置。
ソースはksksts / junk / source — bitbucket.org。インストールする場合はrawから。
cho45さんの$X関数(Nov 17 2007 :: New version of $X / nulog, NULL::something : out of the washer)と$N関数(まるごとJavaScript & Ajax ! Vol.1)を使っています。
登録:
投稿 (Atom)