argparseで引数の個数を指定する

Pythonの標準ライブラリargparseで、nargsを使うと、オプションがとる引数の個数を指定できます。例えば、

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--arg1", nargs=3)
args = parser.parse_args()

if args.arg1:
    print(args.arg1)

とすると、オプションarg1はちょうど3個の引数を要求することを意味します。オプションの使用時には、

$ python prog1.py --arg1 a b c

と書けます。このとき、args.arg1には['a', 'b', 'c']が入ります。

同様に、

  • nargs='?':0個または1個の引数を要求
  • nargs='*':0個以上の引数を要求
  • nargs='+':1個以上の引数を要求

という意味になります。

Visual C++にて、CPUにAVX命令があるかどうかを実行中に知る方法

表題のことは、__cpuid, __cpuidexにあるサンプルコードで実現できます。Visual Studio 2017で確認しました。

自分の初代Core i7では、下記のようにAVXに対応していないことがわかります。

GenuineIntel
Intel(R) Core(TM) i7 CPU         870  @ 2.93GHz
3DNOW not supported
3DNOWEXT not supported
ABM not supported
ADX not supported
AES not supported
AVX not supported
AVX2 not supported
AVX512CD not supported
AVX512ER not supported
AVX512F not supported
AVX512PF not supported
BMI1 not supported
BMI2 not supported
CLFSH supported
CMPXCHG16B supported
CX8 supported
ERMS not supported
F16C not supported
FMA not supported
FSGSBASE not supported
FXSR supported
HLE not supported
INVPCID not supported
LAHF supported
LZCNT not supported
MMX supported
MMXEXT not supported
MONITOR supported
MOVBE not supported
MSR supported
OSXSAVE not supported
PCLMULQDQ not supported
POPCNT supported
PREFETCHWT1 not supported
RDRAND not supported
RDSEED not supported
RDTSCP supported
RTM not supported
SEP supported
SHA not supported
SSE supported
SSE2 supported
SSE3 supported
SSE4.1 supported
SSE4.2 supported
SSE4a not supported
SSSE3 supported
SYSCALL not supported
TBM not supported
XOP not supported
XSAVE not supported

g++によるhello world再訪

g++でhello.cppから実行ファイルを生成するときに何が起こっているかを調べてまとめました。全体的に gcc Compilation Process and Steps of C Program in Linux を参考にしています。元記事はCファイルを対象にしていますが、本記事ではC++ファイルを対象としています。

ソースコード

以下のコード hello.cpp をステップバイステップでビルドしていきます。このコードはC++11相当のコードです。

#include <iostream>
#include <vector>
#define MESSAGE "hello  "

int main(void)
{
    std::vector<int> vec{1,2,3,4,5};
    for(auto e: vec) {
        std::cout << MESSAGE << __FILE__ << " " << e << std::endl;
    }
    
    return 0;
}

1. プリプロセッシング

プリプロセッシングの結果を見る

まず最初に、プリプロセッシングと呼ばれる前処理が行われます。具体的には、

  • #includeでインクルードしたヘッダファイルの展開
  • #defineで定義した文字列の置換処理
  • __FILE____LINE__などの記号定数の置換処理

が行われます。(参考:マクロ展開の例

$ cpp -x c++ -std=c++11 hello.cpp > hello.i

または

$ g++ -E -std=c++11 hello.cpp > hello.i

で、プロプロセッシング後のファイルをhello.iという名前で保存できます。

hello.iの抜粋を以下に示します。

# 1 "hello.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.cpp"
# 1 "/usr/include/c++/5/iostream" 1 3
# 36 "/usr/include/c++/5/iostream" 3
...(中略)...
# 5 "hello.cpp"
int main(void)
{
    std::vector<int> vec{1,2,3,4,5};
    for(auto e: vec) {
        std::cout << "hello  " << "hello.cpp" << " " << e << std::endl;
    }

    return 0;
}

ヘッダファイルが検索されるディレクトリの場所を調べる

ヘッダファイルが捜索されるディレクトリの場所は、Cの場合は

$ gcc -xc -E -v -

C++の場合は

$ gcc -xc++ -E -v -

で調べることができます(参考:c++ - What are the GCC default include directories? - Stack Overflow)。私の場合は以下のように表示されました。

()
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/5
 /usr/include/x86_64-linux-gnu/c++/5
 /usr/include/c++/5/backward
 /usr/lib/gcc/x86_64-linux-gnu/5/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

includeされるヘッダファイルのフルパスを調べる

ヘッダファイルは芋づる式にincludeされていくわけですが、具体的にどのパスにあるどのヘッダファイルがincludeされたかは、

$ g++ -std=c++11 -H hello.cpp

で調べることができます(参考:c++ - How to tell where a header file is included from? - Stack Overflow)。私の環境だと以下のようにツリーが表示されました。

$ g++ -H hello.cpp       
. /usr/include/c++/5/iostream
.. /usr/include/x86_64-linux-gnu/c++/5/bits/c++config.h
... /usr/include/x86_64-linux-gnu/c++/5/bits/os_defines.h
.... /usr/include/features.h
..... /usr/include/x86_64-linux-gnu/sys/cdefs.h
...... /usr/include/x86_64-linux-gnu/bits/wordsize.h
..... /usr/include/x86_64-linux-gnu/gnu/stubs.h
...... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
...(略)...

2. アセンブリコードへの変換

次に、コンパイラは、hello.iアセンブリコードに変換します。

$ g++ -std=c++11 -S hello.i

とすると、アセンブリコードhello.sが生成されます。

        .file   "hello.cpp"
        .section        .rodata
        .type   _ZStL19piecewise_construct, @object
        .size   _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
        .zero   1
        .local  _ZStL8__ioinit
        .comm   _ZStL8__ioinit,1,1
...(略)...

3. オブジェクトファイルへの変換

次に、asコマンドを使って、アセンブリコードhello.sからマシン語に変換し、オブジェクトファイルhello.oを得ます。

$ as hello.s -o hello.o

hello.oの中身は

$ objdump -d hello.o

で調べることができます。以下は実行例です。

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   41 55                   push   %r13
   6:   41 54                   push   %r12
   8:   53                      push   %rbx
   9:   48 83 ec 58             sub    $0x58,%rsp
   d:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
...(略)...

4. リンク

実行ファイルを生成する

最後に、さっき生成したオブジェクトファイルhello.oおよび、他に必要なオブジェクトファイルを集めてリンクし、実行ファイルを生成します。

$ g++ hello.o -o hello

とすると、実行ファイルhelloが生成されます。

ライブラリが検索されるディレクトリの場所、リンクされるライブラリのフルパスを調べる

$ g++ -v hello.o -o hello

とすると、以下のように詳細情報が表示されます。

LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/

は、ライブラリが検索されるディレクトリの場所を表していると思います。

また、

COLLECT_GCC_OPTIONS='-v' '-o' 'hello' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccvdOxv1.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. hello.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

は、リンク時に使われたコマンドの詳細を表していると思います。collect2とはgcc/g++が内部で呼び出すリンクのためのコマンドのようです。ldとの違いはよくわかりません…。

その他の情報

$ g++ -save-temps -std=c++11 hello.cpp

とすると、上記でステップバイステップで生成した中間ファイル hello.ii, hello.s, hello.o が一気に生成されます。

Ubuntuの「壊れた変更禁止パッケージがあります」エラーにaptitudeで対処する方法

Ubuntu 16.04にて、少し前からapt-getで新しいパッケージを入れようとすると「壊れた変更禁止パッケージがあります」エラーが出てインストールできない事象に悩まされていました。

$ sudo apt-get install libgfortran3  
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
インストールすることができないパッケージがありました。おそらく、あり得
ない状況を要求したか、(不安定版ディストリビューションを使用しているの
であれば) 必要なパッケージがまだ作成されていなかったり Incoming から移
動されていないことが考えられます。
以下の情報がこの問題を解決するために役立つかもしれません:

以下のパッケージには満たせない依存関係があります:
 libgfortran3 : 依存: gcc-5-base (= 5.3.1-14ubuntu2) しかし、5.4.0-6ubuntu1~16.04.4 はインストールされようとしています
E: 問題を解決することができません。壊れた変更禁止パッケージがあります。

aptitudeを使ってこの問題への対応を試みました(参考:apt - E: Unable to correct problems, you have held broken packages - Ask Ubuntu)。以下にその方法について記します。自分でもこれが正しい方法なのかよくわかっていません。試す場合は自己責任でお願いします。

$ sudo aptitude install libgfortran3

とすると、以下のように、壊れたパッケージを修正するための改善案が表示されます。改善案を受け入れるならY、他の改善案を探すならnです。

以下の新規パッケージがインストールされます:
  libgfortran3{b} 
0 個のパッケージを更新、 1 個を新たにインストール、 0 個を削除予定、0 個が更新されていない。
260 k バイトのアーカイブを取得する必要があります。 展開後に 1,290 k バイトのディスク領域が新たに消費されます。
以下のパッケージには満たされていない依存関係があります:
 libgfortran3 : 依存: gcc-5-base (= 5.3.1-14ubuntu2) [5.4.0-6ubuntu1~16.04.4 が既にインストール済みです]
以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージを現在のバージョンに一時固定する:
1)     libgfortran3 [インストールされていません]      



この解決方法を受け入れますか? [Y/n/q/?]

私の場合、Yとしてもパッケージをインストールできなかったので、nで次の改善案を探しました。その結果が以下です。

以下のアクションでこれらの依存関係の問題は解決されます:

      以下のパッケージを削除する:                                              
1)      build-essential                                                        
2)      g++                                                                    
3)      g++-5                                                                  
4)      gcc                                                                    
5)      gcc-5                                                                  
6)      libasan2                                                               
7)      libatomic1                                                             
8)      libcilkrts5                                                            
9)      libgcc-5-dev                                                           
10)     libitm1                                                                
11)     liblsan0                                                               
12)     libmpx0                                                                
13)     libstdc++-5-dev                                                        
14)     libtsan0                                                               
15)     libubsan0                                                              

      以下のパッケージをインストールする:                                      
16)     tcc [0.9.27~git20151227.933c223-1 (xenial)]                            

      以下のパッケージをダウングレードする:                                    
17)     cpp-5 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]       
18)     gcc-5-base [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]  
19)     libcc1-0 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]    
20)     libgomp1 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]    
21)     libquadmath0 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]
22)     libstdc++6 [5.4.0-6ubuntu1~16.04.4 (now) -> 5.3.1-14ubuntu2 (xenial)]  

      以下の依存関係を未解決のままにする:                                      
23)     codeblocks が gcc | g++ を推奨                                         
24)     dpkg-dev が build-essential を推奨                                     
25)     cmake が gcc を推奨                                                    


この解決方法を受け入れますか? [Y/n/q/?] 

ここでYとすると、無事所望のパッケージをインストールできました。しかし、今度はgccとg++がアンインストールされ、利用できなくなってしまいました。

sudo aptitude install g++

以下の新規パッケージがインストールされます:
  g++ g++-5{a} gcc{a} gcc-5{a} libasan2{a} libatomic1{a} libc-dev-bin{a} 
  libc6-dev{ab} libcilkrts5{a} libgcc-5-dev{a} libitm1{a} liblsan0{a} 
  libmpx0{a} libstdc++-5-dev{a} libtsan0{a} libubsan0{a} linux-libc-dev{a} 
  manpages-dev{a} 
0 個のパッケージを更新、 18 個を新たにインストール、 0 個を削除予定、0 個が更新されていない。
26.5 M バイトのアーカイブを取得する必要があります。 展開後に 101 M バイトのディスク領域が新たに消費されます。
以下のパッケージには満たされていない依存関係があります:
 libc6-dev : 依存: libc6 (= 2.23-0ubuntu3) [2.23-0ubuntu7 が既にインストール済みです]
以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージを現在のバージョンに一時固定する:    
1)     g++ [インストールされていません]                   
2)     g++-5 [インストールされていません]                 
3)     libc6-dev [インストールされていません]             
4)     libstdc++-5-dev [インストールされていません]       

     以下の依存関係を未解決のままにする:                  
5)     gcc が libc6-dev | libc-dev を推奨                 
6)     gcc-5 が libc6-dev (>= 2.13-0ubuntu6) を推奨       
7)     libgcc-5-dev が libc6-dev (>= 2.13-0ubuntu6) を推奨

Yで答えましたが、まだgccもg++も利用できません。再びsudo aptitude install g++で、いちどnしたあとの

以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージをダウングレードする:                      
1)     libc6 [2.23-0ubuntu7 (now) -> 2.23-0ubuntu3 (xenial)]    
2)     libc6-dbg [2.23-0ubuntu7 (now) -> 2.23-0ubuntu3 (xenial)]

Yで受け入れると、gccとg++が使えるようになりました。

apt-get updateで 「ターゲット ○○ は 複数回設定されています」というエラーに対応する

Ubuntu 16.04でsudo apt-get updateすると、以下のようなエラーが出ました。

W: ターゲット Packages (apps/binary-amd64/Packages) は /etc/apt/sources.list.d/getdeb.list:1 と /etc/apt/sources.list.d/getdeb.list:2 で複数回設定されています
W: ターゲット Packages (apps/binary-i386/Packages) は /etc/apt/sources.list.d/getdeb.list:1 と /etc/apt/sources.list.d/getdeb.list:2 で複数回設定されています
…
W: ターゲット DEP-11 (main/dep11/Components-amd64.yml) は /etc/apt/sources.list.d/google-chrome.list:3 と /etc/apt/sources.list.d/google.list:1 で複数回設定されています
W: ターゲット DEP-11-icons (main/dep11/icons-64x64.tar) は /etc/apt/sources.list.d/google-chrome.list:3 と /etc/apt/sources.list.d/google.list:1 で複数回設定されています

apt - How can I automatically fix W: Target Packages ... is configured multiple times? - Ask Ubuntu にあるとおりPythonスクリプトをダウンロードして実行すると、

$ sudo ./apt-remove-duplicate-source-entries.py 
Overlapping source entries:
  1. /etc/apt/sources.list.d/getdeb.list: deb http://archive.getdeb.net/ubuntu yakkety-getdeb apps
  2. /etc/apt/sources.list.d/getdeb.list: deb http://archive.getdeb.net/ubuntu yakkety-getdeb apps
I disabled the latter entry.

Overlapping source entries:
  1. /etc/apt/sources.list.d/google.list: deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main
  2. /etc/apt/sources.list.d/google-chrome.list: deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main
I disabled the latter entry.


2 source entries were disabled:
  # deb http://archive.getdeb.net/ubuntu yakkety-getdeb apps
  # deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main

Do you want to save these changes? (y/N)

と出たので、yとするとエラーが出なくなりました。実のところ、何が起こっているのかよくわかっていません。試す場合は自己責任でお願いします。

Fluent PythonはPython中級者にお勧めの名著

少し前のことですが、一年以上かかってのろのろ読んでいたFluent Python - O'Reilly Mediaをようやく読み終わりました。792ページもの厚さにもかかわらず密度が非常に濃く、どのページにも発見がある名著だと思います。オライリー公式での高評価も頷けます(今はなぜかレビューページがなくなっているようですが)。

本の特徴

本が執筆された当時のPython 3.4を対象に執筆されていますが、asyncioなど一部Python 3.5相当の機能についての言及もあります。また、Python 2.7との比較も要所でなされています。

この本の特徴は、Pythonicな書き方とは何か、なぜPythonの言語仕様がこう決められたのか、という疑問を、数多くの資料を援用して解き明かしていることだと思います。資料には、書籍のみならず、PEPsや、Stack Overflow、PyConのチュートリアル資料などのWeb上のリソースが数多く含まれます。いかにPythonが多くの議論のもと進化してきたかがよく分かるとともに、著者の博識さにも驚かされます。リンクが多いので電子書籍のほうが紙より楽かもしれません。

また、著者であるLuciano RamalhoはPythonの講師の経験も豊富とのことで、説明もわかりやすいと感じました。非ネイティブには辛い難解な言い回しやユーモアなどもなく、素直で読みやすい英語だと思います。

誰向けの本か

初心者向けの本ではなく、仕事や趣味ですでにある程度Pythonを使っている人向けです。Pythonicなコードとは何かを知りたい人におすすめできます。

勉強になった箇所

どの章も濃密ですが、以下は、特にこの本でしか読めない情報があると私が感じた箇所です。

ユニコード関係

第4章 “Text versus Bytes"は、ユニコードを中心としたテキスト処理に関する話題がよくまとまっていて、Pythonを使う人でなくても読んでは損はない内容だと思います。

自然言語処理系の人なら、テキストの前処理に使える情報を見つけられるかもしれません。例えばunicode.normalize('NFKC', ½)と呼ぶことで½1/2に置換できることなどが紹介されています。

並行処理関係

第17章 “Concurrency with Futures”, 第18章 “Concurrency with asyncio"と、かなりの部分が並行処理の説明に割かれています。特にasyncioはあまり解説がWebにないような気がするので、貴重な情報源だと思います。ただ、告白すると、このあたりはあまり消化できていない箇所です。

100 numpy exercisesの解説 76~100

100 numpy exercisesの解説 51~75 - minus9d’s diary の続きです。引き続き、 numpy-100/100 Numpy exercises.ipynb at master · rougier/numpy-100 · GitHub を片手にご覧ください。

76. Consider a one-dimensional array Z, build a two-dimensional array whose first row is (Z[0],Z[1],Z[2]) and each subsequent row is shifted by 1 (last row should be (Z[-3],Z[-2],Z[-1])

例えばZ = np.array([0, 1, …, 9])のとき、以下のような行列を作るのが目標です。

0 1 2
1 2 3
2 3 4
...
7 8 9

この回答例を理解するためには、strideという概念を理解する必要があります。

stridesとは、「Pythonによるデータ分析入門」を読む NumPy編 - 車輪を再発明 によると、"「『隣』の要素にアクセスするために、ポインタをいくら動かせばいいか」を表す数値"です。例えば、ある3x4行列のstrideをnp.arange(12).reshape(3,4).stridesというコードで調べると、(32,8)とわかります。8の意味は、「ポインタを一個右に動かすためには8バイト移動する」という意味で、32の意味は、「ポインタを一個下に動かすためには32バイト移動する」という意味です。np.arange(12).reshape(3,4)で生成した行列のdtypeはint64なので、計算が合っています。

回答例で使われているstride_tricks.as_stridedは、stridesの値を任意に指定することで、ポインタの進め方を偽装する関数です。回答例を以下に示します。

def rolling(a, window):
    shape = (a.size - window + 1, window)  # この場合 (8, 3)
    strides = (a.itemsize, a.itemsize)     # この場合 (8, 8)
    return stride_tricks.as_strided(a, shape=shape, strides=strides)  # (a)
Z = rolling(np.arange(10), 3)

式(a)は、「shapeが(10,)であるndarray aを、stridesを(8, 8)と偽装して、shapeが(8, 3)になるように舐めていく」という意味になります。stridesが(8, 8)ということは、「ポインタを一個右に動かすにも、ポインタを一個下に動かすにも、8バイト移動する」ということになります。

79. Consider 2 sets of points P0,P1 describing lines (2d) and a point p, how to compute distance from p to each line i (P0[i],P1[i])?

P0とP1とを結ぶ直線と、点pとの距離を求める問題です。想定解では10組分のP0, P1を考えているのでややこしいコードになっていますが、解法自体は高校数学の範囲です。

はてなブログで数式を綴るのが苦痛なので、ここからは画像で。

80. Consider an arbitrary array, write a function that extract a subpart with a fixed shape and centered on a given element (pad with a fill value when necessary)

理解できていません…。

81. Consider an array Z = [1,2,3,4,5,6,7,8,9,10,11,12,13,14], how to generate an array R = [[1,2,3,4], [2,3,4,5], [3,4,5,6], …, [11,12,13,14]]?

なぜかQ76と実質問題です。

82. Compute a matrix rank

matrix_rank()という関数が提供されている (numpy.linalg.matrix_rank — NumPy v1.13.dev0 Manual ) のでこれを使うのが良さそうです。

84. Extract all the contiguous 3x3 blocks from a random 10x10 matrix

Q76, Q81の応用編です。

86. Consider a set of p matrices wich shape (n,n) and a set of p vectors with shape (n,1). How to compute the sum of of the p matrix products at once? (result has shape (n,1))

テンソル積を行う関数tensordot()を用いて、pnnのテンソルと、pn1のテンソルの積をとり、n*1の結果をえる問題です。tensordot()についてはpython - Understanding tensordot - Stack Overflowを見るとよくイメージがわくと思います。

88. How to implement the Game of Life using numpy arrays?

ライフゲームの実装。各セルについて隣り合う8セルの状況を調べるのに、8通りにずらしたarrayを足し合わせる技に感心しました。

89. How to get the n largest values of an array

np.argsort(arr)は、配列arrを昇順にソートしたときの、配列のインデックスを返します。

Z = [9, 12, 16, 10, 14, 1, 4, 18, 13, 5]
print(np.argsort(Z))  # "[5 6 9 0 3 1 8 4 2 7]" とprintされる

np.argpartition(arr, idx)は、配列arrのうち、値が小さいものidx + 1個分のインデックスを返します。

Z = [9, 12, 16, 10, 14, 1, 4, 18, 13, 5]
print(np.argpartition(Z, 3))  # "[5 9 6 0 1 3 2 7 8 4] とprintされる

この場合、[5 9 6 0]までが、もっとも小さい要素ベスト4に対応するインデックスです。必ずしも小さい順になっている保証はないことに注意です。また、以降の[1 3 2 7 8 4]は何の意味もない値のはずです。

90. Given an arbitrary number of vectors, build the cartesian product (every combinations of every item)

デカルト積(=直積)を実装する問題です。pythonの標準ライブラリだと

list(itertools.product([1, 2, 3], [4, 5], [6, 7]))

というコードで直積を実現できますが、これをnumpyで実装しようという問題です。

回答例は理解できていません…。

92. Consider a large vector Z, compute Z to the power of 3 using 3 different methods

自分の手元では以下の結果でした。Q69に続き、np.einsum()は高速です。

1 loop, best of 3: 2.68 s per loop
1 loop, best of 3: 452 ms per loop
1 loop, best of 3: 299 ms per loop

93. Consider two arrays A and B of shape (8,3) and (2,2). How to find rows of A that contain elements of each row of B regardless of the order of the elements in B?

Aを構成する各行のうち、「Bを構成するどの行とも、共通する要素が存在する」ような行を探すという問題です。 一見してわかりにくい問題なので、例を見ます。

np.random.seed(0)
A = np.random.randint(0,5,(8,3))
B = np.random.randint(0,5,(2,2))

で生成されるのは

A
[[4 0 3]
 [3 3 1]
 [3 2 4]
 [0 0 4]
 [2 1 0]
 [1 1 0]
 [1 4 3]
 [0 3 0]]
B
[[2 3]
 [0 1]]

です。Aの最初の行[4 0 3]は、3がBの0行目に、1がBの1行目に存在するので、題意を満たします。一方、Aの3行目[3 2 4]は、Bの1行目の要素を一つも持たないため、題意を満たしません。

回答例をうまく説明することが難しいので、中間値を示すことでお茶を濁します。

C = A[..., np.newaxis, np.newaxis] == B

により、8x3x3x3のTrue, False表を作成します。さらに

C.any((3,1))

として

[[ True  True]
 [ True  True]
 [ True False]
 [False  True]
 [ True  True]
 [False  True]
 [ True  True]
 [ True  True]]
C.any((3,1)).all()

として

[ True  True False False  True False  True  True]

となります。

96. Given a two dimensional array, how to extract unique rows?

理解できていません…。

98. Considering a path described by two vectors (X,Y), how to sample it using equidistant samples (★★★)?

ある軌跡の座標列がベクトルX, Yで与えられるとき、この軌跡をリサンプルして補間する?という問題のようです。要再読

99. Given an integer n and a 2D array X, select from X the rows which can be interpreted as draws from a multinomial distribution with n degrees, i.e., the rows which only contain integers and which sum to n.

行列Xの各行のうち、整数のみを含み、合計がnになる行を探す問題です。M = np.logical_and.reduce(np.mod(X, 1) == 0, axis=-1)が全要素が整数であるという条件を、(X.sum(axis=-1) == n)が合計nであるという条件を表しています。

100. Compute bootstrapped 95% confidence intervals for the mean of a 1D array X (i.e., resample the elements of an array with replacement N times, compute the mean of each sample, and then compute percentiles over the means).

bootstrapとは、与えられた一つのサンプルから、シミュレーションにより統計値を求める方法です。

  • X = np.random.randn(100)
    • 長さ100のサンプルが一つで与えられます。
  • N = 1000; idx = np.random.randint(0, X.size, (N, X.size))
    • 1000回、復元抽出ありで、100個の要素を取り出すシミュレーションを実施します。
  • means = X[idx].mean(axis=1)
    • 1000回のシミュレーションのそれぞれで取り出した要素の平均値をとります
  • confint = np.percentile(means, [2.5, 97.5])
    • 1000個分の平均値をソートして、中央の95%の範囲をとったものが、95%の信頼区間になります。