e-Learning の教材の回答を取り扱う話です。回答は数値を想定していて,その有効数字を検証したいと思っています。数式処理ソフト等に回答を渡すと,自動的に処理が入ってしまうので,回答の有効数字が分からなくなります。回答を文字列のまま処理をしていくことになるかと思います。まず最初は回答の取り込みです。
sympify は文字列(中身は数式)を Sympy に式として取り込む関数です。
from sympy import *
expr = sympify('x +2*x +1')
print(expr)
>>> 3*x + 1
見ての通り、処理されてしまいます。止める方法は下記。
from sympy import *
expr = sympify('x +2*x +1', evaluate=False)
print(expr)
>>> x + 2*x + 1
これで評価が止まります。項の並べ替えはなされるようです。ただしこれは ubuntu22 での話。ubuntu20 では evaluate=False を書き加えるとエラーとなりました。Python の環境は,OSインストールしたままの状態です。それに synaptic で sympy を入れています。ubnutu22 で環境構築をすることになりそうです。以下はエラーキャッチの方法です。数式に相当する文字列かどうか判断することが目標なので,エラーへの対処が必要です。
from sympy import *
try:
expr = sympify('1 + 1', evaluate=False)
except Exception as ex:
expr = ex
print(expr)
print(type(expr))
>>> 1 + 1
>>> <class 'sympy.core.add.Add'>
下付きの文字などの扱いが気になります。アンダーラインを用いて定義は可能なようです。maxima の表記とは異なります。jupyter-notebook では init_printing() によって,TeX 的な表示が可能になります。その場合表示には print ではなくて,display を使います。
from sympy import *
init_printing()
x= symbols("x_{0:3}")
print(x)
display(x)
display(x[0])
出力は下記です。
sympify で数式を取り込む場合を考えます。アンダーラインは上手く取り込めなくて、x0 というような表記だとこんな感じになります。違いがなさそうです。
atoms(Symbol) で、定義されている変数を調べています。x0 と x1 が見つかっていますが、その結果は set オブジェクト(要素間の重複を無くした集合)で、これをリストに変更しています。リストでないとインデックスが利用できません(set には順番がない)。変数への代入を subs でやっています。代入もできているので、確かに変数となっているようです。
回答の有効数字判定ですが,今思いつく判定処理の順番は
- sympy が解釈できる数式としての表現になっているかどうか
- 文字が入っていないことの確認。入っていなければ数値のみの回答と認定する
- 数値ひとつかどうかの判断。
1.23 + 2.34
や1.234 * 2.345
他にはsin(3.1415)
などを排除する - 有効数字の判定
こんなところです。現在の e-Learning 教材は maxima の形式で回答してくるので、それを sympy 用に編集する必要もありますが,数値だけと判断した後なら必要ないのかもしれません。
下記のようなコードを書いてみました。色々なパターンの数値の有効数字を判定するものです。関数 getSignificantFigures が有効数字を求める所です。引数 tmpStr には,整数や 1.234e-5 等の形式の数式が記述された文字列が入ります。またコードには 1.234*10^3 や 1.234*10**3 のような形式への対応も書いています。
%reset -f
import sys
from sympy import *
def getSignificantFigures(tmpStr):
def mainProcess(tmpStr):
workingString = tmpStr
index_e = tmpStr.find('e')
index_E = tmpStr.find('E')
if index_e != -1:
workingString = tmpStr[:index_e]
if index_E != -1:
workingString = tmpStr[:index_E]
workingString = workingString.replace('+',"")
workingString = workingString.replace('-',"")
workingString = workingString.replace('.',"")
while workingString[0] == '0':
workingString = workingString[1:]
#print('last string "' + workingString + '"')
significantFigures = len(workingString)
return significantFigures
message = "none"
significantFigures = -1
typeArray = ["<class 'sympy.core.numbers.Integer'>", "<class 'sympy.core.numbers.Float'>"]
tmpStr = tmpStr.replace(' ',"")
#print('"' + tmpStr + '"')
try:
expr = sympify(tmpStr, evaluate=False)
except Exception as ex:
expr = ex
#print(expr)
exprClass = str(type(expr))
if exprClass == "<class 'sympy.core.sympify.SympifyError'>":
return significantFigures, "is not expression"
# 以下、とりあえず数式にはなっている
# 指数部の表記が異なる場合への対応
tmpStr2 = tmpStr.replace('*10^',"e")
tmpStr2 = tmpStr2.replace('*10**',"e")
try:
expr2 = sympify(tmpStr2, evaluate=False)
except Exception as ex:
expr2 = ex
#print(expr2)
expr2Class = str(type(expr2))
if exprClass in typeArray:
significantFigures = mainProcess(tmpStr)
return significantFigures, "float or integer"
elif expr2Class in typeArray:
significantFigures = mainProcess(tmpStr2)
return significantFigures, "you use 10^ or 10**."
else:
return significantFigures, "is not integer or float"
testArray = []
testArray.append('-0.12e-3')
testArray.append('-0.12E-3')
testArray.append('-0.0120e-3')
testArray.append(' + 0.01200e-3')
testArray.append('-2.14*10^3')
testArray.append('-2.14*10**3')
testArray.append('-0.123')
testArray.append('-123')
testArray.append('- 123') # 符号との間に空白
testArray.append('-123 ') # 右側に空白
testArray.append(' - 123 ') # 多数の空白
testArray.append(' -0123 ')
testArray.append('123*10^2*3*10^2')
for i in range(len(testArray)):
tmpStr = testArray[i]
significantFigures = -1
print('\nNo. ' + str(i))
print('"' + tmpStr + '"')
significantFigures, message = getSignificantFigures(tmpStr)
print(message)
print('significantFigures : ' + str(significantFigures))
出力は下記のような感じです。
No. 0
"-0.12e-3"
float or integer
significantFigures : 2
No. 1
"-0.12E-3"
float or integer
significantFigures : 2
No. 2
"-0.0120e-3"
float or integer
significantFigures : 3
No. 3
" + 0.01200e-3"
float or integer
significantFigures : 4
No. 4
"-2.14*10^3"
you use 10^ or 10**.
significantFigures : 3
No. 5
"-2.14*10**3"
you use 10^ or 10**.
significantFigures : 3
No. 6
"-0.123"
float or integer
significantFigures : 3
No. 7
"-123"
float or integer
significantFigures : 3
No. 8
"- 123"
float or integer
significantFigures : 3
No. 9
"-123 "
float or integer
significantFigures : 3
No. 10
" - 123 "
float or integer
significantFigures : 3
No. 11
" -0123 "
is not expression
significantFigures : -1
No. 12
"123*10^2*3*10^2"
is not integer or float
significantFigures : -1