カテゴリー
Python 未分類

Excel 作業を自動化する為の Python 応用

列見出しを順番に繰り出す

ワークシートの列位置を区別するシンボルを連続的に発生させるジェネレータ(Python の予約語:yield 式を持つ特殊な関数)を作りました。

コード

def xl_col_name_gen():
  # 1st dimension (from A to Z)
  for i in range(0x41, 0x5b):
    yield '{:c}'.format(i) 
  # 2nd dimension (from AA to ZZ)
  for i in range(0x41, 0x5b):
    for j in range(0x41, 0x5b):
      yield '{:c}{:c}'.format(i,j)
  # 3rd dimension (from AAA to ZZZ)
  for i in range(0x41, 0x5b):
    for j in range(0x41, 0x5b):
      for k in range(0x41, 0x5b):
        yield '{:c}{:c}{:c}'.format(i,j,k)

この関数から最初に繰り出されるシンボルは’A'(0x41)で、Excel ワークシートの最も左側に位置する列を識別する為のものです。次に 繰り出されるのが’B’,順番に’C’,’D’,’E’‥と続き、’Z'(0x5a)まで繰り出されると次が’AA’に替わります。参照記事:ASCIIコード表

利用例

>>> gen = xl_col_name_gen()
>>> gen
<generator object xl_col_name_gen at 0x000001C788C98A50>
>>> next(gen)
'A'
>>> next(gen)
'B'
>>> next(gen)
'C'
>>> def func_A(gen):
...   print(next(gen), next(gen), next(gen))
...
>>> def func_B(gen):
...   print([item for item in gen])
...
>>> func_A(gen)
D E F
>>> func_B(gen)
['G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM', 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 
..... 途中略 .....
'YX', 'YY', 'YZ', 'ZA', 'ZB', 'ZC', 'ZD', 'ZE', 'ZF', 'ZG', 'ZH', 'ZI', 'ZJ', 'ZK', 'ZL', 'ZM', 'ZN', 'ZO', 'ZP', 'ZQ', 'ZR', 'ZS', 'ZT', 'ZU', 'ZV', 'ZW', 'ZX', 'ZY', 'ZZ', 'AAA', 'AAB', 'AAC', 'AAD', 'AAE', 
..... 途中略 .....
 'ZYW', 'ZYX', 'ZYY', 'ZYZ', 'ZZA', 'ZZB', 'ZZC', 'ZZD', 'ZZE', 'ZZF', 'ZZG', 'ZZH', 'ZZI', 'ZZJ', 'ZZK', 'ZZL', 'ZZM', 'ZZN', 'ZZO', 'ZZP', 'ZZQ', 'ZZR', 'ZZS', 'ZZT', 'ZZU', 'ZZV', 'ZZW', 'ZZX', 'ZZY', 'ZZZ']
>>>

説明

  • Excel 作業を自動化するプログラムではランダムにセル番地を変える事はほとんどなく、列順アクセスなので、プログラムが見やすくする為ジェネレータを使いました。
  • ジェネレータとせず、生成したシンボルをリストやタプルで戻すシンプルな方法もありますが、これだと 26 + (26*26) + (26*26*26) = 18278 バイトのストレージが必要です。ところがジェネレータでは next メソッドを呼び出すと、次の yield 式が現れるまで演算を実行するだけなので、追加のストレージが要らないのが特長です。
  • このジェネレータで列名を表示させると ‘ZZZ’まで繰り出しますが、実際のところ「Excel の仕様と制限」のため、’XFD’ が利用可能な最大値です。
  • 冒頭からの繰り出しを何回も繰り返したい場合は、次のコード例のように gen = xl_col_name_gen() の代入を繰り返してください。
>>> gen = xl_col_name_gen()
>>> next(gen)
'A'
>>> next(gen)
'B'
>>> gen = xl_col_name_gen()
>>> next(gen)
'A'
>>> next(gen)
'B'
>>>

応用例(Pandas)

このジェネレータは科学実験やプログラムのテスト結果を Excel へまとめて保存しておきたい場合などに応用することが出来ます。

予想値と測定値(あるいは計算結果)が CSV やその他の手段を使い入手可能だとします。Pandas を使ってこのデータを処理し、分かったことを誰かへレポートする事例を想定してみます。参照記事:pandas documentation

import pandas as pd

df = pd.DataFrame({
    # 2 番目のテストだけ NG という想定のサンプルデータ
    '予想値': [ 543, 651, 995, 331 ],
    '測定値': [ 543, 654, 995, 331 ],
})

報告する相手がエンジニアリング分野の方であれば次の計算結果を示せば結果を理解するはずです。

>>> df[df['予想値'] != df['測定値']]
   予想値  測定値
1  651  654
>>>

プログラムのユニットテストであれば、例外をスローしてくれた方が都合がよく、無効同値クラスのテストが通った事をシンプルに証明できます。

>>> pd.testing.assert_series_equal(df['予想値'], df['測定値'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\someone\.venv\lib\site-packages\pandas\_testing\asserters.py", line 1077, in assert_series_equal
..... 途中略 .....
AssertionError: Series are different

Series values are different (25.0 %)
[index]: [0, 1, 2, 3]
[left]:  [543, 651, 995, 331]
[right]: [543, 654, 995, 331]
>>>

上記の何れでもない場合は万人向けのまとめ方が必要です。そこで Excel を登場させます。

予想値と測定値を照し合せた結果を表示させる列の名前を仮に「判定」とします。

col_names = df.columns.tolist() + ['判定']

先のジェネレータ式を使って Excel のセルの列名を必要な数だけ繰り出します(事例では3つ。つまり’A’,’B’,’C’)。

gen = xl_col_name_gen()
ref_cols = [next(xl_col_name_gen) for i in range(len(col_names))]

「判定」列へ埋め込む Excel 計算式の元となるフォーマット文字列を用意します。波かっこで囲まれた部分へ、後述の format関数へ与えた引数があてがわれます。空の波かっこ部分にセルの列名、rownoを含む波かっこ部分にセルの行番号がそれぞれ埋め込まれる想定です。

expr_fmt = '=({}{rowno}={}{rowno})'

無名関数: f を定義します。これはデータセットの行数回呼び出され、セル番地を伴うExcel式を文字列として返します。引数の idx は DataFrame のインデックスが渡されます。定数 2 が足されている理由は、Excel の行番号が 1 から開始されることと、列名の行が 1 行分置かれているからです。

f = lambda idx: expr_fmt.format(*ref_cols[:], rowno=idx + 2)

セル番地による参照を含んだ Excel 計算式を「判定」列へ埋め込みます。-1 という添え字を与えるとリストの末尾に位置する要素が得られます。

df[col_names[-1]] = df.index.map(f)

上記に説明したコードを連続的に実行させた結果を例示します。

>>> # 判定用の Excel 計算式を置く列を含めた列リスト
>>> col_names = df.columns.tolist() + ['判定']
>>> gen = xl_col_name_gen()
>>>
>>> # 使われている列の数だけ Excel の列名を繰り出す
>>> # (この例では 'A','B','C' の各列)
>>> ref_cols = [gen) for i in range(len(col_names))]
>>>
>>> # Excel へ埋め込む判定式のフォーマット
>>> expr_fmt = '=({}{rowno}={}{rowno})'
>>>
>>> # expr_fmt のプレースフォルダへ値を埋め込む為のラムダ式
>>> # + 2 している理由は Pandas のインデックスが 0 から始まる
>>> # のと、列名の行が置かれる次の行から評価式が置かれる為。
>>> f = lambda idx: expr_fmt.format(*ref_cols[:], rowno=idx + 2)
>>>
>>> # 「判定」という名前の新しい列を DataFrame へ加えつつ、
>>> # 評価式を1行ずつ埋め込んでゆく
>>> df[col_names[-1]] = df.index.map(f)
>>>

DataFrame に何が保存されたか確認してみましょう。

>>> df
   予想値  測定値        判定
0  543  543  =(A2=B2)
1  651  654  =(A3=B3)
2  995  995  =(A4=B4)
3  331  331  =(A5=B5)
>>>

このデータは Pandas の to_csv 関数を使うとファイルへ保存することが出来ます。データに日本語が含まれている場合、encodingを指定することで文字化けを防ぐことが出来ます。

df.to_csv('validate.csv',encoding='utf-8_sig', index = False)

保存された「validate.csv」というファイルをExcel へインポートしてみます。手順は次の通りです。

[メニュー]-[ファイル]-[開く]-《ダイアログから validate.csv を選択》-[テキストファイルウイザード1/3]-《”先頭行をデータの見出しとして使用する”をチェック。”次へ”》-[ テキストファイルウイザード2/3 ]-《”区切り文字”=”カンマ” 。”次へ” 》-[ テキストファイルウイザード3/3 ]-《”完了”》

インポートしたデータがExcel へ表示されます。「判定」列へ埋め込んだ式がインポート時に計算されて「TRUE」または「FALSE」の表示になっている事と、2番目に表示されているNGだったテスト結果を反映して「判定」が「FALSE」になっている事がポイントです。

確認すべきデータが大量にあるときは Excel の視覚効果を利用する事も可能です。C列をマウスで全選択して次の手順で「FALSE」と表示されたセルを網掛けにすることが出来ます。

[メニュー]-[条件付き書式..]-[セルの強調表示ルール…]-[指定の値に等しい]-《”次の値に等しいセルを書式設定:”←”FALSE”》

これなら NG だったテストが直ぐに探し出し易くなります。

カテゴリー
Windows 未分類

どうしても消せない異常な名前のファイルを意図せず作ってしまった時の消し方

下記を試したが何れも失敗

  • エクスプローラからそのファイルを選択して、DEL キーやポップアップメニューの「削除」
  • コマンドプロンプトから del コマンド
  • PowerShell から
    Get-ChildItem . -Filter <ファイル名の一部にマッチする文字列>| `
      ForEach-Object { Remove-Item -LiteralPath $_.Name }
  • コマンドプロンプトから
    del "\\?\<フルパス>"

解決方法

  1. 7-Zip を入手
  2. マニュアルに沿ってインストール
  3. コマンドプロンプトから下記のコマンドを実行する。-sdel というオプションがポイント
    7z a -sdel temp.zip <ファイル名の一部にマッチする文字列>
  4. カレントディレクトリに出来た temp.zip をエクスプローラなどで普通に消す
カテゴリー
Oracle Database 運用 未分類

ORA-28001 the password has expired の対応

Oracle をインストールした後何もしないと 180日目で表題のエラーを受取り、接続障害が発生します。この障害を発生させないためには、あらかじめ運用アカウントへ適用するセキュリティーポリシーを定めて、プロファイルに反映させることが必要ですが、目前で起きている障害を直ちに取り除き、運用を速やかに再開させる為の方法も同じように必要なので対応手順をメモしておきます。

和文表記:
ORA-28001: パスワードが期限切れです。

SQL*Plus 等でDBAとしてデータベースを接続し次のコマンドを実行して、プロファイルの設定状況を確認します。

SQL*Plus を使う場合には事前に次のコマンドを実行しておくと、画面表示が見やすくなります:

set linesize 100 pagesize 250
column USERNAME format a30
column ACCOUNT_STATUS format a20
column PROFILE format a10
column LIMIT format a16

現状、DFAULT プロファイルの設定がどうなっているかを調べるのが次のSQLです。

select LIMIT from dba_profiles where RESOURCE_NAME='PASSWORD_LIFE_TIME' and PROFILE='DEFAULT';

初期値のままだと次の様に表示されます。

LIMIT
----------------
180

データベース作成後180日目以後に大騒ぎにならないようにする為には次のSQLを実行しておきます。

ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

パスワードの有効期限が切れているアカウントの現況を調べるには次のSQLを実行します。

select USERNAME,ACCOUNT_STATUS,EXPIRY_DATE,PROFILE from dba_users where username = 'アカウント名';

上記の問合せ結果からパスワードの有効期限が切れている事は”EXPIRED”というキーワードで確認できます。

USERNAME                       ACCOUNT_STATUS       EXPIRY_D PROFILE
------------------------------ -------------------- -------- ----------
アカウント名                    EXPIRED              日付時刻  DEFAULT

このアカウントを有効化する為には次の SQL を実行します。

ALTER USER アカウント名 IDENTIFIED BY 新しいパスワード;

これで終わりです。