20220322のPythonに関する記事は27件です。

SSE4.2: RDTSC と CRC32 の2命令だけで乱数らしきものを作る

値の生成方法 生成処理は、Intel IA32 の SSE4.2 に対応したプロセッサ命令で 生成プログラム RDTSC CRC32 EAX, EDX 二命令だけです。 EAX の値が乱数のようなものになるだろうという期待をします。 RDTSC と CRC32 の関係 CPU の CRC32 は元となる crc32 計算 eax = crc32(edx) とは少し違い eax = crc32(eax ^ edx) という計算をします。(レジスタのビット反転を含むので詳しくは仕様書を参照してください) 元の y = CRC32(x) では、複数の x に対して、y が1つ(重複)となることがありません。符号なし32ビット整数における全単射になるでしょうか。(過去記事) RDTSC は EDX:EAX にプロセッサのタイムスタンプ(分解能は CPU クロック) を 64 ビットで返します。EDX は 232 クロック置きに更新されます。4GHz 動作で約 1 秒間隔です。 これを生成プログラムのように CRC32 に渡すと、RDTSC[EDX:EAX] と CRC32 の関係は EDX EAX CRC32 00000000 00000000 00000000 00000000 00000001 dd45aab8 00000000 00000002 bf672381 00000000 00000003 62228939 ... ... ... 00000000 fffffffe 6add1e80 00000000 ffffffff b798b438 00000001 00000000 dd45aab8 00000001 00000001 00000000 00000001 00000002 62228939 00000001 00000003 bf672381 ... ... ... CRC32命令の計算 sse42crc32.py def crc32(eax, edx): eax ^= edx for _ in range(32): f = eax & 1 eax >>= 1 if not f: continue eax ^= 0x82f63b78 return eax となるので、 TSC = (EDX << 32) | EAX に対する CRC32[TSC] を乱数列(質に疑問符あり)と捉えます。 生成プログラムは、呼び出された時点の RDTSC(時間とともに増える)から CRC32[TSC] で引き出すことになります。結果、乱数列臭いものが出てくることは予想できますが、実行状況によっては乱数列とよべないものが並ぶことも考えられます。 Python モジュール spcrand 実際は、どんな数列になるかは実行してみなければ分からないので Python モジュールにしてみました。 メソッドは1つです spcrand() -> int: 符号なし整数(32ビット)で生成プログラム一回分 spcrand(count: int) -> bytearray: 生成プログラム count 回分で count * 4 バイトの数列 動作確認している環境 OS   macOS 12.2.1 C コンパイラ (Xcode 13.2.1)   Apple clang version 13.0.0 (clang-1300.0.29.30)   Target: x86_64-apple-darwin21.3.0 Python : /usr/bin/python3   Python 3.8.9 (default, Oct 26 2021, 07:25:54)   [Clang 13.0.0 (clang-1300.0.29.30)] on darwin spcrand.c #ifndef PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN #endif /* PY_SSIZE_T_CLEAN */ #include <Python.h> #if (defined(i386) || \ defined(__i386) || \ defined(__i386__) || \ defined(__i486__) || \ defined(__i586__) || \ defined(__i686__) || \ defined(__amd64) || \ defined(__amd64__) || \ defined(__x86_64) || \ defined(__x86_64__) || \ 0) #define ISA_X86 1 #else #define ISA_X86 0 #endif /* * Method: spcrand */ #if ISA_X86 #define HAS_CHECK_SSE42 1 typedef struct cpuid_result { unsigned int eax; /* reg:00 */ unsigned int ecx; /* reg:01 */ unsigned int edx; /* reg:10 */ unsigned int ebx; /* reg:11 */ } cpuid_result; inline static int check_sse42() { cpuid_result res; res.eax = 1; res.ecx = 0; res.edx = 0; res.ebx = 0; __asm__ volatile ( "mov %0,%%eax;" "mov %1,%%ecx;" "xor %%edx,%%edx;" "xor %%ebx,%%ebx;" "cpuid;" "mov %%eax,%0;" "mov %%ecx,%1;" "mov %%edx,%2;" "mov %%ebx,%3;" : "+m" (res.eax), "+m" (res.ecx), "+m" (res.edx), "+m" (res.ebx) : /**/ : "eax", "ecx", "edx", "ebx"); return (res.ecx >> 20) & 1; } inline static uint32_t ezrand() { #if defined(__GNUC__) uint32_t eax, edx; __asm__ volatile ( "rdtsc\n" "crc32l\t%%edx, %%eax" : "=a" (eax), "=d" (edx)); return eax; #else /* !__GNUC__ */ #error "unknown compiler" #endif } #else /* !ISA_X86 */ #error "unknown processor" #endif static PyObject * method_spcrand(PyObject *unused, PyObject *const *args, Py_ssize_t size) { PyByteArrayObject *ba; PyObject *r = NULL; uint32_t *buffer; Py_ssize_t count; Py_ssize_t i; (void) unused; if (size == 0) return PyLong_FromUnsignedLong(ezrand()); if (size != 1) { PyErr_SetString(PyExc_TypeError, "spcrand expected at 1 argument"); return NULL; } if (!PyLong_CheckExact(args[0])) { PyErr_SetObject(PyExc_TypeError, args[0]); return NULL; } count = PyLong_AsSsize_t(args[0]); if (count < 0) { if (!PyErr_Occurred()) PyErr_SetObject(PyExc_TypeError, args[0]); return NULL; } if (!(r = PyByteArray_FromStringAndSize("", 0))) return NULL; if (PyByteArray_Resize(r, count * sizeof(uint32_t)) < 0) { Py_XDECREF(r); return NULL; } ba = (PyByteArrayObject *) r; buffer = (uint32_t *) ba->ob_start; for (i = 0; i < count; i++) buffer[i] = ezrand(); return r; } /* * Module: spcrand */ static PyMethodDef spcrand_methods[] = { {"spcrand", (PyCFunction) method_spcrand, METH_FASTCALL, NULL}, {NULL, NULL, 0, NULL}, /* end */ }; static PyModuleDef spcrand_def = { PyModuleDef_HEAD_INIT, .m_name = "spcrand", .m_doc = NULL, .m_size = -1, .m_methods = spcrand_methods, }; PyMODINIT_FUNC PyInit_spcrand(void) { #if HAS_CHECK_SSE42 if (check_sse42() < 0) return NULL; #endif /* HAS_CHECK_SSE42 */ return PyModule_Create(&spcrand_def); } setup.py #!/usr/bin/env python3 import os from distutils.core import setup, Extension def getenv(name, defval=None): if name in os.environ: return os.environ[name] return defval DEBUG = getenv('DEBUG') in ('true', 'yes') MAJOR_VERSION = 0 MINOR_VERSION = 1 DEBUG_VERSION = 0 VERSION = '%d.%d.%d' % (MAJOR_VERSION, MINOR_VERSION, DEBUG_VERSION) DEFINE_MACROS = [ ('MAJOR_VERSION', MAJOR_VERSION), ('MINOR_VERSION', MINOR_VERSION), ('DEBUG_VERSION', DEBUG_VERSION), ] UNDEF_MACROS = [] EXTRA_COMPILE_ARGS = [ '-W', '-Wall', '-Wno-invalid-offsetof', '-Wno-deprecated-declarations', ] if DEBUG: DEFINE_MACROS.append(('DEBUG', 1)) UNDEF_MACROS.append('NDEBUG') EXTRA_COMPILE_ARGS.append('-O0') pass setup(name='spcrand', version=VERSION, description='', ext_modules=[Extension( name='spcrand', define_macros=DEFINE_MACROS, undef_macros=UNDEF_MACROS, extra_compile_args=EXTRA_COMPILE_ARGS, sources=['spcrand.c'])]) setuptools を使えという警告が出るのはわかっているのですが… ビルドとテスト $ env ARCHFLAGS="-arch x86_64" python3 setup.py build running build running build_ext building 'spcrand' extension clang -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -iwithsysroot/System/Library/Frameworks/System.framework/PrivateHeaders -iwithsysroot/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/Headers -Werror=implicit-function-declaration -arch x86_64 -DMAJOR_VERSION=0 -DMINOR_VERSION=1 -DDEBUG_VERSION=0 -I/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/include/python3.8 -c spcrand.c -o build/temp.macosx-10.14-x86_64-3.8/spcrand.o -W -Wall -Wno-invalid-offsetof -Wno-deprecated-declarations creating build/lib.macosx-10.14-x86_64-3.8 clang -bundle -undefined dynamic_lookup -Wl,-headerpad,0x1000 -arch x86_64 build/temp.macosx-10.14-x86_64-3.8/spcrand.o -o build/lib.macosx-10.14-x86_64-3.8/spcrand.cpython-38-darwin.so $ $ (cd build/lib.macosx-10.14-x86_64-3.8; python3) Python 3.8.9 (default, Feb 18 2022, 07:45:34) [Clang 13.1.6 (clang-1316.0.21.2)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from spcrand import spcrand >>> hex(spcrand()) '0x1ff4677b' >>> hex(spcrand()) '0xe7806714' >>> hex(spcrand()) '0xf996f420' >>> r = spcrand(4096) >>> [r.count(i) for i in range(256)] [72, 69, 60, 75, 61, 62, 60, 57, 59, 64, 66, 51, 58, 68, 58, 68, 63, 84, 68, 73, 61, 63, 65, 60, 66, 70, 68, 66, 55, 68, 78, 63, 60, 71, 64, 64, 49, 73, 60, 61, 79, 59, 62, 71, 52, 69, 63, 67, 68, 45, 51, 69, 57, 73, 70, 45, 72, 61, 58, 75, 55, 62, 72, 62, 55, 60, 70, 73, 63, 63, 47, 63, 61, 58, 56, 72, 69, 62, 72, 75, 68, 57, 51, 66, 59, 57, 69, 57, 67, 59, 59, 68, 68, 55, 57, 64, 82, 62, 63, 70, 59, 61, 76, 60, 62, 46, 64, 64, 72, 66, 64, 57, 69, 59, 60, 67, 64, 67, 54, 68, 68, 62, 71, 68, 67, 60, 58, 53, 78, 65, 67, 65, 57, 62, 56, 48, 56, 53, 60, 59, 69, 72, 58, 61, 51, 64, 66, 66, 65, 62, 63, 53, 59, 58, 55, 65, 73, 62, 63, 72, 64, 70, 61, 84, 54, 71, 72, 76, 67, 63, 67, 55, 59, 73, 62, 61, 56, 68, 64, 63, 72, 67, 77, 63, 66, 69, 54, 60, 64, 73, 59, 67, 67, 76, 58, 74, 66, 62, 64, 61, 57, 70, 69, 61, 64, 75, 79, 77, 59, 76, 67, 51, 67, 66, 58, 64, 57, 53, 58, 63, 68, 65, 74, 52, 69, 72, 64, 59, 65, 63, 69, 59, 61, 51, 65, 88, 57, 71, 63, 57, 62, 62, 60, 61, 67, 73, 61, 66, 69, 65, 75, 65, 75, 51, 62, 74] >>> min([r.count(i) for i in range(256)]) 45 >>> max([r.count(i) for i in range(256)]) 88 >>> len(r) 16384 >>> def dump(l): ... for s in range(0, len(l), 16): ... print(' '.join(['%02x' % d for d in l[s:s+16]])) ... >>> dump(r[:256]) a2 f8 99 3c a5 4f fc ea 06 b8 db 59 b5 19 3e b1 9d fa 3c d5 59 4d 38 2f 1f 9d 7d 8c a3 0f 7e da e6 9b 1d 9f 4f 5b 78 44 27 28 bd b3 68 8b 9c 9e 5f 0d ba 1f e6 6c d9 a6 28 ec 9f 34 1f 6a b9 b5 b6 aa dc 6e de d9 19 99 10 59 5f 0b 0f 3c 7b ee 92 3e 1e 52 2e ac 1d 04 8d 5b 3a b7 c2 f8 1b 9a aa 8b de 6d b5 ee fa 88 09 7c f9 de 66 9d d5 37 4e 7e d7 53 79 f8 f1 d2 36 5b d0 ff f7 e8 70 d3 c0 6e 56 52 8f cd 77 7f f4 ac 56 35 48 3e 55 63 0d aa 36 26 f5 90 b4 68 49 02 b7 3e 35 1f f6 19 1d fc f4 7d 0d 5d f2 1f c3 dd b4 8d f4 5b 92 0c 0c 61 10 42 b0 f3 13 14 4d cd 35 8c e4 0d 50 57 79 0f 35 eb c5 9d 36 bd 5b db 75 e7 78 33 d7 b6 c4 a1 d4 e0 5e df d1 31 90 5f 97 a3 97 08 20 10 09 4e 63 4a b5 dc 60 1c 96 34 c2 4d 2a a6 c1 1b 31 fb a3 75 7e 58 82 58 38 88 c7 fb 0f 0e e1 7a >>> dump(r[256:256+256]) c1 8e a7 e8 04 05 41 4f b8 97 42 19 c4 8a 03 3e fc c8 07 38 c1 79 63 d1 84 ed 00 94 38 7f 03 c2 7c d7 82 da 41 66 e6 33 bc 58 c0 ab 7b ab e2 b7 b5 2b a4 25 82 ad 82 a4 aa 4e 80 c0 89 a6 22 91 35 34 21 c7 2a 51 05 22 7c 39 0b a9 c0 ab 08 ff e2 7f 48 f3 5e ed 4b a5 fc 26 8e 4b c1 97 ea a2 5f d1 a9 f8 fb 5a ee 26 d3 b9 ec 42 e4 3f ca c3 2a bf 8c 51 d2 85 0e 1f e5 03 28 9e aa a0 09 b3 17 f9 2c 81 ab 6b 2f d7 6f dc 2b 2d d3 4e 28 7b aa 57 cd 8a 1c f2 8c b4 d2 72 ca 26 90 9a c9 0e 5e 1a 8f 9c 69 9c a9 1d 10 85 4c ec 5f 26 6d c1 68 a0 4b 40 c1 60 2e 9b 48 92 af b6 f4 00 ac e0 eb 65 88 05 3b bc 08 2f f5 3c 4e bd d7 e8 0e b1 6b 7a 0d e7 cf f1 4a 39 51 b7 09 63 ed 25 0a 35 4f ee cf db e6 2e aa 00 d1 a8 8c 81 9e 0b ad ac a6 49 a9 aa 29 65 ca f6 66 c6 eb db b6 1f 6b f1 このときのバイト列の分布としては 16384/256=64, min=64-19, max=64+24 の範囲に収まっているようです。 参考:count 回ループ部アセンブラ出力(抜粋) C for (i = 0; i < count; i++) buffer[i] = ezrand(); コンパイル結果(抜粋) LBB1_14: rdtsc crc32l %edx, %eax movl %eax, (%rcx,%rsi,4) addq $1, %rsi cmpq %rsi, %r15 jne LBB1_14 タイトルからして、この記事は「ポンコツ」という意味を含ませています。 よって「この乱数アルゴリズムを使え」みたいなコメントは その通り過ぎて鬱陶しいだけなので一切不要です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Numpyのため趣味的にPython 3.10をmakeする(M1 Mac: arm64アークテクチャ)

はじめに  M1 Mac (mac OS Monterey) を手に入れて、デフォルトで入るPython 3.8を使ってみたらNumpyが入らない。調べてみれば、下の記事にあるように、Python 3.9以上のバージョンならばpipからNumpyはインストールできるし、新しいバージョンのPython自体もcondaやHomebrewから簡単にインストールできるとの事1。  この時点で、素直に上記の方法に従えさえすれば問題 (Python3とNumpyのインストール) は解決できます。この記事ではあえてそれを傍に置き、condaもHomebrewも使わずにソースコードからPython 3.10をmakeして、M1 Macにをインストールする方法を記します。Python 3.9などバージョンが違っていてもインストール方法はほぼ変わりません。  Python 3.10のインストールは以下の手順で行います: OpenSSLとPythonソースコードのダウンロード OpenSSLのmake Pythonのmake Pythonとpipの実行 Pathの設定 また、Mac/README.rstの邦訳を付録として付けました(本編より長いです)。一通り読むと、MacOSにPythonをインストールする際に必要となる条件の理由が理解できます、 OpenSSLはなぜ必要?  Pythonのインストールなのに、手順にOpenSSLが登場しています。なぜなら、OpenSSLはPython上でhttps通信等を行う為に必要となるからです。別にPythonでそんな処理なんてしないし...。と思って入れないと、次の最悪の事が起こります。 Pythonと一緒にインストールされるpipはhttpsプロトコルで通信を行う為、OpenSSL抜きでPythonをmakeした場合、通信が失敗してインターネット経由でモジュールを一つも追加できなくなります。 上の記事曰く、OpenSSLはOSにプリインストールされている物でも大丈夫らしいのですが、実はMacに標準でインストールされているOpenSSLの正体はLibreSSLなので、OpenSSLを使うならインストールが必要となります。HomebrewからOpenSSLもインストールできるそうなのだけど、今回は使わないのでこちらもmakeでインストールします。 OpenSSLとPythonソースコードのダウンロード  以下からソースコードをダウンロードします。この記事を執筆した2023年3月時点より新しいバージョン (場合によっては致命的なバグのアップデートバージョン) がリリースされている可能性があるので、配布元のサイトを確認する様にしてください。 Python 3.10 (https://www.python.org/downloads/source/) 最新安定版 (2023年3月): 3.10.3 (https://www.python.org/ftp/python/3.10.3/Python-3.10.3.tgz) OpenSSL (https://www.openssl.org/source/) 最新安定版 (2023年3月): 1.1.1n (https://www.openssl.org/source/openssl-1.1.1n.tar.gz) ダウンロードしたたtarballは、% tar zxvf XXX.tar.gz等で適当な所に解凍してください。 OpenSSLのmake  この手順では、OpenSSLをシステム下の/libや/optディレクトリを汚したくないため、自分のhomeディレクトリに作った"local/"ディレクトリ下にインストールしています。趣味の問題なので、特にこだわりがなければ--prefixと--openssldirは省略して、デフォルトの場所にインストールでも良いと思います。  また、OpenSSLは"./configure"ではなく"./config"を実行することに注意してください。 $ cd openssl-1.1.1n $ ./config --prefix=/Users/hoge/local/opt/openssl --openssldir=/Users/hoge/local/ssl $ make $ make test $ make install OpenSSLのバージョンは1.1.1kだとarm64に対応していないのか、makeに失敗します。1.1.1m以上ならmakeできたので、最新版を使う場合には特に問題なくmakeできるはずです。 使用したconfigの引数 引数名 説明 デフォルト値 --prefix=DIR OpenSSLの設定ファイルおよび、デフォルトの証明書やKey Storeの保存先ディレクトリ /usr/local --openssldir=DIR インストールされるディレクトリ群に対する最上位のパス /usr/local/ssl Pythonのmake  Pythonのmakeにおいてデフォルトで用いられるコンパイラはGCC (Gnu Compiler Collection) のgccとg++ですが、Apple製コンパイラを使用したいので、ここではClangを指定してコンパイルを行います。  PythonはUnix形式ではなく、フレームワーク形式でインストールを行います。フレームワーク形式の詳細は付録をご参照ください。理由を簡単に書けば、GUIを用いるコード (matplotlib等) を実行できる様にするためと、ディレクトリ管理の簡単化のためとなります。  OpenSSLをデフォルト以外の場所にインストールした場合は、"./configure"の引数"--with-openssl"に、OpenSSLのmake時に"--prefix"で指定してたディレクトリを指定します。同時に、"--with-openssl-rpath"を"auto"にします。"--with-openssl-rpath"が"no"でrpath2が指定されない場合、Python実行時に動的ライブラリがロードされる際に、システムが想定する一般的なディレクトリ (/usr/libや/usr/local/lib等) からしか目的のライブラリが探索されないため、この場合のように異なる場所にインストールしたライブラリが見つからずロードが失敗したり、場合によっては探索されたディレクトリ内に存在する異なるバージョンのライブラリがロードされて、おかしな挙動を引き起こす場合があります。 $ cd Python-3.10.3 $ ./configure --prefix=/Users/hoga/local \ --enable-framework=/Users/hoge/local/Library/Frameworks \ --enable-universalsdk --with-universal-archs=universal2 \ --with-openssl=/Users/hoge/local/opt/openssl --with-openssl-rpath=auto\ --with-ensurepip=install \ --enable-optimizations CC=clang CXX=clang $ make -j4 $ make -j4 test $ make install "./confiure"実行時に表示されるログ名の中で、以下のopenssl関係の構成が解決されていることを確認してください。 checking for openssl/ssl.h in /Users/kogata/local2/opt/openssl... yes checking for --with-builtin-hashlib-hashes... md5,sha1,sha256,sha512,sha3,blake2 解決されていない場合でも"./configure"は成功するので、気づかないままmakeした場合、インターネットからモジュールがダウンロードできないpipが出来上がり、makeのやり直しとなります。 make installに"-j"オプションを指定するとインストールに失敗します。 使用したconfigureの引数 引数名 説明 デフォルト値 --prefix=PREFIX アーキテクチャと独立したファイルを"PREFIX"にインストールする。このオプションが指定されない場合は、"/usr/local"にインストールされる。 - --enable-framework[=INSTALLDIR]  従来のUnix形式のインストールではなく、Python.frameworkを作成する。"INSTALLDIR"オプションで、Python.frameworkのインストール先を指定できる("INSTALLDIR"を指定しない場合は"/Library/Frameworks/"にインストールされる)。詳細はMac/README.rstを見よ。 no --enable-universalsdk[=SDKDIR] ユニバーサルバイナリでコンパイルされたプログラムを作成する。"SDKDIR"はビルド時にどのmac OS SDKを用いるかを指定する("SDKDIR"が指定されない場合は、現在動いているXcodeかCommand Line Toolsのdeveloperディレクトリにあるデフォルトmac OS SDKが使われる。そのため、現在のシステムで"SDKDIR"を指定する必要はまずない)。詳細はMac/README.rstを見よ。 no --with-universal-archs=ARCH どのアーキテクチャにおけるmacOS ユニバーサルバイナリが作られるかを指定する。このオプションは"--enable-universalsdk"が指定されている時のみ有効である。"ARCH"に指定できる値は次となる: "universal2", "intel-64", "intel-32", "intel", "32-bit", "64-bit", "3-way", or "all" - --with-openssl=DIR OpenSSLディレクトリのルート - --with-openssl-rpath=[DIR|auto|no] OpenSSLライブラリに対するランタイムライブラリのディレクトリ (rpath2) を指定する。指定できる値は次となり、 "no" (デフォルト): rpathを指定しない、"auto": --with-opensslで指定されたライブラリとpkg-configから、自動的にrpathを設定する、"DIR": 明示的にrpathを指定する。 no --with-ensurepip[=install|upgrade|no] 付属しているpipを用いて"install" か"upgrade"を行う。(defaultは"upgrade") - --enable-optimizations 高価かつ安定した最適化を行う(Profile-Guided Optimization等) no CC C compiler command gcc CXX C++ compiler command g++ 使用したmakeの引数 引数名 説明 デフォルト値 -j [jobの数] make時に同時に実行されるjobの数を指定する。jobの数を指定しない場合は数を制限しない。指定するジョブ数はこちらが詳しいが、どちらにせよ最大でも物理コア数程度にするのが良い。ジョブ数を指定しない場合はマシンに大きな負荷が掛かる場合があるので、jobの数は必ず指定する事。 - Pythonとpipの実行  インストールが成功しているかの確認に、ここまでの作業でインストールされたpipを使い、目的のNumpyをインストールしてみます。 $ /Users/hoge/local/bin/pip3.10 install numpy 以下の様に表示されれば成功です。PythonとOpenSSLの動的ライブラリとの連携も無事出来ています。 Collecting numpy Downloading numpy-1.22.3-cp310-cp310-macosx_11_0_arm64.whl (12.8 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.8/12.8 MB 55.4 MB/s eta 0:00:00 Installing collected packages: numpy Successfully installed numpy-1.22.3 Pythonを実行してみます。 $ /Users/hoge/local/bin/python3.10 インストールに問題なければ、インタプリタが起動されます。Numpyをインポートしたり、挙動を確認したらCtrl-Dかexit()で終了します。特にエラーや警告がでなければ、無事、M1 Mac (mac OS Monterey) 上にNumpyが動くPython環境を構築できています。 Pathの設定  ここまでで目的は達成されましたが、Python実行時に毎回/Users/~/Python3.10と打つのも面倒なので、Path環境変数を設定してコマンド名だけで実行できる様にします。自身で使用しているシェルの設定ファイル (OSデフォルトなら.zshrc) に以下を書き加えます。 Zshの場合(Bashも同じ) export PATH="/Users/hoge/local/bin:${PATH}" 設定ファイルをターミナルを再起動したり、sourceコマンドで反映させれば、以下の様にPythonが実行できる様になります。 $ python3.10 付録. Mac/README.rstの邦訳  原文はPython3.10.3ソースコード内に存在するmac/README.rstなので、本邦訳はPython Software Foundation (PSF) ライセンス下で公開されます3。この文書は、私がいちいち英語を読むのが面倒なため、私的利用を目的として訳した文書となります。内容については訳者の英語力が怪しいため、参考にする際は今一度原文をご確認ください(間違いがありましたらコメントください)。 Python on macOS README 著者: Jack Jansen (2004-07) Ronald Oussoren (2010-04) Ned Deily (2012-06) configureのmacOS用引数 --enable-framework[=DIR]  この引数が指定された場合、コンパイルされるプログラムは、従来のUnixのインストール形式ではなく、Python.frameworkとして作成される。フレームワークのより詳細な情報についてはmacOSでフレームワークベースのPythonをビルドして使用するセクションを見よ。  オプションのディレクトリ引数を指定した場合、フレームワークはそのディレクトリ内にインストールされる。これにより、自身のホームディレクトリにpythonのフレームワークをインストールする事もできる: $ ./configure --enable-framework=/Users/ronald/Library/Frameworks $ make && make install  上の場合だと、フレームワーク自体が/Users/ronald/Library/Frameworksにインストールされ、Users/ronald/Applicationsにアプリケーションが、/Users/ronald/binにコマンドラインツールがインストールされる。 --with-framework-name=NAME  Pythonフレームワークに対する名前を指定する(デフォルトはPython) 。このオプションは"--enable-framework"が指定された時のみ有効である。 --enable-universalsdk[=PATH]  Pythonをユニバーサルバイナリのプログラムとして作成する。これは通常(訳注: Unix形式)およびフレームワーク形式の両方で使用できる。    この任意の引数は、コンパイルされるプログラムが動くために、どのmacOS SDKを用いるべきかを指定する。現在のシステムでは、大抵の場合PATHを指定す必要はなく、/だけで良い。その場合、現在動いているXcodeかCommand Line ToolsのdeveloperディレクトリにあるデフォルトmacOS SDKが使われる。より詳しい情報は、macOS xcrun man pageを見よ。現行バージョンのmacOSおよびXcodeは、システムヘッダーファイルをもはやそれらの慣習的な場所 (/usr/includeおよび/System/Library/Frameworks) に置いておらず、MacOSX SDKの内部に見つけられる。Appleが提供するビルドツールはこの事を意識せずに処理するので、現行バージョンのPythonもこれで処理する様になっている。そのため、もはやこの任意の引数は必要ではなく、 macOS 10.14以降、xcode-selectでシステムヘッダーを強制的にインストールすることもできない。 --with-universal-archs=VALUE  どの種類のユニバーサルバイナルでPythonを作成するべきか指定する。このオプションは"--enable-universalsdk"が指定された場合のみ有効である。SDKがPPC (訳注: Power PC) に対応していれば、デフォルトでは32-bitとなり、そうでなければintelがデフォルトとなる。intelは32-bitと64-bit両方のユニバーサルバイナリを意味し、想定している物と違う可能性があることに注意する事:例えば、macOS 10.15 Catalina現在だと、32-bitでのバイナリの実行は、もはやオペレーティングシステムでサポートされていない。したがって、"--with-universal-archs"に対して以下の様に明示的に値を指定するか、どちらも使用しない事が最適となる。 --enable-universalsdk --with-universal-archs=intel-64 macOSでユニバーサルバイナリのPythonをビルドして使用する 1.ユニバーサルバイナリとは何か  Pythonにおけるユニバーサルバイナリでコンパイルされたプログラムは、複数のCPUアーキテクチャに対応したオブジェクトコードを含む。ユニバーサルなmacOS実行可能ファイルやライブラリは、アーキテクチャ固有のコードを一つに纏めており、それにより、サポートする全てのアーキテクチャでネイティブな速さで実行できる。ユニバーサルバイナリは、当時存在するPower PC (PPC) マシンに、IntelベースのMacに対するサポートを追加するために、macOS 10.4で導入された。macOS 10.5において、64-bit Intelと64-bit PPCアーキテクチャに拡張された。この仕組みにより、使用されているmacOSのバージョンとビルドツールによって、様々なアーキテクチャの組み合わせでPythonをビルドする事を可能である。PPCのサポートはmacOS 10.7でなくなり、32-bit Intelのサポーターはmac OS 10.15でなくなった。そのため、現行のmacOS 10.15では、実行できるアーキテクチャとして64-bit Intel (x86_64) しかサポートされていない。 2.ユニバーサルバイナリのビルド方法  configureに"--enable-universalsdkフラグを指定する事でユニバーサルバイナリを有効にできる。 $ ./configure --enable-universalsdk $ make $ make install このフラグはpythonをフレームワークとしてビルドする場合だけでなく、従来のunix形式のビルドでも使用できる。ユニバーサルバイナリはmacOS 10.4におけるXcode 2.1と10.4u SDKで最初にサポートされた。Xcode 3とmacOS 10.5から、より多くの設定が利用できる様になった。  通常、ユニバーサルビルドは、AppleのXcode開発ツール含まれている、Appleから供給されるコンパイラや他のビルドツールが提供する特定の機能に依存する。現在動いているmacOSのリリースに適したXcodeかコマンドラインツールのコンポーネントのインストールが推奨される。より詳しい情報はPython Developer's Guide (https://devguide.python.org/setup/) を見よ。 2.1 ユニバーサルバイナリのタイプ  幾つかのタイプのユニバーサルバイナリをビルドすることができ、デフォルトではPPCがサポートされるビルド環境 (macOS 10.4とXcode 2, macOS 10.5および10.6とXcode 3) なら32-bitバイナリ (i386とppc) のみ、ppcをサポートしていないビルド環境 (macOS 10.6におけるXcode 4およびそれ以降のシステム) ならIntel-32/-64-bitバイナリ (i386 and X86_64) となる。そのタイプはconfigureの"--with-universal-archs=VALUE"オプションを使用して指定できる: universal2: arm64, x86_64 intel: i386, x86_64 intel-32: i386 intel-64: x86_64 32-bit: ppc, i386 3-way: i386, x86_64, ppc 64-bit: ppc64, x86_64 all: ppc, ppc64, i386, x86_64 64-bitアーキテクチャ (ppc64、x86_64およびarm64) を含むユニバーサルバイナリをビルドするためには、macOS 10.5かそれ以降が動いてるシステムでビルドしなければならない。allおよび64-bit (ppc64, x86_64)タイプのビルドは10.5 SDKのみで可能であり、なぜならppc64のサポートがmocOS 10.5にしか含まれていない為である。macOS 10.6のXcode 3にはppcサポートが遺産として含まれていたにもかかわらず、macOS 10.6でリリースされたXcode 4では削除され、macOS 10.7ではppcがサポートされない事が標準となった。まとめると、以下のSDKとuniversal-archsのタイプの組み合わせが有効となる: 10.4u SDKおよびXcode 2は32-bitのみ 10.5 SDKおよびXcode 3.1.xは全タイプ 10.6 SDKおよびXcode 3.2.xはintel、intel-32、intel-64、3-wayおよび32-bit 10.6 SDKおよびXcode 4はintel、intel-32およびintel-64 10.7から10.14 SDKsはintel、intel-32およびintel-64 10.15およびそれ以降のSDKsはintel-64のみ 11.0およびそれ以降のSDKsはuniversal2  フレームワーク形式でビルドする場合のmakefileは、ユニバーサルアーキテクチャが少なくとも1つの32-bitアーキテクチャを含む場合、python3.x-32もインストールする(すなわち、64-bitとintel-64以外の全タイプ)。また、AppleシリコンMacのRosetta 2 Intenエミュレータで簡単に実行できる様にするため、universal2ではpython3.x-intel64バイナリもインストールしている。 アーキテクチャを指定しての実行 archコマンドを使用してアーキテクチャを指定してコードを実行できる: $ arch -i386 python マシンのハードウェアに関係なく、明示的に32-bitモードとしても実行できる: $ arch -i386 -ppc python archコマンドの使用は、そのarchコマンドで実行されたPythonから起動されるサブプロセス (プログラムやテスト) に、選択されたアーキテクチャを自動的に引き継がないため、アーキテクチャの指定を完全には解決しない。もしメインのインタプリタから起動されたサブプロセスも32-bitモードで確実に起動したい場合には、python3.x-32バイナリ上で、subprocess Popen4に対してsys.executable5を実行する引数として使用する。  同じ様に、universal2バイナリのx86_64モードを強制的に実行するには、python3.x-intel64を使用する。 macOSでフレームワークベースのPythonをビルドして使用する 1.一体なぜ通常の静的版Pythonの代わりにフレームワーク版Pythonを使用するのか?  この主な理由は、PythonでGUIプログラムを作れる様にする為である。X11/XDarwinベースのGUIを除いた全てのGUIプログラムは、macOSアプリケーションバンドル (".app")からの起動を必要とする。  フレームワークを用いない.appを作成することは技術的には可能であるが、これを本当に必要とするならば、自身で作成すべきである。  フレームワークを用いる第二の理由は、Pythonに関係するアイテムを、次の2つの場所にのみ配置するからである。その場所は"/Library/Framework/Python.framework"および "/Applications/Python <VERSION>"となり、ここで、 "<VERSION>"は"3.8"や"2.7"となる。これにより、バイナリ配布版からPythonをインストールしたユーザが、再びそれを取り除きたい場合に、処理が簡単となる。さらに、フレームワークとして稼働する方法を選ぶことにより、管理者権限のないユーザが、自身のホームディレクトリにバイナリ配布版を再コンパイルせずにインストールできる。 2.通常の静的版Pythonとフレームワーク版Pythonは何が違うのか?    格納場所が異なるだけで、普段使用する範囲においては特に違いはない。/Library/Frameworks/Python.frameworkを見れば、たくさんの相対シンボリックリンクが確認できる(詳しくは、Appleのドキュメントを見よ)。通常のUnix形式におけるPythonのファイルレイアウトに慣れているなら、Versions/Currentに行けば、おなじみのbinとlibディレクトリが確認できる。 3.追加パッケージは必要か?  十中八九、必要である。Tkinterのサポートを望むなら、macOS AquaTkディストリビューションを手に入れることが必要となり、これはmacOS 10.4以降にはデフォルトでインストールされている。macOS 10.6から提供が開始されあたCocoaベースのAquaTkは不安定であることが証明されているので気をつけること。もし可能であれば、macOS 10.6以降でビルドする前に、最新バージョンのインストールを検討するべきで、例えばActiveTcl 8.6等である。https://www.python.org/download/mac/tcltk/を見よ。SDKとビルドするなら、SDKのLibrary/Frameworksディレクトリ内に最新版のTclおよびTkフレームワークが含めれているか確認すること。場合によっては、それらのインストールされた場所 (/Library/Frameworks) に手動でシンボリックリンクを貼る必要がある。wxPythonを使用する場合、それを行う必要がある。Cocoaを使用する場合は、PyObjCを得る必要がある。 4.フレームワーク版Pythonのビルド方法 このディレクトリ (Mac/) は、"/Applications/Python <VERSION>"に幾つかのPython関連のアプリケーション (つまり、完全なmacOS .appアプリケーション) を、Pythonフレームワーク内にPython.app隠しヘルパーアプリケーションを、/usr/local/bin内に"python"を含むunixツールを作成するMakefileを保有する。加えて、Macサブツリーの関連部分をPython.frameworkにインストールする"installmacsubtree"ターゲットも保有する。  通常の呼び出しでは、最後のステップでメインのMakefileを間接的に呼び出す。 ./configure --enable-framework make make install この手順では、フレームワークは/Library/Framework/Python.framework、アプリケーションは/Applications/Python <VERSION>およびUnixツールは/usr/local/binに配置される。  他の場所にインストールする際、例えばマシンの管理者権限を持っていない場合に$HOME/Library/Frameworksを対象とするなら、インストール可能である。これは、--enable-framework=$HOME/Library/Frameworksをつけてconfigureすることで行うことができる。他の2つのディレクトリも、その場合は$HOME/Applications/Python-<VERSION>および$HOME/binにインストールされる。    全てではなく、一部だけインストールを行いたい場合は、メインのMakefileを見よ。frameworkinstallは、フレームワーク自身、Macサブツリー、アプリケーションおよびunixツールをインストールする幾つかのサブターゲットから成り立っている。    通常のframeworkinstallに含まれない追加ターゲットであるframeworkinstallextrasがあり、Toolsディレクトリを"/Applications/Python <VERSION>"にインストールする。これは、バイナリの配布に役立つ。 "/Applications/Python <VERSION>"下のプログラムは何をするのか? "IDLE.app"はPythonの統合開発環境 (エディタ、デバッガなど) である。  "Python Launcher.app"は、.py、.pyc および.pywがダブルクリックされれた時に実行されるヘルパーアプリケーションである。最初の二つ(.pyおよび.pyc)はターミナルウインドウが起動され、通常のコマンドラインでPythonからスクリプトを実行する。後者 (.pyc ) に対しては、Python.appインタプリタ上でスクリプトを実行され、スクリプトがGUI関連を実行できるようにするためである。Optionキーを押したままドラックかダブルクリックすると、スクリプトに実行時オプションを設定できる。これらオプションはPython Launcherの設定ダイアログから永続的に設定することもできる。    pythonx.xプログラムは、コマンドラインからpythonスクリプトとして実行する。以前は、macOS上のPythonの初期リリースでGUIプログラムを実行するために必要だったpythonwx.xを含む、さまざまな互換性を持つエイリアスもインストールされていた。3.4.0時点で、pythonwx.xエイリアスはもうインストールされていない。 バイナリ配布版をどう作るか?  公開されているソースをhttps://www.python.org/download/からダウンロードして、展開する。"Mac/BuildScript"に移動する。そこに、全ての作業を行うbuild-installer.pyスクリプトがある。これは、幾つかのサードパーティ製ライブラリをダウンロードしてビルドし、フレームワーク版Pythonの設定とビルドをしてインストールを行い、インストーラパッケージファイルを作り、それをDMGイメージとして固める。このスクリプトは加えて、このリリースに対する現在のPythonドキュメント一式のHTMLコピーを、フレームワークに含ませるためにビルドする。インストーラパッケージはIDLE、pydoc、ShellユーザおよびFinderユーザが使用するためのドキュメントへのリンクを作成する。  スクリプトはユニバーサルバイナリをビルドするので、このスクリプト(build-installer.py)はmacOS 10.4以降およびXcode 2.1以降がインストールされた環境で実行しなければならない。しかしながら、Pythonのビルドプロセスは、macOS 10.4だと追加設定なしに利用できない幾つかの依存関係を持つため、Xcode 2で提供されている物以外にも、追加のソフトウェアが必要かもしれない。  古いバージョンのSDKsやXcodeを組み合わせて、より新しいシステム上で古いシステムに互換性のあるインストーラをビルドする事は可能ではあるが、これは完全確実ではない。そのため、作成される実行バイナリ、共有ライブラリおよび.soバンドルに対して、動的リンクの依存性が適切か全てのサポートされるシステムで入念に調査およびテストする必要がある。最小限のmacOSバージョンがサポートされて稼働しているシステム上で、配布版をビルドするのが安全である。 これら全ては通常完全に /tmp/_pyに切り離されて行われるため、通常のビルドディレクトリは使用されず、/にもインストールを行う事はない。    このスクリプトはファイルを探すために、BuildScriptディレクトリ内で実行する必要がある。スクリプトは幾つかのコマンドライン引数を指定できるので、より詳細な情報は--helpを引数にスクリプトを実行して得よ。 Configureの警告  configureスクリプトは時々以下の様な警告を出す:   configure: WARNING: libintl.h: present but cannot be compiled configure: WARNING: libintl.h: check for missing prerequisite headers? configure: WARNING: libintl.h: see the Autoconf documentation configure: WARNING: libintl.h: section "Present But Cannot Be Compiled" configure: WARNING: libintl.h: proceeding with the preprocessor's result configure: WARNING: libintl.h: in the future, the compiler will take precedence configure: WARNING: ## --------------------------------------- ## configure: WARNING: ## Report this to https://bugs.python.org/ ## configure: WARNING: ## --------------------------------------- ## これは大抵の場合、ユニバーサルバイナリをビルドしようろする時に、/usr/local内に必要なアーキテクチャのライブラリを保有していない事を意味する。ビルドを完了させるため、一時的に/usr/localを傍に動かせ。 フレームワーク版インストールをアンインストールする (バイナリインストーラー版インストールも含む)    フレームワークのアンインストールは、インストールされた全てを手動で削除して行う。これは、ソースおよびバイナリインストーラを使用したインストールのどちらに当てはまる。mac OSはシステム自身によるアンインストーラを提供していない。  フレームワーク版のインストールにおける主な部分は、フレームワークそれ自体であり、/Library/Frameworks/Python.frameworkにインストールされている。ここには複数バージョンのPythonが存在することがあり、一つのバージョンだけを削除したい場合は、そのバージョンに対応したサブディレクトリを削除する: /Library/Frameworks/Python.framework/Versions/X.Y。その場合、/Library/Frameworks/Python.framework/Versions/Current がインストールされたバージョンのPythonをさしているシンボリックリンクか確認すること。  フレームワーク版のインストールは/Applications/Python X.Yに幾つかのアプリケーションもインストールしている。  最後に、フレームワーク版インストールは/usr/local/binにファイルをインストールし、/Library/Frameworks/Python.framework/Versions/X.Y/binに/usr/local/bin内のファイル全てのシンボリックリンクを貼っている。 Weak linkingのサポート6  CpythonのソースはmacOS 10.9をデプロイの対象とする一方、最新のSDKでビルドする事に対応している。これは、macOS 10.10以降で導入されたシンボルのweak linkingと、実行時にそれらが有効か確認して実現している。    これには、macOS 10.13以降のApple製コンパイラーのツールチェインが必要となる。 HAVE_<FUNCTION> はconfigureスクリプトによって定義される(または未定義となる)マクロ。  HAVE_<FUNCTION>_RUNTIMEは関連するソースファイル上で定義されているマクロ。このマクロは、十分に新しいApple製コンパイラが使用される場には、__builtin_availableが呼ばれる様に拡張し、そうでなければtrue値を返す。 <function>が呼び出される前に、HAVE_<FUNCTION>_RUNTIMEを使用する。このマクロは、if文の中で単体の式として使用されなければいけない: if (HAVE_<FUNCTION>_RUNTIME) { /* <function> is available */ } または、 if (HAVE_<FUNCTION>_RUNTIME) {} else { /* <function> is not available */ } 他のパターン (例えば!HAVE_<FUNCTION>_RUNTIME) はApple製コンパイラではサポートされていない。 参考 https://www.python.org/downloads/macos/ https://www.python.org/community/sigs/current/pythonmac-sig/ https://devguide.python.org/ macOS 64-bit universal2 installerという物もあり...。 ↩ プログラムが実行時に動的ライブラリを呼び出す際、検索対象として追加されるディレクトリ。プログラムのコンパイル時に、リンカーの-rpathフラグ等により指定される。 ↩ ↩2 Copyright © 2001-2022 Python Software Foundation; All Rights Reserved (参照: https://docs.python.org/ja/3/license.html#psf-license) ↩ https://docs.python.org/ja/3/library/subprocess.html ↩ https://docs.python.org/ja/3/library/sys.html ↩ CPythonのコードを弄る場合に必要となる知識なので、今回の本題的には特に理解が必要な知識ではないです。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】SQLite3の使用方法

SQLite3をPythonで操作する 現在Django、Flaskなどのフレームワークを使用せずに、 簡単なWebアプリケーション(Todo管理アプリ)を開発してます。 DBを何にするか(何が使用できるか)調査したところ、標準でSQLite3が 使用できるとのことなので、 Pythonのsqlite3を使用してPythonのコードでsqlite3を操作する方法を記載します。 (初投稿のため優しく見守れっていただけると嬉しいです) 参考ドキュメント python公式ドキュメント Pythonバージョン Pythonバージョンは以下で確認できる。 python -V Python 3.8.0 基本的な記載方法 基本的には以下のように記載方法になる。 import sqlite3 dbname = 'example.db' conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute(sqlクエリ) conn.commit() conn.close() import sqlite3でsqlite3をインポートする connect(dbname)で使用するDBファイルに接続する(.dbファイルが存在しない場合、新規で作成される。) cursor()でオブジェクト化しexecuteが実行できるようにする。 execute(sqlクエリ)でsqlクエリを実行できます。 DBに変更がある場合に、commitすることで更新できる。 close()でDBとの接続を閉じる テーブル作成 create_table.py import sqlite3 dbname = 'example.db' conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute('CREATE TABLE examples(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING, price INTEGER)') conn.close() create文でテーブルを作成する id INTEGER PRIMARY KEY でidを指定しなくても値がn+1で作成される(最終が3の場合4が入る) AUTOINCREMENTを入れると最終が3のデータを削除後、データを登録されると3ではなく、4が入る。 データ登録 innsert.py import sqlite3 dbname = 'example.db' conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute('INSERT INTO examples values(1, "りんご",100)') # コミットしないと反映されないため、登録、変更処理をした際はcommit()する conn.commit() conn.close() データ取得 select.py import sqlite3 dbname = 'example.db' conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute('SELECT * FROM examples') conn.close() データ更新 update.py import sqlite3 dbname = 'example.db' conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute('UPDATE examples SET price = 260 WHERE id = "3"') conn.commit() conn.close() データ削除 delete.py import sqlite3 dbname = 'example.db' conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute('DELETE FROM examples WHERE id = "2"') conn.commit() conn.close()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

部分和問題の3つのアプローチをPythonを用いて実装する

問題概要 n 個の正の整数 a[0],a[1],…,a[n−1] と正の整数 S が与えられる。これらの整数から何個かの整数を選んで総和が S になるようにすることが可能か判定せよ。可能ならば "Yes" と出力し、不可能ならば "No" と出力せよ。 【制約】・1≤n≤10・1≤a[i]≤1000・1≤S≤10000 【数値例】 1) n=3 a=(7,5,3) S=10 答え: Yes (7 と 3 を選べばよいです) 2) n = 2 a=(9,7) S=6 答え: No 問題概要はけんちょん様のこちらの記事から抜粋させていただいています iterative(反復的)なアプローチ from sys import stdin N, S = map(int, stdin.readline().split()) *A, = map(int, stdin.readline().split()) for i in range(2**N): temp_sum = 0 for j in range(N): if ((i >> j) & 1): temp_sum += A[j] if temp_sum == S: print("Yes") break else: print("No") n=3 a=(7,5,3) S=10の時を具体例を考えてみます。 2**3 = 8 であるので、i は 0~7 の間で探索されます。 全探索した際の表 (1) ちなみに、以下の内側のfor文については、右シフト演算を繰り返し、右端の数が 1 であればその数を選ぶ、という操作をおこなっています。 N = 3 であるので、j は 0~2 をループします。 j が 0 の時、一番右の数をチェック、 j が 1 の時、真ん中の数をチェック、 j が 2 の時、一番左の数をチェックしています。 for j in range(N): if ((i >> j) & 1): temp_sum += A[j] recursive(再帰的)なアプローチ from sys import stdin N, S = map(int, stdin.readline().split()) *A, = map(int, stdin.readline().split()) flg = False def dfs(position, temp_sum): global flg if position == N-1: if temp_sum == S or temp_sum+A[position] == S: flg = True return # 選ぶ場合 dfs(position+1, temp_sum+A[position]) # 選ばない場合 dfs(position+1, temp_sum) dfs(0, 0) print("Yes" if flg else "No") その数を「選ぶ」or「選ばない」を再帰的に繰り返していく解法です。 終了の条件として、postionがN-1,つまり、最後の数を選ぶかどうかに達した際に目標であるSに一致するかどうかを判定しています Dynamic Programming(動的計画法)によるアプローチ from sys import stdin N, S = map(int, stdin.readline().split()) *A, = map(int, stdin.readline().split()) dp = [[True] + [False]*S for _ in range(N)] for i in range(N): for j in range(S+1): if j >= A[i]: dp[i][j] = dp[i-1][j] or dp[i-1][j-A[i]] else: dp[i][j] = dp[i-1][j] print("Yes" if dp[-1][-1] else "No") n=3 a=(3, 5, 7) S=10の時をdpの様子を以下に示します 0 1 2 3 4 5 6 7 8 9 10 3 True False False True False False False False False False False 5 True False False True False True False False True False False 7 True False False True False True False True True False True dp[0],つまり最初の行は a = (3) の選び方によってとりえる値をしめしています。 3を選ぶか、何も選ばないかの2択なので、0, 3のみがTrueとなっています dp[1],つまり、真ん中の行では、a =(3, 5) の選び方によってとりえる値をしめしています。 3と5を選んだ場合の8,57のみを選んだ場合の5にTrueが追加されていることがわかります。 dp[2], つまり、最後の行が、0 ~ 10 までで a = (3, 5, 7) の選び方によってとりえる値をしめしています。 ここの例では S = 3, 5, 7, 8, 10 がaの選び方によって取りえる値だということがわかります。 最後に 至らぬ点があれば気軽にご指摘お願いします。 一つの問題に対して、一つ解法を実装すると満足しがちですが、あえて複数の解法を考えることは、初心者の自分にとってはとても有効でした!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python3.10 で bit_count メソッドが追加されたので ABC 081 B - Shift Only を解いてみよう

Python 3.10 が登場しましたね。パターンマッチの追加などより大きく便利な変更も多くありますが、地味な更新点として、int#bit_countメソッドが追加されました。 これは POPCNT などとも呼ばれ、二進数表記した際の 1 の数を返すような関数です。たとえば、十進数の 5 は二進数では 101 となりますので、 assert (5).bit_count() == 2 となります。 これまでもn.bit_count()の代わりにbin(n).count("1")とすることで同様の値を取ることができましたが、おそらく専用のメソッドを用意することで速度の改善が見込めると思われます。実際最近の CPU では POPCNT の専用命令を用意されていたりもするので、おそらくそちらを利用するものと思われます。専用命令がなかった場合でもより高速に求めるアルゴリズムが知られているので、高速化できるでしょう。 ちなみに Python のint型は多倍長整数を使っているため、負の整数に対してint#bit_countを使用するとどうなるか気になるところですが、これは assert n.bit_count() == abs(n).bit_count() となるようです。おそらくbin(n).count("1")の結果に合わせるようにしているのだと思われます。bin(-5) == "-0b101"のように表示されるので。 今回はint#bit_countの追加を記念(?)して AtCoder の過去問精選 10 問より POPCNT を使用することでスマートに解ける Shift Only を解いてみようと思います。ただし AtCoder で利用できるのは Python3.8 と PyPy7.3 (Python3.6 相当) なので実際に使用するのはbin(n).count("1")の方です。 記述は競プロで初めて Python 触ったくらいの Python 初学者向けになっており、普段から Python を触っている人にはややくどい説明になっていると思いますがご了承ください。 \def\bin#1{{#1}_2} 問題概要 まずは問題を見てみましょう。 問題文 黒板に $N$ 個の正の整数 $A_1, \ldots, A_N$ が書かれています. すぬけ君は,黒板に書かれている整数がすべて偶数であるとき,次の操作を行うことができます. 黒板に書かれている整数すべてを,$2$ で割ったものに置き換える. すぬけ君は最大で何回操作を行うことができるかを求めてください. 制約 $1 \le N \le 200$ $1 \le A_i \le 10^9$ 入力 入力は以下の形式で標準入力から与えられる。 N A1 A2 ... AN 出力 すぬけ君は最大で何回操作を行うことができるかを出力せよ. シミュレーション やるべきことは明確ですし、計算量も大したことはないので、これをそのままコードに落とし込むことで解くことができそうです。これをシミュレーションと言います。 まずは入力を受け取ります。 _ = input() a = [int(n) for n in input().split()] input関数は入力を 1 行分文字列str型で得ます。1 行目は要素数 $N$ を受け取っていますが、Python で解く場合この値は不要なので明示的に_に代入することで、これをそれ以降使用しないことを示します。 2 行目は少々ややこしいことをしています。これは「内包表記」です。 [A for B in C] という内包表記は、 C のそれぞれの要素を B という名前で受け取り A の式で評価したものを集めたリスト を作ります。 input().split()は、入力された文字列をスペースで分けた文字列のリストlist[str]を得ます。たとえば8 12 40と入力されたとき、input()は"8 12 40"という文字列を返します。これをstr#split()メソッドが["8", "12", "40"]という文字列のリストに変換します。 このそれぞれの要素をnという名前で受け取り、int(n)という式によって評価します。つまり、各要素が整数int型に変換していますので、最終的にaには[8, 12, 40]という整数のリストlist[int]が代入されることになります。 入力が済んだのでシミュレーションをしていきます。まず、「すべて偶数であるとき」を調べるための関数を定義しておきましょう。 def is_all_even(l): for e in l: if e % 2 == 1: return False return True これは「引数lの要素eを順に取り出し、eが奇数(2で割ったあまりが1)だったら中断してFalseを返す、最後まで行った(全部偶数だった)らTrueを返す」という動作をそのまま書き下しています。 そして「全部偶数だったら」動作を続けられるので、これは while 文を使って次のように書けるでしょう。 count = 0 while is_all_even(a): a = [ai // 2 for ai in a] count += 1 再び内包表記が出てきました。やっていることは「aの要素すべてを2で割ってaを置き換える」だけです。なお、割り算の演算子として/ではなく//を使用していますが、これは解がint型になるよう、切り捨てを行う除算の演算子です。 そして、繰り返しの前にcount変数を0で初期化し、繰り返しのたびに1増やしています。こうすることで何回 $2$ で割ったかを記録します。 最後にcount変数を出力して終了です。 print(count) シミュレーション(改善) 上記の解法で問題なく AC を取ることができますが、もう少し改善できるところがあります。 たとえば、今回「すべて偶数か調べる」関数を定義しましたが、python組み込みの関数としてallがあります。これは、引数がすべてTrueであったときだけTrueを返し、一つでもFalseがあればFalseとなる関数です。 つまり、リストの各要素が偶数か調べた配列を内包表記で作って渡せば、自前で関数を用意する必要はなくなります。 count = 0 while all([ai % 2 == 0 for ai in a]): a = [ai // 2 for ai in a] count += 1 これをさらに改善できます。内包表記[A for B in C]ですが、これはリストに変換することを示す角カッコ[]とジェネレータ式(A for B in C)の組み合わせになっています(ジェネレータ式の丸カッコは、それが唯一の引数の場合省略できます。[A for B in C]はlist(A for B in C)のシンタックスシュガーであり、これはlist((A for B in C))の省略でもあるわけです)。 そして、関数allは引数としてリストに限らず、イテラブルなオブジェクトを取ることができます。イテラブルなオブジェクトとは、簡単に言うとひとつひとつ要素を取り出すことができるようなもので、for文に対応するオブジェクトと言い換えることができます。リストの他にはタプルやセットがあります。辞書もイテラブルなオブジェクトですが、取り出せるのはキーだけです。 ここまで書けばピンとくる人もいるかと思いますが、ジェネレータ式もイテラブルなオブジェクトのひとつです。ゆえに、以下のように書き換えることができます。 count = 0 while all(ai % 2 == 0 for ai in a): a = [ai // 2 for ai in a] count += 1 リストの内包表記ではなくジェネレータ式を直接書くことの利点は文字数を節約できるだけではありません。内包表記でリストを作るとき、各要素を実際に計算してリストを作るのに対し、ジェネレータ式はいわば「計算するためのルール」そのものを取り扱うことができます。 つまり、all関数にリストの内包表記を渡すと、内包表記によって実際にすべての要素が計算され、その後で中にFalseがあるかどうかを調べることになりますが、ジェネレータ式ではひとつひとつを計算していき、途中でFalseが出てきたら即中断することができるため、時間が節約できるのです。 NTZを直接求めて最小を探す ここまでの説明ではいつまで経ってもint#bit_countが出てきませんでした。実は、シミュレーションするよりもスマートに解く方法が存在するのです。それを考えつくのは少々難しいですが、難しいのはアイデアまでで、計算の内容はさほどでもありません。 突然ですが、$120$ という数字を $10$ で割るとどうなるでしょう。答えは当然 $12$ であり、みなさんもすぐ分かると思います。 では、どのようにして $12$ という数字を思いついたでしょうか。まさか愚直に割り算の筆算などをしたわけではないと思います。 我々が無意識に使っている十進数では、$10$ を一つのまとまりとして扱っています。たとえば $120$ なら $100 = 10^2$ が $1$ 個、$10 = 10^1$ が $2$ 個、$1 = 10^0$ が $0$ 個という風にです。それを $10$ で割るのですから、当然 $10 = 10^1$ が $1$ 個、$1 = 10^0$ が $2$ 個となり、すなわち $12$ となるわけです。これは見た目上 $120 \to 12$ という風に、右にひとつずらしたようになります。 二進数でも同じことで、たとえば $6$ を $2$ で割ると $3$ になりますが、これも二進数で表せば $\bin{110} \to \bin{11}$ となり、右にひとつずらしていることになります。これをコンピュータの世界では「(右)シフト」といい、これこそが問題のタイトル“Shift Only”の由来でしょう。 さて、もう一度十進数の例に戻りましょう。$120$ は $10$ で $1$ 回割れます。$3700$ は $2$ 回割れます。これは当然、右シフトするのですから $1$ の位から連続する $0$ の数を数えるのと一緒です。 二進数でも同じことで、$6$ は $2$ で $1$ 回割ることができ、$56$ は $3$ 回割れますが、二進数で表せばそれぞれ $\bin{110}$、$\bin{111000}$ となりますから一目瞭然です。 すなわち、今回の問題は「二進数で表記した際、 $1$ の位から連続する $0$ の数を求め、その最小を答えよ」と置き換えることができるのです。ここで求めたい値を NTZ: Number of Trailing Zero とよび、わりと頻出する概念でもあります。 では、それをどのように求めるかです。これにはビット演算のマジックが必要になります。 まず、いままで見てきた $\bin{110}$ のような二進数は、$\bin{\cdots000110}$ というように、上の桁が無限に $0$ で埋められているものとみなすことができます。 では反対に、すべての桁が $1$ で埋められている、$\bin{\cdots111}$ という架空の数を考えてみてください。これは十進数でいうと $\cdots999$ みたいな感じです。 これに $1$ を足すとどうなるでしょう。無限に繰り上がりしつづけますから、逆説的にすべての桁が $0$ で埋められる(納得できない方は、「$0$ にならない桁がない」とお考えください。有限ならどこかで繰り上がりが止まりますが、無限なので止まらないのです)と考えることができます。$\bin{\cdots000}$ は、明らかに $0$ です。 $1$ を足して $0$ になる数は、普通 $-1$ ですから、この架空の数は $-1$ として扱うことにしましょう。実際、コンピュータでマイナスの数を表現する場合、このような考え方がでてきます。コンピュータの場合桁数は有限ですが、溢れた分は無視するのです。これを「二の補数」といいます。 さて、ここである数 $x$ に対し、それを二進数で表現した時の $0$ と $1$ をすべての桁でひっくり返す $\lnot x$ というものを考えます。例えば $\lnot 6 = \lnot \bin{110} = \bin{\cdots111001}$ というような感じです。 ここで $x + \lnot x$ を考えると、当然すべての桁が互い違いのため、繰り上がりすることなくすべて $1$ になりますから、常に $x + \lnot x = \bin{\cdots111} = -1$ が成り立つことがわかります。 この式を変形すると、$-x = \lnot x + 1$ という式が得られます。では、こうして得られた $-x$ は、もとの $x$ に対してどのような性質をもつでしょうか。 具体例として $x = 101100_2$ という数を当てはめてみます。 $\lnot x = \cdots111010011_2$ です。これに $1$ を足すと、もともと $0$ が連続していた部分が繰り上がり続け、もともと初めて $1$ が登場した部分 で繰り上がりが止まります。それより上の桁は$\lnot x$ のままですから、もともとの数の桁の反転です。よって、$-x = \bin{\cdots 111010100}$ となります。 さて、$-x$ の性質についてまとめます。 下から数えてもともと $0$ が連続していた部分 → $0$ のまま 下から数えてもともと初めて $1$ が登場した桁 → $1$ のまま それより上の桁 → もとの反転 ここで、特殊な演算 $\cap$ を用意します。 $x \cap y$ は、$x$ と $y$ をそれぞれ二進数で表記したとき、両方が $1$ になる桁のみ $1$ となり、それ以外は $0$ になるような二進数表記の数字を返す演算です。たとえば、 $9 \cap 10 = \bin{1001} \cap \bin{1010} = \bin{1000} = 8$ のような感じです。 これを使って $x \cap -x$ を計算するとどうなるでしょう。 下から数えてもともと $0$ が連続していた部分 → $0$ のまま → 両方 $0$ なので $0$ 下から数えてもともと初めて $1$ が登場した桁 → $1$ のまま → 両方 $1$ なので $1$ それより上の桁 → もとの反転 → 互い違いなので $0$ となりますから、$x$ を二進数表記したとき、下から数えて初めて $1$ が登場した桁以外を $0$ に置き換えた数が得られます。つまり、$\bin{101100}$ を $\bin{000100}$ のように変換できました。 さて、我々が最初に求めたかったのはなんだったでしょう。$1$ の位から連続する $0$ の個数でしたね。では、$\bin{000100}$ から $1$ を引いてください。繰り下がりによって $\bin{000011}$ となりました。 もうおわかりですね。この $1$ の数を数えればいいのです。そして、それはint#bit_countによって実現できます! では Python で書き下していきましょう。 $x \cap y$ は Python ではx & yと書けます。最小値は組み込みのmin関数で求めることができ、これもジェネレータ式を入れることができます。 _ = input() a = [int(n) for n in input().split()] #count = min(((ai & -ai) - 1).bit_count() for ai in a) ## Python 3.10 count = min(bin((ai & -ai) - 1).count("1") for ai in a) print(count) 偶数判定も繰り返しも使うことなく答えを求めることができました。 ひとつの数にまとめてから NTZ を求める 上でも十分なのですが、もうすこし別解があります。 $x \cap y$ と対をなす $x \cup y$ という演算があります。これは、両方が $0$ になる桁のみ $0$ となり、それ以外は $1$ になるというもので、双対関係にあります。Python ではx | yと書けます。 この演算を使って $b = A_1 \cup A_2 \cup \ldots \cup A_N$ というように $A$ をすべてまとめてしまうとどうなるでしょう。すべて $0$ である桁のみが $0$ となるわけなので、$b$ の NTZ は $A$ の各要素の NTZ の最小に一致します。 _ = input() a = [int(n) for n in input().split()] b = 0 for ai in a: b |= ai #count = ((b & -b) - 1).bit_count() ## Python 3.10 count = bin((b & -b) - 1).count("1") print(count) これでも答えを得ることができます。 for 文を使わず 1 行で $b$ を求めることもできます。これは組み込みの関数では実現できませんが、標準ライブラリにあるfunctools.reduceを使うことで実現できます。 from functools import reduce これはどんな関数かというと、要素をひとつずつ取り出してなんらかの関数でまとめていき、最終的にひとつの値を得るものです。要素すべての合計を得るsumという組み込み関数がありますが、 assert reduce(lambda x, y: x + y, l) == sum(l) のように、それをより一般化した関数です。 reduce関数の記法は以下の感じです。 reduce(function, iterable, initializer) funtionは引数をふたつ取る関数、iterableはイテラブルなオブジェクト、initializerは初期値(省略可能)です。functionに与える関数を 1 行で書くにはラムダ式を使います。たとえば、 def hogehoge(x, y): return some_expression というような関数を、 lambda x, y: some_expression と書くことができます。 これによって b = reduce(lambda x, y: x | y, a) と一行で求めることができました。 余談ですが、自分でラムダ式を使って定義する代わりに、operatorという標準ライブラリにある「関数形式の標準演算子」を使う方法もあります。+はadd、|はor_という名前で定義されています。 from operator import add, or_ assert reduce(add, l) == sum(l) b = reduce(or_, a) 解けた! というわけで、最終的な私の提出は以下のコードです。 from functools import reduce from operator import or_ _ = input() a = [int(n) for n in input().split()] b = reduce(or_, a) # count = ((b & -b) - 1).bit_count() ## Python 3.10 count = bin((b & -b) - 1).count("1") print(count)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Cloud MonitoringのメトリクスをPythonで取得する

作ってみましたが、若干詰まったので書いておきます。 コード コード from datetime import datetime, timedelta from google.cloud import monitoring_v3 project = "your-project" # プロジェクト名 project_id = f"projects/{project}" instance_name = "instance-name" # インスタンス名 # 今現在から過去1時間分のデータを取得 now = datetime.now() time_span_hour = 1 start_ts = int((now - timedelta(hours=time_span_hour)).timestamp()) end_ts = int(now.timestamp()) # フィルターには色々設定可能はこちらを参照 https://cloud.google.com/monitoring/api/v3/filters metric_filter = f'metric.type = "compute.googleapis.com/instance/cpu/utilization" AND metric.labels.instance_name = "{instance_name}"' metric_interval = monitoring_v3.TimeInterval(start_time={"seconds": start_ts}, end_time={"seconds": end_ts}) metric_cli = monitoring_v3.MetricServiceClient() res = metric_cli.list_time_series( request=monitoring_v3.ListTimeSeriesRequest( name=project_id, filter=metric_filter, interval=metric_interval, view=monitoring_v3.ListTimeSeriesRequest.TimeSeriesView.FULL, aggregation=monitoring_v3.Aggregation( alignment_period={"seconds": 300}, per_series_aligner=monitoring_v3.Aggregation.Aligner.ALIGN_MAX, ), ) ) for r in res: header = f"{r.resource.type} - {r.metric.labels['instance_name']} - {r.metric.type}" print(header) print("----------------") for p in r.points: print(f"{p.interval.start_time} : {p.value.double_value}") 出力例 gce_instance - instance-name - compute.googleapis.com/instance/cpu/utilization ---------------- 2022-03-22 10:32:27+00:00 : 0.017728691561633998 2022-03-22 10:27:27+00:00 : 0.01778202000300553 2022-03-22 10:22:27+00:00 : 0.018050090768349493 2022-03-22 10:17:27+00:00 : 0.01823367738704557 2022-03-22 10:12:27+00:00 : 0.01796084328830716 2022-03-22 10:07:27+00:00 : 0.020671517363830577 2022-03-22 10:02:27+00:00 : 0.018054658292775607 2022-03-22 09:57:27+00:00 : 0.01777473582532707 2022-03-22 09:52:27+00:00 : 0.017657926236356994 2022-03-22 09:47:27+00:00 : 0.017721036677447916 2022-03-22 09:42:27+00:00 : 0.020249340092521113 2022-03-22 09:37:27+00:00 : 0.0206812247338424 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CygwinにPythonをインストールする方法

CygwinにPythonをインストール Cygwinの設定をするため、setup-x86_64.exe を起動する。 Python39、wget を追加する。 Cygwinにpipをインストール 以下の記事を参考 以上、CygwinにPythonをインストールする方法でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Juliusで自作N-gramを使う方法

bingram形式のN-gramを作成 N-gramの作成方法の詳細は前回の記事PythonでN-gramを作る(Julius)に記載しております。 今回は作成したN-gramを C:/Julius/my_julius/ にコピーしておく。 自作N-gramの適用 過去のWindows環境のPythonで大語彙連続音声認識エンジン Julius を使う方法でダウンロードした音声認識パッケージ(dictation-kit-4.5)の /dictation-kit-4.5/am-dnn.jconf を開く。 以下の部分を見つける。 am-dnn.jconf # The phone set of DNN-HMM is different from GMM-HMM, so requires another dictionary -v model/lang_m/bccwj.60k.pdp.htkdic -vは単語辞書です。自作の単語辞書を使いたい場合はここを変更する。 自作N-gramを使う場合は以下のように一行追加する。 am-dnn.jconf # The phone set of DNN-HMM is different from GMM-HMM, so requires another dictionary -v model/lang_m/bccwj.60k.pdp.htkdic -b C:/Julius/my_julius/n-gram.bingram ### 追加 以上で設定完了。 あとは、過去のWindows環境のPythonで大語彙連続音声認識エンジン Julius を使う方法と同様にJuliusを実行する。 以上、Juliusで自作N-gramを使う方法でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

日記;回すだけIII ⑪ pythonでSDO、PDOの読み書き<その6 Profile Position Mode + アナログ入力(EPOS4)>ベータ版

 EPOS4のコントローラには、 ディジタル入力4ポート、出力2ポート アナログ入力2ポート、出力2ポート が用意されています。オブジェクト・ディレクトリに登録されているので、SDOで読み書きできます。前回ディジタル入出力を利用しました。今回はアナログ入力です。 アナログ入力  Analog I/O (X8)コネクタのピン番号と機能です。 1 white Analog input 1, positive signal 2 brown Analog input 1, negative signal 3 green Analog input 2, positive signal 4 yellow Analog input 2, negative signal 5 grey Analog output 1 6 pink Analog output 2 7 blue Ground  1-2のアナログ入力1、3-4のアナログ入力2を利用します。ともに12ビット、±10Vの差動入力です。 機能を定義する  WizardsのStartupからAnalog Inputを選択します。Analog input1にFunctionalityを画面のようにSet value velocity選択します。  電圧Voltageを速度Velocityに換算することができます。あとでわかったことですが、これはCSVモードで生きるようで、今回のようなProfile Position Modeでは有効なVelocity値が得られませんでした。  Analog input2はNoneのままです。  Finishで終了します。  オブジェクト・ディレクトリでも確認できます。  0x3160にアナログ電圧が入っています。  0x60ffに換算された速度値が入っています。見ていると、値は更新されます。しかし、プログラムでは初期の値しか代入できず、更新された値は反映できませんでした。 ハードウェア  10kΩのボリュームを二つ並べた基板を用意しました。  コネクタX7 Digital I/Oの7番 Vdd(5V)から5Vをもらってきました。ディジタルとアナログ・コネクタのGNDは共通のようです。 1 Analog input 1, positive signal 2 Analog input 1, negative signal 3 Analog input 2, positive signal 4 Analog input 2, negative signal 5 +5V 6 GND プログラム  入力はAnalog input1だけの対応です。  node.sdo[0x3160]['Analog input 1 voltage'].physは電圧を読み取っています。実際の電圧の千倍の整数値が入っているので、速度の値にそのまま使います。差動入力のため、ボリュームを最小にするとマイナス値をとってくることがあります。対策をしていないので、エラーで止まります。 import canopen import time # Start with creating a network representing one CAN bus network = canopen.Network() # Add some nodes with corresponding Object Dictionaries node = canopen.BaseNode402(5,'maxon_motor_EPOS4_0170h_6050h_0000h_0000h.eds') network.add_node(node) # Connect to the CAN bus network.connect(bustype='ixxat', channel=0, bitrate=1000000) print("\n===start ID=5 EPOS4\n") def NMT_start(): node.nmt.send_command(0x81) # NMT reset time.sleep(0.5) node.nmt.send_command(0x82) # NMT Reset Communication #node.nmt.wait_for_bootup(15) time.sleep(0.5) node.nmt.send_command(0x80) # NMT Pre-ope time.sleep(0.5) node.nmt.send_command(0x1) # NMT start def CiA402_start(): print("---Switch CiA402") node.setup_402_state_machine() #node.sdo[0x6040].raw = 0x0010 # Fault Reset node.sdo[0x1003].raw = 0x0000 time.sleep(0.1) node.sdo[0x1003].raw = 0x0000 time.sleep(0.1) #node.sdo[0x1003].raw = 0x0000 #time.sleep(0.1) #node.state = 'NOT READY TO SWITCH ON' #node.state = 'SWITCH ON DISABLED' Statusword = node.sdo[0x6041].raw print('Statusword {:0>16b}'.format(Statusword)) #node.state = 'READY TO SWITCH ON' node.sdo[0x6040].raw = 0x0006 time.sleep(0.1) node.sdo[0x6040].raw = 0x0006 time.sleep(0.1) #node.sdo[0x6040].raw = 0x0006 #time.sleep(0.1) #node.state = 'SWITCHED ON' node.sdo[0x6040].raw = 0x0007 time.sleep(0.1) node.sdo[0x6040].raw = 0x0007 time.sleep(0.1) #node.sdo[0x6040].raw = 0x0007 #time.sleep(0.1) #node.state = 'OPERATION ENABLED' node.sdo[0x6040].raw = 0x000f time.sleep(0.1) node.sdo[0x6040].raw = 0x000f time.sleep(0.1) #node.sdo[0x6040].raw = 0x000f #time.sleep(0.1) #----int---------------- NMT_start() network.check() print('NMT:node state = {0}\n'.format(node.nmt.state)) network.sync.start(0.1) CiA402_start() print("\nSWITCH ready\n") print('Statusword {:0>16b}\n'.format(node.sdo[0x6041].raw)) #----main---------------- def move(velocity): node.sdo[0x6040].raw = 0x0006 node.sdo[0x6040].raw = 0x000f node.rpdo[3]['Controlword'].phys = 0x007f node.rpdo[3]['Target position'].phys = 5000 node.rpdo[3].transmit() node.rpdo[4]['Controlword'].phys = 0x007f node.rpdo[4]['Profile velocity'].phys = velocity node.rpdo[4].transmit() def readPosition(): print('Target position {:5d}'.format(node.tpdo[3]['Position actual value'].raw)) readPosition() node.sdo[0x6060].raw = 0x0001 # Modes of operation <- Profile Position Mode #node.sdo[0x607a].raw = 0 # Target position node.sdo[0x6081].raw = 30 # Profile velocity node.nmt.state = 'OPERATIONAL' print('\n>>> move\n') while 1: newVerocity = int(node.sdo[0x3160]['Analog input 1 voltage'].phys)+30 print(newVerocity) move(newVerocity) readPosition() time.sleep(3) #------------------- print("\n-----") node.sdo[0x6040].raw = 0x0010 # Fault Reset print("") node.nmt.send_command(0x02) # NMT remote stop network.check() print('node state end) = {0}'.format(node.nmt.state)) node.nmt.send_command(0x81) # NMT reset time.sleep(0.5) node.nmt.send_command(0x82) # NMT Reset Communication network.sync.stop() network.disconnect()  最初は変な速度で回ります。node.sdo[0x6081].raw = 30がないと、速度が変化しません。def move(velocity):の関数にしないと、速度が変化しません。  まだ何か、初期設定などで、設定すべき事柄などがフォローしきれていないようです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python: コール可能なオブジェクト(関数とか)かチェックする

def f(): pass async def af(): pass class C: @staticmethod def s(): pass @classmethod def c(cls): pass def i(self): pass if __name__ == "__main__": print(callable("x")) # False print(callable(None)) # False print(callable(lambda x: x + 1)) # True print(callable(f)) # True print(callable(af)) # True print(callable(C.s)) # True print(callable(C.c)) # True print(callable(C.__init__)) # True print(callable(C().i)) # True
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ミリ秒が四捨五入されて日付が繰り上がってしまう問題

#目次 何が起こったのか 原因 解決策 何が起こったのか openpyxlを使ってExcelに日付を出力(表示形式: yyyy-m-d)したところ、データベースの値(期待値)と異なる結果になってしまった。 ・Excel ・データベース(期待値) このようにデータベース上は 2022/3/21 なのにも関わらず、Excelに出力された日付は 2022/3/22 となっており、日付が繰り上がってしまっている。 原因 日付のミリ秒が500ms以上だった場合、Excelの仕様で四捨五入されてしまうから。 データベースの値をよく見ると、ミリ秒が付いている。 openpyxlでExcelに書き込む際、Excelの日付の仕様でこのミリ秒が四捨五入されてしまい「1秒」になってしまう。つまり、2022/3/21 23:59:59 + 1秒 = 2022/3/22 0:00:00 というわけだ。 ・ミリ秒が500以上の場合 → 1秒プラスで日付が繰り上がってしまい 2022/3/22 で出力されてしまう ・ミリ秒が500未満の場合 → 0秒プラスで日付は繰り上がらず 2022/3/21 で出力される 解決策 openpyxlで日付を書き込む際にミリ秒を切り捨てる セル.value = 日付.replace(microsecond=1) microsecondを0で置き換えるとエクセルの表示がバグるらしいので1を指定 結果、、、 正しく表示された?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでzipを展開する(Bandcampでダウンロードしたアルバムをアーティスト/アルバム名に展開)

書いた理由: Bandcampでアルバムを大量にダウンロードしたが、zip内に直でmp3が置かれている。 「アーティスト名/アルバム名」フォルダに展開したい。 ZipOpener.py # coding: utf-8 from zipfile import ZipFile import os import glob #zipファイル名は「アーティスト名 - アルバム名.zip」を想定 delimiter = " - " current_dir_name = os.path.dirname(__file__) file_names = glob.glob(current_dir_name + "/*.zip") for file_name in file_names: print(file_name) artist_name, album_name = file_name[:-4].split(delimiter, 1) tgt_dir = os.path.join(current_dir_name, artist_name, album_name) with ZipFile(file_name, 'r') as zip: zip.printdir() zip.extractall(tgt_dir) 使い方: 1.ZipOpener.pyをダウンロードしたzipファイルと同じディレクトリに置きます。 2.ZipOpener.pyを実行します。 注意点: delimiterで区切られていないファイルがあるとエラーを吐きます。 不安な場合、zip.extractall(tgt_dir) をコメントアウトして実行するとすぐ問題のファイルが分かります。 今度やりたいこと: マルチスレッドでやってみたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プロトコルクラスの代わりにジェネリック型を返す

概要 あるプロトコルを満たすオブジェクトを受け取り、そのオブジェクトを返す関数を考える。この関数の引数及び戻り値の型は、プロトコルクラスそのものより、プロトコルクラスを上界としたジェネリック型にしておくと都合が良い。 具体例 次のようなプロトコルを使ってソート関数を定義する。 ソート関数は破壊的で、利便性のためにソート結果を返すとする。 class Sortable(Protocol): def __len__(self) -> int: ... def swap(self, i: int, j: int) -> None: ... def less(self, i: int, j: int) -> bool: ... def sort(c: Sortable) -> Sortable: ... return c この時、上のソート関数のように戻り値の型がプロトコルクラスになっていると、受け取ったオブジェクトに対する操作が制限されてしまう。 例えば、次のようなgetメソッドを持つオブジェクトをソートすると、戻り値の型がSortableなのでgetメソッドが見つからない。 class WeightedList: def __len__(self) -> int: ... def swap(self, i: int, j: int) -> None: ... def less(self, i: int, j: int) -> bool: ... def get(self, i: int) -> str: ... items = WeightedList() ... sort(items).get(10) # -> "Sortable" has no attribute “get” もちろん今回の場合は二行に分けて、 sort(items) items.get(10) などとすれば問題ないのだが、lambda式の中では使えない。 解決策 戻り値の型をプロトコルクラスではなく、受け取ったオブジェクトの型と一致させれば良い。 つまりプロトコルを満たすジェネリック型を受け取り同じ型を返す関数として定義する。 T = TypeVar("T", bound=Sortable) def sort(c: T) -> T: ... return c items = WeightedList() ... sort(items).get(10) # sort(items)は WeightedList を返すので get を使える デメリットとしては、型変数の名前をシンプルなTなどとしていると、何を受け取る関数なのか自明でなくなってしまう。 「Sortableを継承している任意の型」と一目で分かる良い名前があれば良いのだけれど・・・。 ところで この TypeVar における bound の使い方は、ドキュメントには説明されていません。 TypeVarの項には、 a type variable may specify an upper bound using bound= <type> . This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subclass of the boundary type, see PEP 484. と書かれていて、「an actual type ... must be a subclass of the boundary type」今回の例だとWeightedListはSortableのサブクラスである必要があるように読めます。 厳密に言えば、WeightedListはSortableの構造的部分型(structural subtyping)であってサブクラスではありません。 エラーになるかと思いましたが、mypyではエラーになりませんでした。 ついでなので、PEP 484 の該当部分を見てみると、 A type variable may specify an upper bound using bound= <type> (note: <type> itself cannot be parameterized by type variables). This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subtype of the boundary type. となっていました。「an actual type ... must be a subtype of the boundary type」つまり構造的部分型でも問題ないようです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lambda Layersを使って処理をまとめてみた

背景 アカウントAの複数のLambdaから別のアカウントのEC2の情報を取得する実装を行っていて、Lambdaの中がほぼ同じ処理なのでLambda Layersを使ってmodule化してみます 今回話すこと Lambda Layersの概要 Lambda Layersの使い方 今回話さないこと クロスアカウントロール周り Lambdaのコードの解説 Lambda関数の作成方法 Lambda Layersの概要 公式には下記のような説明があります Lambda レイヤーは、追加のコードやデータを含めることができる .zip ファイルアーカイブです。 簡潔に言うと1つの関数を定義してそれを複数のLambda関数で使うことができるということです。 実際にLambda Layersを作成していく コードのzip化 mkidr python vi python/test.py zip -r lambda_layer_test.zip python/ 下記コードはtest.pyに入力してください import boto3 def ec2_access(acct_b): ACCESS_KEY = acct_b['Credentials']['AccessKeyId'] SECRET_KEY = acct_b['Credentials']['SecretAccessKey'] SESSION_TOKEN = acct_b['Credentials']['SessionToken'] ec2 = boto3.client( 'ec2', aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY, aws_session_token=SESSION_TOKEN, ) return ec2 def ec2_get_list(acct_b): ec2_instance_ids =[] ec2_instance_names =[] ec2_data = ec2_access(acct_b).describe_instances() for ec2_reservation in ec2_data['Reservations']: for ec2_instance in ec2_reservation['Instances']: instance_tag_name = list(filter(lambda item: item['Key'] == 'Name', ec2_instance["Tags"])) if instance_tag_name: ec2_instance_names.append(instance_tag_name[0]["Value"]) return { 'statusCode': 200, 'body': ec2_instance_names } なぜpythonフォルダを作らなければいけないかと思ったとおもいますが Layresは/optディレクトに展開されます pythonを例に出すと/opt/pythonに展開されるのでpythonディレクトリの下にファイルを配置します 公式の記事はこちらを参考にしてください Lambda Layersを作成 ①Lambaの画面からレイヤーを選択して、レイヤーの作成 ② 項目 値 名前 キャメルケース .zipファイルをアップロード zipファイルをアップロード 互換性のあるアーキテクチャ x86_64 ランタイム 今回は3.8を選択(複数選択することも可能) そして作成 Lambda関数からlambda Layersを読み込む 任意のLambda関数を作成する import test_layer import boto3 sts_connection = boto3.client('sts') acct_b = sts_connection.assume_role( RoleArn="arn:aws:iam::取得したいec2のアカウント番号:role/LambdaのRole名", RoleSessionName="cross_acct_lambda" ) def lambda_handler(event, context): return test_layer.ec2_get_list(instanse_tag_name,acct_b) layersを押してレイヤーの追加を押す カスタムレイヤーから先程作成したLayersを選択 LambdaのテストをするとEC2がprintされています Test Event Name hoge Response { "statusCode": 200, "body": [ "dev-ec2-1", "dev-ec2-2" ] } Function Logs START RequestId: 0c675016-70a4-4501-abfe-f97749dd1585 Version: $LATEST END RequestId: 0c675016-70a4-4501-abfe-f97749dd1585 REPORT RequestId: 0c675016-70a4-4501-abfe-f97749dd1585 Duration: 1283.40 ms Billed Duration: 1284 ms Memory Size: 128 MB Max Memory Used: 81 MB Init Duration: 961.56 ms Request ID 0c675016-70a4-4501-abfe-f97749dd1585 !!完!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

matlabで配列を書き出してPythonで読み込む

そんなこともありますわな マトラボ変数をMatファイルで書き出してScipyで読み込みます matlabでmatファイルを書き出す matファイルはHDF5的な書き出しができて、なおかつファイルサイズもお手頃で素敵です。 保存したい変数を次のように用意するとして sample.m p = rand(1,10); q = ones(10); 保存はこう sample.m save('sample.mat','p','q') 別に変数一つずつ保存してもらってもいいし、まとめて保存しても良いです。 変数はpythonで読むときのkeyにもなります。 Pythonで読み込む Scipyで読み込むと楽です。 sample.py from scipy.io import loadmat mat = loadmat("sample.mat") p = mat["p"] q = mat["q"] """ mat {'__header__': b'MATLAB 5.0 MAT-file, Platform: PCWIN64, Created on: Thu Mar 17 20:56:18 2022', '__version__': '1.0', '__globals__': [], 'p': array([[ 9.20610261e-01+5.33716017e-04j, 1.48098207e-01+1.37103097e-03j, -6.05423217e-02+4.97786550e-04j, ..., -1.29067978e-02-1.75805343e-05j, -5.98711507e-03+1.70542157e-06j, -3.41703417e-02-2.97162334e-06j], [ 7.78413766e-01+2.07434704e-01j, -5.29508081e-01-3.98614515e-02j, -2.22033326e-03+1.92038018e-01j, ..., -1.43717870e-01+5.92793752e-01j, 6.08463911e-01-1.28892855e-01j, -4.48988646e-01-4.41817629e-01j], [ 6.53348627e-02-1.16482903e-01j, 1.20795314e-01-1.22166736e-01j, 2.05349556e-01+5.95030609e-01j, ..., -3.88507368e-01+2.14046387e-01j, -6.56241982e-02+8.11742302e-02j, -6.18053104e-01-4.76789143e-01j], ..., [-4.96542729e-04-3.06323181e-05j, 1.37027067e-03+8.29222340e-05j, -5.08556043e-04-2.42244334e-05j, ..., -1.79536450e-05+5.10806267e-07j, 8.69602806e-07-2.13918906e-07j, -2.44910908e-06-5.23879227e-08j], [ 4.87682061e-04+2.54058950e-05j, 1.36103872e-03+6.73835019e-05j, 5.01392178e-04+2.19939364e-05j, ..., -1.88442556e-05+1.33598648e-06j, 5.54320250e-07-8.69686817e-07j, -2.99690101e-06-8.20490071e-07j], [-4.49789492e-04-2.01375169e-05j, 1.33779474e-03+2.64936269e-05j, -5.04687513e-04-1.14785598e-05j, ..., -1.89231039e-05+3.94031712e-07j, 5.47719410e-07-5.93547698e-07j, -2.13166913e-06-8.44838064e-07j]])} 'q': array([1,1,1,1,1...1,1,1])} """ 以上です
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【センリのラボノート①】自動生成した画像で自動返信【PythonでTwitterを自動化したよ】

 ステップ1「はじめに」  センリの道も一歩から!  みんなおはよう~、水彩センリだよ  今日からなんと、センリのラボノートを公開しちゃうよ  頑張って書いたから、しっかり読んでね!  この記事は   ステップ1「はじめに」…………開発環境や概略   ステップ2「何事も準備が大事!」…………使用する素材、ファイルの準備   ステップ3「いざコーディング」…………スクリプト   ステップ4「細かなとこにも手が届く~」…………スクリプトの説明   ステップ5「未来のために進歩あるのみ!」…………改善点の洗い出し  っていう進行をするから、よろしくね!!  第一回はこれ!  今日紹介するのは、センリが普段送ってる「画像あいさつ」だよ!  詳細はこんな感じ!  画像提供に協力してくれたのは柴犬のフラッフィーさん  映画の紹介が魅力的な配信者さん!  映画の同時視聴なんかもしてて、映画好きにはたまらない配信かも!  今月はサメ映画強化月間らしいから、サメが好きな人は特に見に行ってね~  →(https://www.youtube.com/channel/UCylf8QLkNLkSYBUAHcnzRTw)  ポイント  ・画像は自動生成  ・お名前部分だけ抽出  ・リプライ実行も自動  ラボの環境  センリの開発環境を説明するね Windows 10 Home Visual Studio Code Python 3.10.1 tweepy 4.5.0 Pillow 8.4.0  こんな感じ! 下の二つのモジュールはインストールして、  TwitterのAPI取得は各自終わらせちゃってね~  参考リンク  元になったのは@Hisan_twi さんのこの記事!  →PythonでTwitterの画像生成&自動返信botを作る  大きな変更点としては、モジュールをtwythonからtweepyにしたことと、改行を入れたところかなぁ  ステップ2「何事も準備が大事!」  画像は自動生成って書いてるけど、正確に言うとランダムに素材を組み合わせてるだけなんだ~  まずは素材の準備から!  画像素材の準備  まず背景、写真、吹き出しの三つが必要だよ  最低1枚ずつでも良いけど、たくさんあると嬉しいかなぁ  まずは背景、今回は1366 x 768 のサイズで作ってるよ~  他の素材も同じサイズにすると使いやすいね  これがセンリの写真~  なんだか恥ずかしいね  最後に吹き出しだね  写真と吹き出しは透過画像にしないとダメだからね!  フォントの準備  ここは参考記事と全く同じ状態だよ!  センリ、フォントはよく分からないの……   はなぞめフォント:https://www.asterism-m.com/font/hanazomefont/   アプリ明朝:http://flopdesign.com/blog/font/5852/   やさしさゴシック手書き:http://fontna.com/freefont/?p=40   あずきフォント:http://azukifont.com/font/azuki.html   うつくし明朝体:https://www.flopdesign.com/freefont/utsukushi-mincho-font.html   えり字:http://v7.mine.nu/pysco/gallery/font/06.html  リンクからダウンロードしたら、"Fonts"って名前のフォルダに入れてね  ディレクトリ  それじゃあ、ディレクトリがこんな感じになるようファイル作成&配置してね。 /Senri ├── reply-list.txt  (既返信リスト) ├── done-log.txt  (実行ログ) ├── auth.py      (APIファイル) ├── Functions.py (関数ファイル) ├── run.py       (実行ファイル) ├── run.bat (バッチファイル) └── /imgReply ├── /Fonts │ ├── UtsukushiFONT.otf │ ├── azuki.ttf │ ├── えり字.otf │ ├── アプリ明朝.otf │ ├── はなぞめフォント.ttf │ └── やさしさゴシック手書き.otf ├── /生成画像 └── /素材画像 ├── 背景_緑.png ├── 背景_水色.png ├── 背景_ピンク.png      ……(以下略)  ステップ3「いざコーディング!」  センリはAPIファイルと関数ファイル、実行ファイルの三つに分けて動かしてるよ  APIは大事に保管したいし、実行ファイルは簡潔にまとめたいからね~  説明用に用語を定義してるよ 【リプライ】  センリが受け取るもの 【返信】  センリが送るもの 【既返信リスト】  センリが返信し終わったツイートたち  →何度も返信しないようにリスト化してるよ  APIファイル  ここにTwitterAPIを入力するよ auth.py consumer_key="xxxxxxxxxxxxxxxxxxxxxxxxxx" consumer_secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" access_token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" access_token_secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  実行ファイル  実際に動かすのはこのスクリプトだよ。  後でバッチファイルを作成して、これを一定間隔で実行するの! Run.py from Functions import * if __name__ == '__main__': TLReply()  関数ファイル  中身はここに書き込むよ。  関数部分と実行部分を分けると、機能追加が楽なんだよ~  すっごく長いから折りたたんでるよ! 読むときは開いてね!!    関数ファイル、折りたたみ Functions.py import sys import time import datetime import random import re from PIL import Image, ImageDraw, ImageFont from logging import getLogger, StreamHandler, FileHandler, DEBUG, Formatter #今回のTwitterモジュール import tweepy #auth.pyに書いてるキーをimportします from auth import( consumer_key, consumer_secret, access_token, access_token_secret) #Tweepyのおまじないです auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) #ログ出力のおまじない logger = getLogger(__name__) handler1 = StreamHandler() handler1.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s")) handler2 = FileHandler("done-log.txt", encoding="utf-8-sig") handler2.setLevel(DEBUG) handler2.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s")) logger.setLevel(DEBUG) logger.addHandler(handler1) logger.addHandler(handler2) logger.propagate = False #君の名は。 my_name = "SuisaiSenri" def TLReply(): #同じツイートに何回も返信しないよう、既返信リストを取得 with open("reply-list.txt", mode='r', encoding="utf-8-sig") as f: reader = f.readlines() f.close() #1行ずつ格納します alreadylist = [usr.rstrip('\n') for usr in reader] #TLを取得 try: #TLの検索をします search = api.home_timeline(count=50, exclude_replies=True, tweet_mode='extended') except tweepy.errors.TweepyException as e: logger.error(e.msg) sys.exit(1) #検索結果から自分のツイート、リツイートを省きます result = [x for x in search if "RT @" not in x.full_text if x.user.screen_name != my_name] #古いツイートから順番に返信したいので、reversedを使います tweets = reversed(result) for tweet in tweets: #既返信リストの構え usr = tweet.user.screen_name + ":" + tweet.id_str #返信できそうな文言を探します。ここの書き方はもう少し賢い方法があるはず if "おはよ" in tweet.full_text or "おやす" in tweet.full_text or "寝る" in tweet.full_text: #既返信リストを参照 if usr not in alreadylist: try: if "おはよ" in tweet.full_text: imgReply(tweet, 1) elif "おやす" in tweet.full_text or "寝る" in tweet.full_text: imgReply(tweet, 2) except tweepy.errors.TweepyException as e: logger.error(e.msg) else: logger.info(f"返信しました:\n{tweet.full_text}") alreadylist.append(usr) #いいねします try: api.create_favorite(tweet.id) except: logger.info(f"いいねに失敗しました:\n@{tweet.user.screen_name}") time.sleep(random.uniform(6,10)) #既返信リストを書き出します。 with open("reply-list.txt", mode='w', encoding="utf-8-sig") as f: f.write('\n'.join(alreadylist)) f.close() #名前の加工部分 def nameProcess(name): name = name.rstrip('\n') r = r"[\((\[<\{<「『“【[〈{《〔‘].*?$" if re.sub(r, "", name) == "": r = r"\(.+?\)|(.+?)|\[.+?\]|<.+?>|\{.+?\}|<.+?>|「.+?」|『.+?』|“.+?”|【.+?】|[.+?]|〈.+?〉|{.+?}|《.+?》|〔.+?〕|‘.+?’" p = re.sub(r, "", name) r = r"[@@].*?$" if re.sub(r, "", p) == "": r = r"[@@]" p = re.sub(r, "", p) r = r"[//\||::].+?$" if re.sub(r, "", p) == "": r = r"[//\||::]" p = re.sub(r, "", p) r = r"[\u0000-\u007F\uFF01-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\u3000-\u303F\u3041-\u309F\u30A1-\u30FF\uFF66-\uFF9F\u2E80-\u2FDF\u3005-\u3007\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\U00020000-\U0002EBEF]+" m = re.search(r, p) if m: p = m.group() else: p = "" r = r"[!!]+" p = re.sub(r, "", p) r = r"bot$|Bot$|BOT$" p = re.sub(r, "", p) #それでも文字数が過剰な場合、空白以降も消します if len(p) > 18: r = r"[  ].+?$" p = re.sub(r, "", p) #名前が消滅している場合もあるので場合分け if p != "": #名前に最初から敬称が付いてる人はそのまま呼んであげます。他はさん付け r = r"たん$|さん$|ちゃん$|くん$|君$|様$" if re.search(r, p): title = "" else: title = "さん" p = p + title return p #画像de返信 class imgReply: #初期化。nはおはよう、おやすみ等の変化 def __init__(self, tweet, n): self.tweet = tweet self.n = n self.doReply() #返信操作の関数です def doReply(self): #ユーザーIDを取得します usr_screen_name = self.tweet.user.screen_name #ユーザー名を加工します name = nameProcess(self.tweet.user.name) #加工で名前が消滅した場合、表示名はIDを使用します #絵文字一個みたいなユーザー名を呼ぶ方法がないので、仕方ないです if name == "": name = usr_screen_name #ツイートに添付する画像を用意します imgpath = self.genImg(name) #リプライのためにはID部分の指定が必要 ID_text = "@" + usr_screen_name #リプライを送信します #送信部分。statusに本文を入れても良い。in_replyなんとかはリプライ先のツイートID api.update_status_with_media(status=ID_text, filename=imgpath, in_reply_to_status_id=self.tweet.id) #画像の生成部分です def genImg(self,name): #完成画像を置くフォルダです imgpath = "./imgReply/生成画像/" #写真の一覧です。付け足す際はここに追記します imgmat1_path = "./imgReply/素材画像/センリ_ジャンプ.png" imgmat2_path = "./imgReply/素材画像/センリ_デフォルト.png" imgmat3_path = "./imgReply/素材画像/センリ_ほほえみ.png" imgmat4_path = "./imgReply/素材画像/センリ_座り込み.png" imgmat5_path = "./imgReply/素材画像/センリ_猫.png" imgmat6_path = "./imgReply/素材画像/センリ_横向き.png" imgmat7_path = "./imgReply/素材画像/センリ_座り.png" #背景画像の一覧です bgmat1_path = "./imgReply/素材画像/背景_水色.png" bgmat2_path = "./imgReply/素材画像/背景_ピンク.png" bgmat3_path = "./imgReply/素材画像/背景_星.png" bgmat4_path = "./imgReply/素材画像/背景_花.png" bgmat5_path = "./imgReply/素材画像/背景_音符.png" bgmat6_path = "./imgReply/素材画像/背景_レース.png" bgmat7_path = "./imgReply/素材画像/背景_バラ.png" bgmat8_path = "./imgReply/素材画像/背景_緑.png" #吹き出し画像の一覧です baloon1_path = "./imgReply/素材画像/吹き出し_1.png" baloon2_path = "./imgReply/素材画像/吹き出し_2.png" baloon3_path = "./imgReply/素材画像/吹き出し_3.png" baloon4_path = "./imgReply/素材画像/吹き出し_4.png" #フォントの一覧です。この辺の記述、列挙以外の方法ないかな? font1_path = "./imgReply/Fonts/はなぞめフォント.ttf" font2_path = "./imgReply/Fonts/アプリ明朝.otf" font3_path = "./imgReply/Fonts/やさしさゴシック手書き.otf" font4_path = "./imgReply/Fonts/azuki.ttf" font5_path = "./imgReply/Fonts/UtsukushiFONT.otf" font6_path = "./imgReply/Fonts/えり字.otf" #それぞれをリスト化します imgmat_list = [imgmat1_path, imgmat2_path, imgmat3_path, imgmat4_path, imgmat5_path, imgmat6_path, imgmat7_path] bgmat_list = [bgmat1_path, bgmat2_path, bgmat3_path, bgmat4_path, bgmat5_path, bgmat6_path, bgmat7_path, bgmat8_path] font_list = [font1_path, font2_path, font3_path, font4_path, font5_path, font6_path] baloon_list = [baloon1_path, baloon2_path, baloon3_path, baloon4_path] #それぞれから使用する素材をランダムに選択します character = Image.open(random.choice(imgmat_list)).copy() bgimg = Image.open(random.choice(bgmat_list)).copy() baloon = Image.open(random.choice(baloon_list)).copy() #背景の上に貼り付けます bgimg.paste(baloon,(0,0),baloon) bgimg.paste(character,(0,0),character) #書き出します。描き出すと言うべき? draw = ImageDraw.Draw(bgimg) #ここからは画像に入れるセリフを用意します if self.n == 1: text = random.choice(["おはよう!", "おはよう~", "おはよ~"]) elif self.n == 2: text = random.choice(["おやすみ!", "おやすみ~", "おやすみなさい!"]) #名前が長い場合の折り返し地点などを計算しています。 #半角なら0.5文字として計算し、6字単位で折り返します row = 1 #行数 p = 0 #半角を調査 w = 0 #文字数カウント z = [0] #改行位置の記録 #~さんの直前までを計算対象に for x in name[:-2]: p += 1 w += 1 #英字のchrが65~122なので、それに該当する場合0.5字にする for y in range(65, 123): if chr(y) == x: p -= 0.5 break #半角12文字(p==6)を超えたら改行 if p >= 6 and w != len(name[:-2]): #改行位置を記録 z.append(w) p = 0 row += 1 #文字の初期位置を調整する値です #原点は左上で、varは鉛直方向(下)、horiは水平方向(右) var_sets = 230 hori_sets = 780 #ランダムなフォントを召喚! fonts = random.choice(font_list) #名前の長さに応じて文字の大きさを良い感じにします if p >= 6: font = ImageFont.truetype(fonts,50) else: font = ImageFont.truetype(fonts,60) #一行ずつ書き出します for k in range(row): #最終行でない場合の処理です if k+1 != row: #名前を一行ずつ表示、100ピクセル下に次の行を書きます draw.text((hori_sets, var_sets), name[z[k]:z[k+1]], fill = (0, 0, 0), font = font) var_sets += 100 #最終行はz[k+1]が存在しなくてエラーになるから場合分け else: draw.text((hori_sets, var_sets), name[z[k]:], fill = (0, 0, 0), font = font) var_sets += 100 #名前を書き終えたので本文を書いて終了です draw.text((hori_sets+100, var_sets), text, fill = (0, 0, 0), font = font) #画像の生成時間をファイル名としてローカルに保存します dt_now = datetime.datetime.now() imgname = imgpath + dt_now.strftime('%Y%m%d%H%M%S%f') + ".png" bgimg.save(imgname) return imgname      本当に長くてごめんねぇ、難しいところは今から解説するね!!  ステップ4「細かなとこにも手が届く~」  コードの解説をするね。 TLReply(). try: search = api.home_timeline(count=50, exclude_replies=True, tweet_mode='extended')  exclude_replies をTrueにしないと、みんなのリプライにも返信しちゃって大変だから気をつけてね。  tweet_modeをextendedにしないと、ツイート本文が120字くらいしか取得できないよ。  ただし、extendedにすると本文がfull_textって形になるから気を付けてね。普段はただのtextだよ。 TLReply(). result = [x for x in search if "RT @" not in x.full_text if x.user.screen_name != my_name] tweets = reversed(result)  内包表記で見づらいかもだけど、TLの検索結果からリツイート(本文が"RT @"で始まる)と、センリ自身のツイートを省いてるよ。  取得した検索結果は時系列が逆順になってるから、一応時系列順にしてるよ imgReply(). if name == "": name = usr_screen_name  絵文字だけの名前の人みたいに、上手く名前が取れない人がいるよ。  その場合はIDで呼んであげるんだ~ imgReply(). imgpath = self.genImg(name) ID_text = "@" + usr_screen_name api.update_status_with_media(status=ID_text, filename=imgpath, in_reply_to_status_id=self.tweet.id)  update_status_with_media()は画像付きツイートだよ。  statusで本文、filenameで画像パス、in_reply_to_status_idは返信先のツイートIDだね。  注意として、返信の本文は@(返信先ユーザーのTwitterID)を入れないといけないよ。 genImg(). character = Image.open(random.choice(imgmat_list)).copy() bgimg = Image.open(random.choice(bgmat_list)).copy() baloon = Image.open(random.choice(baloon_list)).copy() bgimg.paste(baloon,(0,0),baloon) bgimg.paste(character,(0,0),character)  bgimg.pasteでbgimg(背景画像)の上に画像を貼り付けるよ。  第一引数は貼り付ける画像、第二引数は座標(左上が(0,0))。  第三引数は画像のトリミングだね~。正方形にしたり円形にしたりできるけど、そのものを渡すと画像の透明部分をトリミングしてくれるよ。 genImg(). row = 1 #行数 p = 0 #半角を調査 w = 0 #文字数カウント z = [0] #改行位置の記録 for x in name[:-2]: p += 1 w += 1 #英字のchrが65~122なので、それに該当する場合0.5字にする for y in range(65, 123): if chr(y) == x: p -= 0.5 break if p >= 6 and w != len(name[:-2]): #改行位置を記録 z.append(w) p = 0 row += 1  ここは改行の準備だよ。  参考元の記事では扱ってなかったからセンリが考えたけど、あまり上手なコードじゃないかも。。。  まずpが、半角を0.5とした文字数カウントだよ。  半角12文字(p==6)を超えたら改行したいの~。  何文字目で改行したのか(z)、累計何行なのか(row)の情報を残してるよ。 genImg(). for k in range(row): if k+1 != row: draw.text((hori_sets, var_sets), name[z[k]:z[k+1]], fill = (0, 0, 0), font = font) var_sets += 100 else: draw.text((hori_sets, var_sets), name[z[k]:], fill = (0, 0, 0), font = font) var_sets += 100 draw.text((hori_sets+100, var_sets), text, fill = (0, 0, 0), font = font)  さっき記録したrowの値についてfor文を回していくよ。  お名前が3行なら、3回に分けて出力する感じだね~  自動実行  出来上がったら、Run.pyを自動で実行するバッチファイルを準備するよ。  タスクスケジューラで数分おきに実行したら完成~  ただ実行するだけでも良いんだけど、ウィンドウが毎回出てくるのはイヤだから最小化状態で実行してもらうね Run.bat @echo off cd /d %~dp0 :最小化状態で実行する if not "%X_MIMIMIZED%"=="1" ( set X_MIMIMIZED=1 start /min cmd /c,"%~0" %* exit ) cd C:\Users\...\Senri python run.py exit /b  ステップ5「未来のために進歩あるのみ!」  いくつかまだまだ直せるところはあると思うの!  その1  画像素材を列挙してるけど、もっとスマートな書き方がありそう~  今は素材が少ないから良いけど、100枚ずつ用意したら大変かも  別のテキストファイルに書き込むとかかなあ? genImg(). imgmat1_path = "./imgReply/素材画像/センリ_ジャンプ.png" imgmat2_path = "./imgReply/素材画像/センリ_デフォルト.png" imgmat3_path = "./imgReply/素材画像/センリ_ほほえみ.png" imgmat4_path = "./imgReply/素材画像/センリ_座り込み.png" imgmat5_path = "./imgReply/素材画像/センリ_猫.png" imgmat6_path = "./imgReply/素材画像/センリ_横向き.png" imgmat7_path = "./imgReply/素材画像/センリ_座り.png" imgmat_list = [imgmat1_path, imgmat2_path, imgmat3_path, imgmat4_path, imgmat5_path, imgmat6_path, imgmat7_path]  その2 名前の操作!  これ、敬称を「さん」を前提として書いてるけど、ちゃんだったら困りそう…… for x in name[:-2]: p += 1 w += 1  その3 これはよくないんだよ~  採用したフォントが常用外の漢字に対応していない場合があって、こんな風に虫さんに食べられちゃうの…… 画像で書きたかったのは呂色 朧さん! 朧さんは関西のお山から来た鬼狐! ご自身で作ったアバターが生き生きと喋る、期待の超新星なんだあ~ →(https://www.youtube.com/channel/UCXb-OHQHFOvhZqPZ3Sy2kZg)  その4 気にした方が良いかも  センリ、この頃Twitter運営さんからシャドウバンっていう措置を取られちゃうの……  どれが原因か分からないけど、もしかしたら返信しすぎてるのかも!?  送信をフォロワーさんだけに限定するとか、一度の検索数を調整するとか、同じ人について一日に一回しか送らないようにするとか、そういう対応が必要なのかなぁ……  その5 みんなの声  コードの間違い、改善案があったらコメントで指摘してくれると成長に繋がって嬉しいんだぁ  逐一修正していきたいから、どしどし指摘してね!  次回予告  ラボノートを公開し、返信に使っている画像が実は自動生成だと周知させたセンリ!  しかし、水彩ラボと呼ばれる謎の研究所から重大なミスを指摘されてしまう!!  水彩ラボとは何なのか、センリの犯した重大なミスとは!?  次回「そんなことよりwordleであそぼう!」   現在テスト稼働中のごはんwordle機能について紹介しちゃうよ、次回も見てねっ!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonの関数をGUIで管理、テスト可能なOSSライブラリ「Logicbook」を開発した話

Logicbookとは LogicbookはPythonの関数を独立してGUI上で管理・テストするためのオープンソースライブラリです。 Reactのコンポーネント管理ライブラリ「Storybook」のPython関数版のようなイメージです。 Reference 詳しいドキュメントはこちらにあるので興味がある方は一度使ってみてください。 Githubはこちら https://github.com/RuiHirano/logicbook 開発しようと思ったきっかけ 現在。私は3年ほど大学発ベンチャー会社のソフトウェア開発に関っており、Webアプリを5人ほどのチームで開発しています。Webアプリは基本的にfrontendはreactを用いてbackendのAPIにはpythonを使用してます。 初めの方はfrontend,backendともにどちらも最小限のテストコードしか書いておらず、それでも保守管理にはそこまで問題はなかったのですが、規模が大きくなるにつれてバグだったりエラーが多発するようになっていました。 また、チームの人数も少しずつ増えると実装する機能をそれぞれに分散するため、チーム内のプログラミング能力の差によってコードの書き方に大きく違いがでてしまうという問題もありました。しかも、新しい人が加わると既存コードの説明をするために何回も打ち合わせを繰り返しておりサポートに大きな時間を割かれていました。 こういった経緯から 「既存コードをうまく説明可能な状態で保守管理できる方法」 はないのかなと思い始めました。 Reactについては「Storybook」というOSSでコンポーネント管理ツールがあり、とても便利に活用しています。 しかし、Pythonに関しては調べてもそのようなライブラリが見当たりませんでした。 そこで、どうせなら自身でOSSとして開発しようと思い、Logicbookの開発を始めました。 Logicbookは「Storybook」を意識していて、開発においてかなり参考にしています。 現在はまだβ版ですが、今後も開発を続け正式リリースできることを目標としています。 Logicbookでできること GUIでできることはREADMEやソースコードの確認、関数の実行、テストの自動実行などいくつかあります。 Example Exampleの項目では実際の関数に対して任意の入力を行いどのような出力がされるかを確認できます。複雑な関数でも一つ一つの関数の入力と出力のみを意識するだけで扱えるため、挙動を理解しやすくなります。 Test Testの項目ではファイルに変更が加わり保存されたタイミングで自動的にテスト実行されます。(GUIの「RUN TEST」をクリックしても実行できます。) テスト結果に応じて色分けされて表示されるので、視覚的にわかりやすいと思います。 Tutorial in less than 5 minutes. 最新のpip3系であればpypiからインストールできます。 pip install logicbook 初期化 cd YourProject logicbook init # Your Project Name: [プロジェクト名] # Your Src Path: [srcまでの相対ディレクトリパス] Src Pathはlogicsというlogicbookで管理する設定を記述するためのフォルダを作成するパスです。 デフォルトは/でプロジェクトルート直下に作成されます。srcなどの下の階層に作成したい場合は「src」などと相対パスで入力してください。 サーバーの立ち上げ cd YourProject logicbook start 上記のコマンドの後、http://localhost:8000 を表示してください。Logicbook UIが表示されていれば成功です。 その他、詳しい方法についてはドキュメントを参照してください。 おわりに Pythonで関数を独立させて管理、テストできるOSSライブラリは今まであまりなかったかと思います。 Pythonはわかりやすく書きやすい反面、厳密な型定義がなくtestを書く人も少ないためバグが発生しやすい言語でもあります。将来的にはLogicbookで一つ一つのロジックを管理しながら開発していくのが当たり前になり、コード管理の一つのアプローチになればいいと考えてます。 まだβ版で正式リリースに向けて開発中なので、興味ある方は一度使ってみて、意見や要望などをいただけたら幸いです。 また、コントリビューターとして一緒に開発していただけるメンバーも募集しているので参加してもらえると嬉しいです。 Logicbook開発者: 平野 流
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Box-Cox変換を含んだモデルをベイズ推定する

やりたいこと Box-Cox変換は以下の式で定義される変数変換で対数変換の一般化だそうです。($\lambda$->0の極限で対数変換になる) \mathrm{BoxCox}(y|\lambda) = \frac{y^\lambda - 1}{\lambda} この変換を行うことで正の実数が実数全体に写像されます。したがって変数が正の実数で正規分布が使用できない場合にでもこの変換を使用することで正規分布に近づけることができます。 Box-Cox変換は式の通りパラメータ$\lambda$を含んでおり、この$\lambda$もデータに依存してチューニングするべきものです。ハイパーパラメータとして探索してもいいですし、SciPyに実装されているように線形モデルを仮定した場合には他のモデルパラメータに依存せずに最尤推定することができますが、ここでは$\lambda$も含めてベイズ推定する方法を実装してみます。 モデル \begin{align} z & = \mathrm{BoxCox}(y|\lambda) \\ z & \sim \mathcal{N(z|\mathbf{w}^T\mathbf{x}, \sigma^2)} \end{align} 上記のように$y$ではなく、Box-Cox変換した$z$が正規分布に従っているという線形回帰モデルを考えます。このモデルにおいて$y$を変数に取った尤度は単純な変数変換だけではなく、次のようにJacobianを含んだ形になります。 \begin{align} P(y|\mathbf{x}, \mathbf{w}, \sigma^2, \lambda) & = P(z|\mathbf{x}, \mathbf{w}, \sigma^2, \lambda)\frac{dz}{dy} \\ & = \mathcal{N(z|\mathbf{w}^T\mathbf{x}, \sigma^2)} y^{\lambda - 1} \\ \ln P(y|\mathbf{x}, \mathbf{w}, \sigma^2, \lambda) & = \ln \mathcal{N(z|\mathbf{w}^T\mathbf{x}, \sigma^2)} + (\lambda-1)\ln y \end{align} Stanによる実装 これをStanで実装すると以下の通りになります。 functions { /* ... function declarations and definitions ... */ vector boxcox(vector y, real lambda, int N) { vector[N] z; real inv_lambda = 1.0 / lambda; for (i in 1:N){ z[i] = (pow(y[i], lambda) - 1.0) * inv_lambda; } return z; } vector inv_boxcox(vector z, real lambda, int N) { vector[N] y; real inv_lambda = 1.0 / lambda; for (i in 1:N){ y[i] = pow((z[i] * lambda + 1.0), inv_lambda); } return y; } } data { /* ... declarations ... */ int N; int N_new; int D; matrix[N, D] X; vector[N] y; matrix[N_new, D] X_new; real<lower=0> w_s; real<lower=0> sigma_beta; real<lower=0> lambda_s; } parameters { /* ... declarations ... */ vector[D] w; real<lower=0> sigma; real lambda; } transformed parameters { /* ... declarations ... statements ... */ vector[N] z; z = boxcox(y, lambda, N); } model { /* ... declarations ... statements ... */ w ~ normal(0.0, w_s); sigma ~ exponential(1.0 / sigma_beta); lambda ~ normal(1.0, lambda_s); z ~ normal(X * w, sigma); target += (lambda - 1.0) * log(y); } generated quantities { /* ... declarations ... statements ... */ vector[N_new] yp; if (N_new > 0){ yp = inv_boxcox(to_vector(normal_rng(X_new * w, sigma)), lambda, N_new); } } ポイントとしては、Stanの自動微分は独立変数の方しか面倒を見てくれないので従属変数($y$)の方の変数変換は自分でJacobiantarget +=の文法で足してあげる必要があるという点です。$\lambda$は中心が1.0の正規分布を事前分布に設定しています。これは$\lambda=1.0$の時は定数項を足している以外は何もしていない変換になるのでそれを中心にしたかったからです。X_new,N_newやgenerated quanitiesはパラメータ推定とは関係ないですが、推定したパラメータのサンプルを使用して未知データに対する予測を行うときに使用します。 実験 出力がPoisson分布の線形モデルのテストデータを作成し、Poisson回帰の代わりにBox-Cox変換を含んだ線形回帰モデルで係数を推定してみます。 \ln h = 5.0 - 1.2 x \\ y \sim \mathrm{Po}(y|h) N = 50 D = 2 w = np.array([5.0, -1.2]) X = np.random.randn(N, D - 1) * 0.1 X = np.column_stack([np.ones(N), X]) h = np.exp(np.dot(X, w)) y = np.random.poisson(h) model = BayesianBoxCoxLinearRegression( w_s=2.0, sigma_beta=2.0, lambda_s=2.0, n_warmup=500, n_samples=1000, n_chains=4, ) model.fit(X, y) model.summary() Mean MCSE StdDev 5% 50% 95% N_Eff N_Eff/s R_hat name lp__ -150.000 0.0850 1.70 -150.000 -150.000 -150.000 420.0 81.0 1.0 w[1] 4.800 0.0720 1.10 3.200 4.600 6.900 250.0 48.0 1.0 w[2] -1.100 0.0350 0.55 -2.200 -0.990 -0.430 245.0 47.0 1.0 sigma 0.100 0.0000 0.00 0.000 0.100 0.100 245.9 47.6 1.0 lambda -0.000 0.0000 0.10 -0.200 -0.000 0.100 266.6 51.6 1.0 $\lambda$ = 0 なので対数変換になり、$\mathbf{w}$も(5.0, -1.2)に近い値が予測できています。 NumPyroによる実装 import jax.numpy as jnp import numpyro as pyro import numpyro.distributions as dist from jax import grad, random, vmap def boxcox(y: float, lam: float): return (y**lam - 1.0) / lam def inv_boxcox(z: float, lam: float): return (z * lam + 1.0) ** (1.0 / lam) trans_grad = vmap(grad(boxcox), (0, None, 0, None)) def model( X: jnp.ndarray, y: Optional[jnp.ndarray] = None, w_s=2.0, sigma_beta=2.0, lambda_s=1.0, ): n, d = X.shape with pyro.plate("d", d): w = pyro.sample("w", dist.Normal(0.0, w_s)) sigma = pyro.sample("s", dist.Exponential(sigma_beta)) z = jnp.dot(X, w) lam = pyro.sample("lambda", dist.Normal(1.0, lambda_s)) if y is not None: yl = transform(y, lam, z, sigma) grad_factor = trans_grad(y, lam, z, sigma) pyro.factor("", jnp.log(grad_factor).sum()) with pyro.plate("n", n): pyro.sample("yl", dist.Normal(z, sigma), obs=yl) else: with pyro.plate("n", n): yl = pyro.sample("yl", dist.Normal(z, sigma)) return pyro.deterministic("y", inv_boxcox(yl, lam)) 基本的にはStanと同等ですが、JAXの自動微分gradを使ってBox-Cox変換の微分を計算し、vmapでベクトル化している点だけが異なります。もちろんStanの時のように解析的に求めた微分を使用してもいいですが、自動微分を利用することで今後Box-Cox変換以外にもいろいろな変換にも使えます。 その他 コード全体は https://github.com/lucidfrontier45/BaysianBoxCoxLM にあります。StanはPyStanではなくCmdStanPyを使用しています。Stanの場合はgenerated quantites、NumPyroの場合はnumpyro.infer.Predictiveを使用して事後予測分布を生成する機能も付けています。また、全体をBayesianBoxCoxLinearRegressionというクラスでまとめ、使用するbackendとしてCmdStanPyとNumPyroを切り替えられるようにしています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jupyterlabとdashは開発の相性が悪い

本当はdashでドロップダウンリストとかを作ってOLAP的なことをしたかったのですが、いろいろやってみて挫折した感じになります、、、。 やり終わった後「誰かに止めてほしかったなぁ」と思ったので記事として公開しておきます。 検討していた要件 Notebookにdashアプリを追加し、ダッシュボードのようにパラメターを変えながら10個ぐらいのグラフの変化を見たい 上記のグラフのセットが複数存在する(同時に眺める必要はない) Jupyter labはリモートアクセスしたい、セキュリティ要件は以下 https sudoできない専用ユーザー 共通パスワードもしくはPAM なぜ相性が悪いか できないことはないが本質的な作業以外で細かい設定が湧いてきて効率が悪い というイメージです。 開発のUXとして効率が悪い jupyter labでdashのアプリ開発を行ううえで、ソースとinline化したアプリのウィンドウのレイアウト作業が毎回発生 dashのコードは結構長い事が多く、Notebookで気持ちよく編集できない Webアプリのような開発スタイルでVSCodeでソースを編集しながら別ウィンドウでdashをテストするのが素直 jupyter lab に dashを組み込むには修正が必要で、サンプルをコピペしてきた時に毎回書き直しが発生する from jupyter_dash import JupyterDash の追加 app = JupyterDash(__name__) への変更 app.run_server(mode='inline') への変更 複数のセルに複数のdashアプリを配置しようとするのが面倒 複数のdashアプリには複数ポートが必要なため app.run_server()でport=XXXX の指定が個別に必要 jupyter labのextensionのインストール・設定の情報にたどり着くまでが遠い V3系でやり方変わっていたりとかで変な記事見るとハマります リモートアクセスする時の設定変更 app.run_server()でhost='0.0.0.0'指定 リモートアクセスをSSL化した時のdash側のSSL対応(flaskのSSL接続化)←未解決 代替策は? パラメターの変更をUIで行うことを諦め、普通に変数定義にしてデータの変化を画面上で確認する ダッシュボードっぽく見るためにdashのレイアウト機能(html.Div([dcc.Graph...)などは使わず、make_subplotを使用する 感想 職業柄、自分で分析した後にデータ分析にあまり詳しくないメンバーにデータを見せることが多いので最初からdashでやってしまえと思ったのですが、dashはdashと割り切ったほうが余計な環境構築とかでエネルギー使わなくて良さそうです、、。ここらへんRだとRstudioでshiny開発まですんなりできるのですが、Pythonはそれぞれの開発元が独立しているのであまり連携させず使っていくのが良いのかもしれませんね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC244 A~E問題 ものすごく丁寧でわかりやすい解説 python 灰色~茶色コーダー向け #AtCoder

ABC244(AtCoder Beginner Contest 244) A~E問題の解説記事です。 灰色~茶色コーダーの方向けに解説しています。 その他のABC解説、動画などは以下です。 A - Last Letter Sを受け取って末尾の文字を出力します。 Sの末尾、つまり右から1文字目はS[-1]と書けば確認できます。 よってS[-1]を出力すればOKです。 入力の受け取り、出力がわからない方は以下の記事を参考にしてください。 【提出】 # 入力の受け取り N=int(input()) S=input() # Sの右から1文字目を出力 print(S[-1]) B - Go Straight and Turn Right 今いる座標と向いている方向を管理します。 今いる座標が(x,y)である場合 東向きに進む:(x+1,y) 南向きに進む:(x,y-1) 西向きに進む:(x-1,y) 北向きに進む:(x,y+1) となるのでそれぞれ条件分岐をすればOKです。 「R」が来たら向きが変わるのでそれも条件分岐を使って行います。 【提出】 # 入力の受け取り N=int(input()) T=input() # 方向:最初は東 direct="East" # 今いる座標 now=[0,0] # i=0~(N-1) for i in range(N): # T[i]=「S」のとき if T[i]=="S": # 今のx座標 now_x=now[0] # 今のy座標 now_y=now[1] # 東向きの場合 if direct=="East": now=[now_x+1,now_y] # 南向きの場合 elif direct=="South": now=[now_x,now_y-1] # 西向きの場合 elif direct=="West": now=[now_x-1,now_y] # 北向きの場合 elif direct=="North": now=[now_x,now_y+1] # T[i]=「R」の場合 else: # 方向を変える if direct=="East": direct="South" elif direct=="South": direct="West" elif direct=="West": direct="North" elif direct=="North": direct="East" # 答えの出力 print(now[0],now[1]) C - Yamanote Line Game Nが小さいので、入力を受け取る度まだ使っていない整数を確認して出力すればOKです。 usedという配列を用意し、最初はすべて「False」にしておきます。 一番最初は「1」を宣言し、「1」を使用済み⇔used[1]=Trueに変更します。 青木くんの宣言した数字を受け取ったらそれもusedをTrueに変更します。 used[1],used[2],...についてまだFalseのものを探し、Falseがあればそれを宣言し、Trueに変更します。 青木くんから「0」が入力されたらプログラムを終了します。 『出力を行うたびに標準出力をflushしてください。』とありますがpythonでは特に気にしなくても問題ありません。 【提出】 # 入力の受け取り N=int(input()) # すでに使った数の記録 used=[False]*(2*N+2) # 最初は「1」を出力 print(1) # 「1」は使用済み used[1]=True # N回 for i in range(N+1): # 青木くんの入力を受け取り x=int(input()) # 「x」は使った used[x]=True # x=「0」の場合 if x==0: # 終了 exit() # k=1~(2k+1) for k in range(1,2*N+2): # まだ使っていないなら if used[k]==False: # 「k」を出力 print(k) # 「k」は使った used[k]=True # forループを抜ける break D - Swap Hats S,Tのパターンは以下の3種類です。 (1)3色すべて同じ位置 (2)1色のみ同じ位置で2色は逆 (3)すべて違う位置 (1)は入力例の通り、どれか2つを選んで10^18回操作を行えばS,Tを同じ並びにできます。 (2)は同じ並びにできません。 (3)適切な入れ替えを2回行うことで(1)と同じパターンにできます。よってS,Tを同じ並びにできます。 (2)が達成不可能なことは公式解説のように考えることで証明できますが、コンテスト中は「このパターンはなんとなく無理そうだなー」程度でとりあえず提出してしまえばいいでしょう。 【提出】 # 入力の受け取り S1,S2,S3=map(str,input().split()) T1,T2,T3=map(str,input().split()) # (1)3色すべて同じ位置 if S1==T1 and S2==T2 and S3==T3: print("Yes") # (2)1色のみ同じ位置で2色は逆 elif S1==T1 or S2==T2 or S3==T3: print("No") # (3)すべて違う位置 else: print("Yes") E - King Bombee この問題はDP問題の中でも特に難易度が高いです。DPをあまり解いたことがない人は先に簡単なDP問題を解いてみることをおすすめします。 以下が比較的簡単なDP問題です。 ABC211C:https://atcoder.jp/contests/abc211/tasks/abc211_c 解説:https://qiita.com/sano192/items/051207b6607b56cc439e#c---chokudai ABC222D:https://atcoder.jp/contests/abc222/tasks/abc222_d 解説:https://qiita.com/sano192/items/e42b9861ece2c339394a#d---between-two-arrays その他のDP問題も演習したい人は「ものすごく丁寧でわかりやすいABC解説一覧」の「逆引き DP」を御覧ください。 https://qiita.com/sano192/items/54accd04df62242b70f0#%E9%80%86%E5%BC%95%E3%81%8D DPで解きます。 DPとは「ある状態までの答えがわかっていれば→その次の状態の答えも出せる」という手続きを何度も行って最終的な答えを出す方法です。 具体的な手順は以下です。 (1)表を作る (2)すぐにわかるところを埋める (3)表の小さい方から答えにたどり着くまで埋める (4)答えを出力する DPの解説動画を作りましたので、本問が難しいと感じた方は是非御覧ください。 https://www.youtube.com/watch?v=gVJ16ThsJYs 入力例1を例として考えましょう。 N M:4 4 K:4 S T:1 3 X:2 U1 V1:1 2 U2 V2:2 3 U3 V3:3 4 U4 V4:1 4 グラフは以下のようになります。 (1)表を作る 今回作る表は「i回の移動でjにたどり着く方法のうち、Xを偶数回/奇数回通る方法の数」とします。 表の名前はdpとします。(dp[i][j][偶/奇]) 例えばdp[2][3][偶]であれば「2回の移動で③にたどり着く方法のうち、②を偶数回通る方法の数」となります。 2回の移動で③にたどり着く方法は ①→②→③ ①→④→③ の2通りでそのうち②を偶数回(0回)通っているのは①→④→③のみなのでdp[2][3][偶]=1となります。 最終的には「K回の移動でTにたどり着く方法のうち、Xを偶数回通る方法の数」が答えなのでdp[K][T][偶]が埋まれば終わりです。 (2)すぐにわかるところを埋める すぐに分かるところは移動回数が0、すなわち0行目です。 0回の移動で到達できるのはスタート地点であるSのみです。また、X≠Sという条件があるのでXを通る回数は必ず0回=偶数になります。 よってdp[0][S][偶]=1となります。 0行目の他の箇所は到達しようがないのですべて0になります。 (3)表の小さい方から答えにたどり着くまで埋める 1回移動してたどり着ける場所を考えましょう。 0回の移動で①にたどり着けることはわかりました。①から行ける場所は②、④の2箇所です。 ・①→②:0回の移動で①にたどり着く方法は1通りでした。よってそこから②にたどり着く方法も1通りです。更に②を一度通るので②を通る回数が1回増え、奇数回となります。まとめるとdp[1][2][奇]=1となります。 ・①→④:同様に④にたどり着く方法も1通りです。②は通らないので②を通る回数は変わらず0回となります。まとめるとdp[1][4][偶]=1となります。 この移動を一般化して考えてみましょう。 UとVが行き来可能な場合、(i-1)回の移動でUにたどり着く方法の数がわかっていれば、そこから1回移動してVにたどり着く方法は単純に足し算をすればよいです。 この時、行き先V=XであればXを通る回数が増える=偶奇が入れ替わります。そうでない場合は偶奇は同じです。 V=Xの場合 dp[i][V][偶]+=dp[i-1][U][奇] dp[i][V][奇]+=dp[i-1][U][偶] V≠Xの場合 dp[i][V][偶]+=dp[i-1][U][偶] dp[i][V][奇]+=dp[i-1][U][奇] これをi=1~Kについて、すべてU,Vの組(U→V,V→U)について計算します。 (4)答えを出力する 表をすべて埋めると以下のようになります。 答えはdp[K][T][偶]=dp[4][3][偶]=4になります。 実装については3次元の配列が必要です。3次元配列は以下のように作ります。 dp=[[[0,0] for i in range(N+1)] for j in range(K+1)] dp[1番目の要素][2番目の要素][3番目の要素]としたいなら dp=[[[0]*(3番目の要素数) for i in range(2番目の要素数)] for j in range(1番目の要素数)] というように並びが逆になると覚えておけばよいです。 説明ではdp[i][j][偶],dp[i][j][奇]というようにしましたが、実装では偶=0,奇=1としています。 pythonでは間に合わないのでpypyで提出します。 【提出】 # pypyで提出 # 余りの定義 mod=998244353 # 入力の受け取り N,M,K,S,T,X=map(int,input().split()) # (1)表を作る # 「i回の移動でjにたどり着く方法のうち、Xを偶数回/奇数回通る方法の数」 dp=[[[0,0] for i in range(N+1)] for j in range(K+1)] # 辺の格納 edge=[] # M回 for i in range(M): # 入力の受け取り U,V=map(int,input().split()) # 辺の記録 edge.append([U,V]) # (2)すぐにわかるところを埋める dp[0][S][0]=1 # (3)表の小さい方から答えにたどり着くまで埋める # i=1~K for i in range(1,K+1): # 各辺の組について for U,V in edge: # U→V # V=Xの場合⇔Xを通る回数が増える⇔偶奇が入れ替わる if V==X: dp[i][V][0]+=dp[i-1][U][1] dp[i][V][1]+=dp[i-1][U][0] # V≠Xの場合⇔Xを通る回数が増えない⇔偶奇は同じ else: dp[i][V][0]+=dp[i-1][U][0] dp[i][V][1]+=dp[i-1][U][1] # 余りを取る dp[i][V][0]%=mod dp[i][V][1]%=mod # V→U # U=Xの場合⇔Xを通る回数が増える⇔偶奇が入れ替わる if U==X: dp[i][U][0]+=dp[i-1][V][1] dp[i][U][1]+=dp[i-1][V][0] # U≠Xの場合⇔Xを通る回数が増えない⇔偶奇は同じ else: dp[i][U][0]+=dp[i-1][V][0] dp[i][U][1]+=dp[i-1][V][1] # 余りを取る dp[i][U][0]%=mod dp[i][U][1]%=mod # (4)答えを出力する print(dp[K][T][0]) 【広告】 『AtCoder 凡人が『緑』になるための精選50問詳細解説』 AtCoderで緑になるための典型50問をひくほど丁寧に解説した本(kindle)、pdf(booth)を販売しています。 値段:100円(Kindle Unlimited対象) 【kindle】 【booth(pdf)】 1~24問目まではサンプルとして無料公開しています 『AtCoder ABC201~225 ARC119~128 灰・茶・緑問題 超詳細解説』 ABC201~225、ARC119~128 の 灰・茶・緑DIfficulty問題(Dif:0~1199) を解説しています。 とにかく 細かく、丁寧に、具体例を豊富に、実装をわかりやすく、コードにコメントをたくさん入れて 解説しています。 サンプルを5問分公開しています Qiitaにて無料公開している『ものすごく丁寧でわかりやすい解説』シリーズをベースにしていますが、 【キーワード】【どう考える?】【別解】を追加 し、 【解説】と【実装のコツ】を分ける ことでよりわかりやすく、 具体例や図もより豊富に 書き直しました。 Qiitaには公開していない ARC119~128の灰・茶・緑DIfficulty問題も書き下ろし ています。 値段:300円(Kindle Unlimited対象) 【kindle】 【booth(pdf)】 ARC119~128の部分のみ抜粋した廉価版 もあります。 値段:200円(Kindle Unlimited対象) 【kindle】 【booth(pdf)】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Blenderのドライバーの使い方

概要 Blenderのドライバーの使い方を紹介します。 ドライバーとは、あるオブジェクトのプロパティを別のオブジェクトから参照するしくみです。 ドライバーを使うとアニメーションの設定などが便利になります。 以降では具体例で説明します。 対象オブジェクト 下図のようなオブジェクトを用意してください。 車体(名前はbodyとします) 前輪(名前はtire_fとします) 後輪(名前はtire_rとします) ここから、車体を動かした時に、自動的に車輪が回転する方法を説明します。 まず、前輪と後輪の親を車体にしましょう。前輪、後輪、車体の順に選択し、Ctrl + P(ペアレント)のオブジェクトを選択します。 これにより車体を動かすと車輪も一緒に動きます。 ドライバーの設定 車体は、y軸の負の方向を向いているとします。ここでは、車体のy座標を使って、前輪のx軸を回転させます(回転させるべき軸はモデルによって異なります)。 まず、車体のy座標のドライバーを作成しコピーします。 車体を選択 N(サイドバー)でサイドバーを表示し、アイテムタブを選択 位置Yで右クリックし「新規ドライバーとしてコピー」を選択 続いて前輪を選択し、サイドバーの回転Xの位置で右クリックし「ドライバーを貼り付け」を選択します。ドライバーを貼り付けると、下図のように紫色になります。 車体を動かしてみましょう。前輪が回転します。しかし、動きがゆっくりですし方向も逆回転です。 ドライバーの編集 ドライバーはスカラーの変数として使えます。 位置Yで「新規ドライバーとしてコピー」すると変数名はlocationになります(変更も可能ですが、ここではそのまま使います)。 今回前輪の高さ(直径)は0.333mとします。このとき円周はπR = π/3です。 また、ドライバーの値を設定する回転の値の単位は度でなくラジアンにしないといけません。 このことから、π/3進んだら-2π回転させる必要があります。 すなわち、locationだけ進んだら、回転は-6 * locationになります。 ドライバーの値を変えるには、前輪を選択して回転Xで右クリックし「ドライバーの編集」を選択します。下図のような画面が表示されます。 ※ 「ドライバーの編集」を使うには、ドライバーが設定されている必要があります。 主な項目を見てみましょう。 ドライブ対象プロパティ:対象オブジェクトとそのプロパティです。ここでは、対象オブジェクトがtire_fで、プロパティがXオイラー角回転ということがわかります。 タイプ:ドライバーの値の種類です 入力値(「入力値を追加」の下の枠内) (項目なし):変数名(locationが変数名) プロパティ:変数の元になるオブジェクト パス:変数の値を表すもの。ここでは、オブジェクトのlocation[1]であるy座標になります。 これらのことから、車体(body)のy座標(location[1])を、ドライバーのlocationとして使えることがわかります。 この状態では、前輪の回転Xの値としてlocationが設定されています。値には式を設定できます。そのためには、タイプを「スクリプト型の式」に変えます。 変えると「式」という項目があるので、-6 * locationに変えます。下図のようになります。 ※ 式にはPythonの式が書けます。 アニメーションをレンダリングして確認すると、下図のようにそれらしく前輪が回転しています。 後輪の設定 後輪も動かしてみましょう。 前輪の回転Xから「新規ドライバーとしてコピー」します。 後輪の回転Xに「ドライバーを貼り付け」します。 これで前輪と後輪が同じように回転します。 ちなみに、ドライバーを削除する場合は、対象を右クリックして「ドライバーを削除」を選びます。 パスアニメーション ここまでは、車体がy軸の方向に動く場合の設定をしました。 ここからは、車体が何らかのパスに沿って動く場合の設定を説明します。 まずは、パスとなるカーブを作成しましょう。カーブの種類は何でもいいのですが、ここではベジエを作成してください。 作成したら、名前をpathにして適当に編集してください。 車体、パスの順に選択し、Ctrl + P(ペアレント)の「パス追従コンストレイント」を選びます。 車体のオブジェクトコンストレイントプロパティを以下のように設定します。「前方の軸」が-Yに、「カーブに従う」がオンになっています。 ※ コンストレイントの設定については、記事「Blenderで待ち行列」も参考にしてみてください。 上図のオフセットを0から-100にすることで、パス上を移動することになります。 したがって、下記の設定をすれば、パスアニメーションで車輪が動くことになります。 時刻を開始位置にします。オフセット上でiを押し、キーフレームを設定します。 時刻を終了位置にします。オフセットを-100にし、オフセット上でiを押し、キーフレームを設定します。 オフセットを右クリックし「新規ドライバーとしてコピー」します。 ドライバーの調整をしましょう。 メジャーなどを使ってパスの長さを推定してください。ここでは10mとします。 また、1m進んだら、-6になるようにします。 このことからドライバーには、「-6 * オフセット / -100 * 10」を設定します。 オフセットの変数はoffsetなので、ドライバーの編集で-6 * offset / -100 * 10を指定します。 ドライバーを使った例の説明は以上です。 補足 車体が一定の速度で動くのであれば、ドライバーを使わずに別々に設定しても問題ありません。 しかし、車体の速度が一定でないならば、別々に設定するのは手間がかかりますし、間違いの元になります。 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python GitHubトレンドデイリーランキング!!【自動更新】

GitHub Trending をキャッチアップする習慣をつけて、強強エンジニアになろう。 この記事では、Python のGithubのトレンドデイリーランキングを25位まで紹介します。 トレンドデイリーランキング 【1 位】 microsoft/routeros-scanner ? 472 star Tool to scan for RouterOS (Mikrotik) forensic artifacts and vulnerabilities. 【2 位】 lxgr-linux/pokete ? 460 star A terminal based Pokemon like game 【3 位】 public-apis/public-apis ? 186,003 star A collective list of free APIs 【4 位】 vnpy/vnpy ? 17,863 star 基于Python的开源量化交易平台开发框架 【5 位】 google-research/kubric ? 967 star A data generation pipeline for creating semi-realistic synthetic multi-object videos with rich annotations such as instance segmentation masks, depth maps, and optical flow. 【6 位】 home-assistant/core ? 50,688 star ? Open source home automation that puts local control and privacy first. 【7 位】 diphosphane/CodeCraft2022-PressureGenerator ? 45 star a pressure data generator made for CodeCraft-2022 华为CodeCraft2022数据压测生成器 【8 位】 josephmisiti/awesome-machine-learning ? 53,662 star A curated list of awesome Machine Learning frameworks, libraries and software. 【9 位】 freqtrade/freqtrade ? 16,741 star Free, open source crypto trading bot 【10 位】 kholia/OSX-KVM ? 13,526 star Run macOS on QEMU/KVM. With OpenCore + Big Sur + Monterey support now! Only commercial (paid) support is available now to avoid spammy issues. No Mac system is required. 【11 位】 projectdiscovery/nuclei-templates ? 3,607 star Community curated list of templates for the nuclei engine to find security vulnerabilities. 【12 位】 iperov/DeepFaceLive ? 3,809 star Real-time face swap for PC streaming or video calls 【13 位】 3b1b/videos ? 3,307 star Code for the manim-generated scenes used in 3blue1brown videos 【14 位】 TeamUltroid/Ultroid ? 1,567 star Advanced Multi-Featured Telegram UserBot, Built in Python Using Telethon lib. 【15 位】 edilgin/DeepForSpeed ? 162 star ConvNet learns to play Need For Speed 【16 位】 ethereum/web3.py ? 3,102 star A python interface for interacting with the Ethereum blockchain and ecosystem. 【17 位】 sherlock-project/sherlock ? 30,088 star ? Hunt down social media accounts by username across social networks 【18 位】 a16z/nft-analyst-starter-pack ? 175 star 【19 位】 datvuthanh/HybridNets ? 105 star HybridNets: End-to-End Perception Network 【20 位】 python-telegram-bot/python-telegram-bot ? 17,989 star We have made you a wrapper you can't refuse 【21 位】 TheAlgorithms/Python ? 132,604 star All Algorithms implemented in Python 【22 位】 MHProDev/PyRoxy ? 12 star 【23 位】 Unknow101/FuckThatPacker ? 400 star A simple python packer to easily bypass Windows Defender 【24 位】 dgtlmoon/changedetection.io ? 3,853 star changedetection.io - The best and simplest self-hosted free open source website change detection monitoring and notification service. An alternative to Visualping, Watchtower etc. Designed for simplicity - the main goal is to simply monitor which websites had a text change for free. Free Open source web page change detection 【25 位】 ermaozi/get_subscribe ? 1,335 star ✈️ 免费机场 / 免费VPN -> 自动获取免 clash/v2ray/trojan/sr/ssr 订阅链接,间隔12小时持续更新 | 科学上网 | 翻墙 最後に 最後まで見ていただきありがとうございました。 LGTMをもらえるととても励みになりますので、ぜひお願いします
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Microsoftのデータサイエンス初心者向けコースをやってみるDay3チャレンジ:Visualizing Distributions

はじめに 本記事では、Microsoftのデータサイエンス初心者向けコースDay3の"Visualizing Distibutions"のPython関連をつまみ食いする。 対象読者  Pythonとデータサイエンスに興味があって、英語が苦手な人(英語が得意な人は、参考文献を直接解いてね。 データ用意&散布図を表示 鳥の順番ごとの体長の一般的な分布の概要を示すものですが、真の分布を表示するには最適な方法ではない。 import pandas as pd birds = pd.read_csv("https://raw.githubusercontent.com/microsoft/Data-Science-For-Beginners/main/data/birds.csv") import matplotlib.pyplot as plt birds.plot(kind="scatter", x="MaxLength", y="Order", figsize=(12, 8)) plt.title("Max Length per Order") plt.ylabel("Order") plt.xlabel("Max Length") plt.show() ヒストグラム Matplotlibで数値データの範囲全体に対するMaxBodyMassの分布を描画する。与えられたデータの配列をより小さなビンに分割することで、データの値の分布を表示することができます。 birds['MaxBodyMass'].plot(kind = 'hist', bins = 10, figsize = (12,12)) plt.show() bin-sizeを変えて粒度を細かく見る。 birds['MaxBodyMass'].plot(kind = 'hist', bins = 30, figsize = (12,12)) plt.show() 体重でフィルタ filteredBirds = birds[(birds['MaxBodyMass'] > 1) & (birds['MaxBodyMass'] < 60)] filteredBirds['MaxBodyMass'].plot(kind = 'hist',bins = 40,figsize = (12,12)) plt.show() 2D-histogram 2つの分布の関係を比較するために2Dヒストグラムを作成する。MaxBodyMassとMaxLengthを比較してみよう。 x = filteredBirds['MaxBodyMass'] y = filteredBirds['MaxLength'] fig, ax = plt.subplots(tight_layout=True) hist = ax.hist2d(x, y) 鳥の保護状態と最小翼幅の関係をヒストグラムで x1 = filteredBirds.loc[filteredBirds.ConservationStatus=='EX', 'MinWingspan'] x2 = filteredBirds.loc[filteredBirds.ConservationStatus=='CR', 'MinWingspan'] x3 = filteredBirds.loc[filteredBirds.ConservationStatus=='EN', 'MinWingspan'] x4 = filteredBirds.loc[filteredBirds.ConservationStatus=='NT', 'MinWingspan'] x5 = filteredBirds.loc[filteredBirds.ConservationStatus=='VU', 'MinWingspan'] x6 = filteredBirds.loc[filteredBirds.ConservationStatus=='LC', 'MinWingspan'] kwargs = dict(alpha=0.5, bins=20) plt.hist(x1, **kwargs, color='red', label='Extinct') plt.hist(x2, **kwargs, color='orange', label='Critically Endangered') plt.hist(x3, **kwargs, color='yellow', label='Endangered') plt.hist(x4, **kwargs, color='green', label='Near Threatened') plt.hist(x5, **kwargs, color='blue', label='Vulnerable') plt.hist(x6, **kwargs, color='gray', label='Least Concern') plt.gca().set(title='Conservation Status', ylabel='Min Wingspan') plt.legend(); あまり相関関係は見られない。 最大体重のヒストグラム(保護状態別) 他に相関の見られそうなものがないかを軽く探って見たが、 最大体重が少し関係ありそうな程度で決定的なものはなかった。 attributes = "MaxBodyMass" x1 = filteredBirds.loc[filteredBirds.ConservationStatus=='EX', attributes] x2 = filteredBirds.loc[filteredBirds.ConservationStatus=='CR', attributes] x3 = filteredBirds.loc[filteredBirds.ConservationStatus=='EN', attributes] x4 = filteredBirds.loc[filteredBirds.ConservationStatus=='NT', attributes] x5 = filteredBirds.loc[filteredBirds.ConservationStatus=='VU', attributes] x6 = filteredBirds.loc[filteredBirds.ConservationStatus=='LC', attributes] kwargs = dict(alpha=0.5, bins=20) plt.hist(x1, **kwargs, color='red', label='Extinct') plt.hist(x2, **kwargs, color='orange', label='Critically Endangered') plt.hist(x3, **kwargs, color='yellow', label='Endangered') plt.hist(x4, **kwargs, color='green', label='Near Threatened') plt.hist(x5, **kwargs, color='blue', label='Vulnerable') plt.hist(x6, **kwargs, color='gray', label='Least Concern') plt.gca().set(title='Conservation Status', ylabel=attributes) plt.legend(); seaborn使って密度プロット import seaborn as sns import matplotlib.pyplot as plt sns.kdeplot(filteredBirds['MinWingspan']) plt.show() 鳥別の最大体格密度をseabornで表示 sns.kdeplot( data=filteredBirds, x="MaxBodyMass", hue="Order", fill=True, common_norm=False, palette="crest", alpha=.5, linewidth=0, ) seabornで鳥のMaxLengthとMinLengthを保護状態と比較して可視化。 また、複数の変数の密度を1つのグラフにマッピングすることも可能。鳥のMaxLengthとMinLengthを保護状態と比較して可視化します。 sns.kdeplot(data=filteredBirds, x="MinLength", y="MaxLength", hue="ConservationStatus")  おしゃれなseabornグラフの出来上がり。デザイナの人が作ったのかな。すごく綺麗なグラフが数行で描けて便利なライブラリでした。 著者のTWITTERアカウント @keiji_dl ホームページ(pytorch/python/nlp) 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyMuPDF を用いて印刷用の PDF を作成

PyMuPDF を用いて PDF の並びを変更して仕上がり A5 版の自作本を印刷するための PDF を作成した。 A5 縦で元の PDF を作成 LaTeX で組版する場合は以下のように記述。 \documentclass{jsarticle} \usepackage{geometry} \pdfpageheight=210mm \pdfpagewidth=148mm : \begin{document} : : \end{document} 印刷用の PDF を作成 以下を実行すると imposition.pdf が作成される。サイズが大きくなるので印刷後は削除した方が良い。blank.pdf は「A5 縦」の空白ページでこのスクリプトと同じディレクトリに配置する。 # -*- coding: utf-8 -*- import os, sys import fitz def main(fname): infile = fitz.open(fname) blank = fitz.open(os.path.join(os.path.dirname(__file__), 'blank.pdf')) page_count = infile.page_count page_max = int((page_count + 3) / 4) * 4 doc = fitz.open() j = 0 for i in range(page_max): mod = i % 4 if mod == 0: k = j elif mod == 1: k = j + 2 elif mod == 2: k = j + 3 elif mod == 3: k = j + 1; j += 4 if k >= page_count: doc.insert_pdf(blank) else: doc.insert_pdf(infile, from_page = k, to_page = k) doc.save('imposition.pdf') if __name__ == "__main__": main(sys.argv[1]) 印刷・裁断・製本 Adbe Acrobat Reader の印刷用のフォームで以下のように設定。 両面印刷して中央で裁断して並べ替える。製本は例えばこの動画を参考に行う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python] delikaにアクセスしてデータをPandas DataFrameにするまで。

はじめに なにやらキャンペーンを行っているみたいなので、まずはPythonでデータを取り出すところまでを調べてみた。 delika? 公式にはこのように書かれています。 "delika" is a data sharing and analysis platform for people involved in data. You can host your data and leverage your analysis with any data shared on the platform. "delika "は、データに関わる人々のためのデータ共有・分析プラットフォームです。プラットフォーム上で共有されているあらゆるデータをホスティングし、分析を活用することができます。 データのGithubを目指す!ということなので、応援したいと思います。 対象のデータはこちら。 データサイエンティスト スキルチェックリスト ver3.01 一般社団法人データサイエンティスト協会 スキルチェックリストより引用 データ出典 Requirements Python 3.7+ ライブラリのドキュメントはこちら。 作業開始 Pythonのライブラリが用意してあるので、取得したいデータセットが決まったら簡単に取得できます。 ライブラリのインストール まずはライブラリをインストールします。 環境に応じてどちらかを実行してください。私は新しい環境を作成して、下のコードを実行してみました。 # delikaのclientのみ pip install --extra-index-url=https://docs.delika.io/python/ delika # delikaのclientとPandas他、必要なパッケージ pip install --extra-index-url=https://docs.delika.io/python/ delika[DataFrame] JWTの取得 token = delika.new_token(host= "https://api.delika.io/v1", open_browser = "true") 実行すると、ログイン画面になり、ログインに成功するとJWTの取得ができます。 一度saveメソッドを実行しておくと、しばらくJWTの取得は不要です。 保存したJWTをロードしてclient変数にいれます。 token.save() client = delika.load_client() DataFrameで取得 delika.pandas.read_delika_dataメソッドを使用して、データをDataframeとして取得します。 # データフレームで取得 df1 = delika.pandas.read_delika_data(account_name="connecto-data", dataset_name="ds-skills", data_name="biz.csv", client= client) 結果 無事取得できています。 まとめ 簡単にPythonで取得ができました。自分の手元のデータをアップするモジュールも用意されていて、オープンデータと掛け合わせて新しい発見をする・・ということが増えてきたら嬉しい。次回はこのデータを使用してテキストアナリティクスにチャレンジしてみようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AtCoder Beginner Contest 244 A~D 4完記事

アルゴリズムの学習改善のための自身の備忘録及び学習の一環として記事を書くことにしました. 読んでくれた方で何かありましたら気兼ねなくコメントしてください.お待ちしております. 今回のC問題は普段とことなり、ヒューリスティックてきな問題だと感じた. 普段の問題と同じようなものを期待する方は飛ばしてもいいだろうと思う. A - Last Letter 問題文 英小文字からなる長さ N の文字列 S が与えられます。S の末尾の文字を出力してください。 制約 N は整数 1≤N≤1000 S は英小文字からなる長さ N の文字列 入力 入力は以下の形式で標準入力から与えられる。 $N$ $S$ 考察 $S$の最後の文字を取り出し出力させれば良い. やるだけ. サンプルコード a.py n=int(input()) s=input() print(s[-1]) B - Go Straight and Turn Right 問題文 xy 平面を考えます。x 軸の正の向きを東向き、y 軸の正の向きを北向きとします。 高橋君ははじめ、点 (x,y)=(0,0) にいて東( x 軸の正の向き)を向いています。 S と R のみからなる長さ N の文字列 $T=t_1,t_2,\dots,t_N$が与えられます。 高橋君は$i=1,2,…,N$の順番で下記を行います。 $t_i$=S ならば、高橋君はいま向いている方向に距離 1 だけ進む。 $t_i$=R ならば、高橋君はその場で右に 90 度回転する。その結果、高橋君の向いている方向が下記の通りに変わる。 回転前の向きが東向き( x 軸の正の向き)ならば、回転後の向きは南向き( y 軸の負の向き)になる。 回転前の向きが南向き( y 軸の負の向き)ならば、回転後の向きは西向き( x 軸の負の向き)になる。 回転前の向きが西向き( x 軸の負の向き)ならば、回転後の向きは北向き( y 軸の正の向き)になる。 回転前の向きが北向き( y 軸の正の向き)ならば、回転後の向きは東向き( x 軸の正の向き)になる。 上記の手順を終えた後に高橋君がいる点の座標を出力してください。 制約 $1\leq N \leq 10^5 $ $N$は整数 $T$ は$ S$と$ R$ のみからなる長さ $N $の文字列 入力 入力は以下の形式で標準入力から与えられる。 $N \ \ M$ $A_1 \ \ A_2 \ \dots \ \ A_N$ $B_1 \ \ B_2 \ \dots \ \ B_M$ 考察 今向いている方向が分かればあとは1歩進むだけである. 東を0とし、回転した回数を数える.4の剰余で今向いている方向が一意に定まる. 最初にいる座標を$ans=[0,0]$とし、x軸方向とy軸方向を管理する. 回転した回数の剰余が$0,2$のいずれかの場合x軸方向、$1,3$のいずれかでy方向の移動を行えばよい. $0,1$で正の方向、$2,3$で負の方向に移動すれば無駄なく答えを求められる. サンプルコード b.py n=int(input()) t=list(input()) cnt=0 ans=[0,0] for i in t: if i=='R': cnt+=1 else: if cnt%4==0: ans[0]+=1 elif cnt%4==1: ans[1]-=1 elif cnt%4==2: ans[0]-=1 else: ans[1]+=1 print(*ans) C - Yamanote Line Game 問題文 高橋君と青木君は 2 人で次の対戦ゲームをします。 高橋君が先手でゲームを始め、ゲームが終了するまでの間、 2 人は交互に 1 以上 2N+1 以下の整数を 1 つずつ宣言します。 どちらかが一度でも宣言した整数は、それ以降どちらも二度と宣言することが出来ません。 先に整数を宣言することが出来なくなった方のプレイヤーの負けとなり、負けなかった方のプレイヤーの勝ちとなります。 このゲームでは必ず高橋君が勝ちます。 高橋君の立場で実際にゲームを行い、ゲームに勝ってください。 制約 $1 \leq N \leq 1000$ $N$は整数 入力 この問題はインタラクティブな問題(あなたの作成したプログラムとジャッジプログラムが入出力を介して対話を行う形式の問題)です。 あなたのプログラムが高橋君の立場で、ジャッジプログラムが青木君の立場でゲームを行います。 まず、あなたのプログラムに標準入力から正の整数 N が与えられます。 その後、ゲームが終了するまで下記の手順を繰り返します。 あなたのプログラムが、高橋君が宣言する整数として、1 以上 2N+1 以下の整数を標準出力に出力します。(どちらかのプレイヤーによってすでに宣言されている整数を出力することは出来ません。) ジャッジプログラムによって、青木君が宣言する整数があなたのプログラムに標準入力から与えられます。(どちらかのプレイヤーによってすでに宣言されている整数が入力されることはありません。) ただし、青木君が宣言できる整数が残っていない場合は、代わりに 0 が与えられ高橋君の勝ちでゲームが終了します。 考察 Nを受け取る 2N+1以下で既出でない数字を出力 値を受け取る 2,3を繰り返し行う.この時既出の数を出力しないように既出の値をsetで管理すると、$O(1)$で有無が判定出来て高速になる. サンプル c.py n=int(input()) ans=set() for i in range(n+1): for j in range(1,2*n+2): if j not in ans: print(j) ans.add(j) break k=int(input()) ans.add(k) D - Swap Hats 問題文 1,2,3 の番号がついた 3 人の高橋くんがおり、赤・緑・青の色がついた 3 種類の帽子がそれぞれ 1 つずつあります。それぞれの高橋くんは帽子を 1 つかぶっており、高橋くん i がはじめにかぶっている帽子の色は文字 $S_i$で表されます。ここで、R は赤、G は緑、B は青に対応しています。これから、以下の操作をちょうど$10^{18}$回行います。 操作 3 人の高橋くんのうち 2 人を選ぶ。2 人はお互いのかぶっている帽子を交換する。 $10^{18}$回の操作の後、高橋くん i が文字 $T_i$に対応する色の帽子をかぶっているようにすることはできますか? 制約 $S_1,S_2,S_3$は R, G, B の並べ替えである $T_1,T_2,T_3$は R, G, B の並べ替えである 入力 入力は以下の形式で標準入力から与えられる。 $S_1,S_2,S_3$ $T_1,T_2,T_3$ 考察 置換の回数を考える. 受け取った時点でSとTが一致している場合、同一箇所を$10^{18}$回入れ替えることでSとTが一致する. どこか、1カ所のみことなることは制約からあり得ないので考えなくて良い. 2カ所ことなる場合を考える.ことなる箇所を適切に入れ替えると1度の操作でSとTが一致する.この後、奇数回操作をする場合、題意を満たすことは必ずない. 最後にすべて位置がことなる場合、必ず2度の入れ替えでSとTが一致する形を作れる.以降$10^{18}-2$回の操作が可能であるので題意を満たせる. SとTを比較し、$s[i]==t[i]$, $\ i=0,1,2$を満たす個数を数え0または3であれば"Yes"、それ以外は"No"を出力すれば良い. サンプル d.py s=list(map(str,input().split())) t=list(map(str,input().split())) cnt=0 if s[0]!=t[0]: cnt+=1 if s[1]!=t[1]: cnt+=1 if s[2]!=t[2]: cnt+=1 if cnt==0 or cnt==3: print("Yes") else: print("No")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Chalice で AWS Data Wrangler を利用する

はじめに AWS 上のデータを Pandas1 で処理したいときには、各種 AWS サービス(RDS, DynamoDB, Athena, S3 など)からのデータの load/unload を簡単化してくれる Python モジュール AWS Data Wrangler2 が超便利です。しかも、AWS 自体が開発してオープンソース公開しているものなので、ある程度安心して使えます。 なんですが、この AWS Data Wrangler を Lambda で利用する際には、デプロイパッケージのサイズに適用されるクォータ(未圧縮で 250MB、zip 圧縮済みで 50MB)が問題になります。具体的には、AWS Data Wrangler を普通に pip でインストールすると、それだけでパッケージサイズが 250MB を超えてしまいデプロイすることができません。 実はこの問題、AWS Data Wrangler のドキュメントに示されているとおり、Lambda Layer を活用することで解決できます。本記事では、これを Lambda 開発のための Python フレームワークである AWS Chalice3 を使ったプロジェクトに適用する方法をまとめます。 準備 説明のために、まずは Chalice でシンプルなプロジェクトを用意します。公式のチュートリアル4などにある通り、chalice new-project app_skelton で新たなプロジェクトを作り、生成された app.py に、AWS Data Wrangler のバージョンを出力するだけの Lambda 関数を定義します。 app.py from chalice import Chalice import awswrangler as wr app = Chalice(app_name='app_skelton') @app.lambda_function() def func(event, context): return {'version': wr.__version__} 通常の外部モジュール利用時と同じように、requirements.txt に awswrangler==2.14.0 を追加して、chalice deploy してみると、、、パッケージサイズクォータに引っかかってデプロイできません。 このシンプルなプロジェクトを例に、AWS Data Wrangler を利用できるようにするための方法を説明します。 以下に説明する方法では、requirements.txt に awswrangler に関するモジュール情報を記載しません(追加する必要はありません)。 方法 AWS Chalice で AWS Data Wrangler を利用するためには、Lambda Layer を活用する必要があります。具体的には2つの方法があるのでそれぞれ説明します。 方法1 「AWS が公開している Lambda Layer を活用する」 特定の AWS リージョン5とバージョン6については、AWS Data Wrangler がデプロイされた Lambda Layer を、AWS が公開してくれています。Chalice プロジェクトの設定ファイルでそれらの Layer ARN を指定すれば利用できる、ということです。Chalice プロジェクト側で手を入れる必要がある箇所は、設定ファイルの .chalice/config.json のみで、デフォルトからの差分(diff)は以下のとおり2行(行頭に + のある行)の追加だけです。 .chalice/config.json { "version": "2.0", "app_name": "app_skelton", + "automatic_layer": true, + "layers": ["arn:aws:lambda:ap-northeast-1:336392948345:layer:AWSDataWrangler-Python38:2"], "stages": { "dev": { } } } ちなみに、上記の例で指定している Layer ARN は、Python 3.8 用の AWS Data Wrangler 2.13.0 です6。 方法2 「AWS が配布している Layer 用パッケージを活用する」 方法1 はお手軽ではあるのですが、特定リージョン5以外のリージョンで利用したいときや、公開されている Managed Layer に何らかの原因でアクセスできなくなる等のリスクを回避したいとき、などのために、代替手段が用意されています。それは、AWS が github で配布している Layer 用パッケージ(Lambda のパッケージサイズクォータに引っかからないサイズにコンパクト化したパッケージ)7の活用です。 例えば、Python 3.8 用の AWS Data Wrangler 2.14.0 に対応する Layer 用パッケージを利用する場合、手順は以下のとおりです。 Layer 用パッケージ(awswrangler-layer-2.14.0-py3.8.zip)をダウンロード ダウンロードした zip ファイルを解凍 解凍された python ディレクトリ内を、Chalice プロジェクトのディレクトリ直下の vendorディレクトリに配置8 .chalice/config.json に "automatic_layer": true を追加 .chalice/config.json { "version": "2.0", "app_name": "app_skelton", + "automatic_layer": true, "stages": { "dev": { } } } これで、クォータに引っかかることなく chalice deploy できるようになります。実際にデプロイすると、Lambda 関数などを含むメインパッケージとは別に、AWS Data Wrangler 一式が格納された Lambda Layer が新たに生成されます。デプロイされた Lambda Layer をダウンロードしてサイズを確認すると 44MB ほどでした。上限 50MB に収まる絶妙なサイズです。 おわりに 今回は、Jupyter Notebook 上で書いた ETL 処理を Lambda に移植しようとしたときに必要になったことをまとめてみました。AWS Data Wrangler に限らず、Chalice プロジェクトでサイズが大きめのモジュールを使う場合に活用できると思います。 pandas - Python Data Analysis Library ↩ What is AWS Data Wrangler? — AWS Data Wrangler documentation ↩ Documentation — AWS Chalice ↩ Quickstart — AWS Chalice ↩ 2022年3月21日時点でのドキュメント(Install — AWS Data Wrangler 2.14.0 documentation)によると、ap-northeast-1, ap-southeast-2, eu-central-1, eu-west-1, us-east-1, us-east-2, us-west-2 の計7リージョンで利用できるようです。 ↩ ↩2 2022年3月21日時点でのドキュメント(Install — AWS Data Wrangler 2.14.0 documentation)によると、Python の 3.7 から 3.9、AWS Data Wrangler の 2.12.0 と 2.13.0 に対応する Layer ARN が利用できるとあります。最新版である 2.14.0 用の Layer ARN は記載されていないのですが、Lambda の管理コンソールで調査できる範囲では、Python 3.8 用の AWS Data Wrangler 2.14.0 に対応する Layer ARN(arn:aws:lambda:ap-northeast-1:336392948345:layer:AWSDataWrangler-Python38:3)が存在していて利用もできるようです。 ↩ ↩2 Releases · awslabs/aws-data-wrangler · GitHub ↩ App Packaging — AWS Chalice ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む