2019年12月2日月曜日

AtCoderで緑色になってた

 AtCoderで色が変わったので記事を書きます。
 無事に緑色に到達しました。先週の時点で到達してたみたいです。

 前回の記事で「まあ緑はいけるんじゃね?(意訳)」と舐めたこと言った記憶があるので、無事に到達してよかったです。
 コンテストには継続して出ています。

 D問題を解けるかどうかでパフォーマンスがだいぶ変わるので、D問題を安定して解けるようになりたいですね(前回と同じことを言っている)。ここ数回のABCは3完が続いていたので反省していたが、本日初の5完を達成したのでなんともいえない表情をしている。
 現時点のAtCoder Problemsはこんな感じ。

 前回の記事の画像と比べると、まあいくらか進んでいるかなという感じですね。ただ11月に入ってからはほとんど進められてないです。10月にcurrent streakが続いていた時は始めたてのハイな状態だったのでガンガン進めてましたが、一回途切れてからはプッツリとやらなくなってしまいました。お仕事あるからね、仕方ないね。

 今後は、D問題を常に脳内でモヤヤンと考えて生活するようにします。current streak気にしてると毎日簡単な問題を1問だけ解くだけで終わってしまうので、毎週末にD問題1個潰すくらいのつもりで。ABC125以前だとC問題もそこそこ引っかかりそうなので、その辺も合わせて。

 もちろん次は水色を目指すことになるのだが、ここから水色までが長いんだろうな、と、色々な人のレートグラフを見ていて思う。

2019年9月16日月曜日

AtCoderで茶色になった

 AtCoderで色が変わったら記事にするらしいので、という他の記事と同じような書き出しでAtCoderで茶色になった話をします。
 昨日のABC141で茶色がつきました。


 東大卒の医者がG社に転職するエントリを読んでいてAtCoderの存在を知った。元々パズル的な感覚で、身近にプログラミングの題材を見つけてブログにしていたので、AtCoderを知って「探さなくても毎週題材が与えられる!」と、始めない理由がなかった。

 初めて参加したABC138はC問題までの3完でした。以前グラフ理論には触れていたのでD問題もいけそうな気がしたがダメでした。
 翌週は最強プログラマー選手権予選に参加したが、A問題ACしたところでお仕事に呼ばれてしまい悲しいことに。
 ABC139はD問題が簡単だったので4完できて嬉しかったが、パフォーマンスがABC138より低くてぐぬぬってなってた。
 その後暇なときにABC過去問のA, B, C問題を、時間がある時にはできそうなD問題を埋めました。
 現時点でAtCoder Problemsはこんな感じ。

 ABC140、141のD問題は粘っこく考えてなんとかACにこぎつけ、4完キープ。

 たまたま3週連続でD問題まで解けていますが、おそらく自分のベースとしてはC問題までの3完はほとんどできて、たまにD問題も解けるくらいでしょうと思っています。E, F問題はちょっと手が出なさそう。色としては緑色滑り込みくらい?

 今後の目標としては、「D問題までは時間内でだいたいACできます」と言えるようになりたいですね。色で目標を立てるなら緑色上位くらいでしょうか。ABC141の順位表をざっと見てみましたが、同じ4完でも15分で解けば水色、1時間以上かけると緑色くらいになっていたので、色とかレートを気にすると時間が重要そう。個人的には悩みに悩みつつ時間内にD問題はほぼACできるというくらいなら万々歳なので、水色を目指す必要はないかなと思いますが、そもそもそんなことは緑色になってから言うことですねすみません。

 今後の勉強については、10月からは本業が忙しくなりそうなので、蟻本と呼ばれている本とか読んだりしたいはしたいが難しそう。そもそも毎週ABCに参加できるかが微妙。色とかレートより「D問題に臆しなくなった実感」が欲しいというのをモチベーションとするので、コンテストに参加できなくても後から解ければいいのかなとも思います。なので当分はAtCoder Problemsをひたすら埋めます。ただコンテストの方が集中力高まるから、出来るだけ参加したい。
 それと競プロやるならC++とか速い言語を学ぶべきみたいですが、競プロをがっつりやろうという気持ちはないので、Pythonでいいかなと思っています。

2019年7月28日日曜日

TMT試験紙

 トレイルメイキングテストの縦断的評価をしたい時に、試験紙が一種類しかないと2回目の方が成績上がるよね。時間が経てばはっきりした位置はさすがに覚えてないだろうけど、ちょっとした慣れとか、TMT試験紙の特徴が・・・(多くは語らない)。
 なのでそういう場合のために、ランダムで試験紙を作成するコードを書いた。やはり多くは語らないが、ただの一様分布からの乱数で各ノードの位置を決定した後に、一工夫している。これはオリジナルがそのように作ってあるからなんだけど、なんのためなのか不明

 gitはこちら。シンプルに
   python get_TMTA.py [filename]
 で指定の場所に試験紙のjpegファイルが作成される。サイズはA4。下は実行例。

TMT-A

TMT-B


 TMT-Bはひらがなで作ろうとしたんだけど、文字化けが解消できなかった。

2019年5月29日水曜日

KerasチュートリアルのCNNがうまくいかない

(2019/05/31:末尾に追記しました)
 深層学習を勉強する機運が高まったので「PythonとKerasによるディープラーニング」を読んでいる。第5章ではまずMNIST数字分類にCNNを使うのだが、サンプルコードを写経したプログラムがうまくいかないので困っている。誰か教えてください。
 こちらがそのノートブック。MNISTデータ読み込みなどが終わった後、前半ではサンプルコード通りのネットワークを持つ分類器を使っている。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten_1 (Flatten)          (None, 576)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                36928     
_________________________________________________________________
dense_2 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0

その結果がこれである。

Epoch 1/5
60000/60000 [==============================] - 97s 2ms/step - loss: 14.0193 - acc: 0.1115
Epoch 2/5
60000/60000 [==============================] - 88s 1ms/step - loss: 14.4351 - acc: 0.1044
Epoch 3/5
60000/60000 [==============================] - 86s 1ms/step - loss: 14.4351 - acc: 0.1044
Epoch 4/5
60000/60000 [==============================] - 91s 2ms/step - loss: 14.4351 - acc: 0.1044
Epoch 5/5
60000/60000 [==============================] - 88s 1ms/step - loss: 14.4351 - acc: 0.1044

 テストデータでの正答率も0.1028と何も学んでいない。どこかでコードミスっているか?と思い、サンプルコードをgit cloneしてそのまま実行してもやはり同じようになった。
 次に上のモデルのConv2D_3が妙に気になったので消してみたのが後半。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_4 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 1600)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 64)                102464    
_________________________________________________________________
dense_4 (Dense)              (None, 10)                650       
=================================================================
Total params: 121,930
Trainable params: 121,930
Non-trainable params: 0

 その結果がこれである。

Epoch 1/5
60000/60000 [==============================] - 85s 1ms/step - loss: 0.1748 - acc: 0.9473
Epoch 2/5
60000/60000 [==============================] - 87s 1ms/step - loss: 0.0534 - acc: 0.9835
Epoch 3/5
60000/60000 [==============================] - 85s 1ms/step - loss: 0.0391 - acc: 0.9881
Epoch 4/5
60000/60000 [==============================] - 84s 1ms/step - loss: 0.0309 - acc: 0.9911
Epoch 5/5
60000/60000 [==============================] - 84s 1ms/step - loss: 0.0259 - acc: 0.9922

 テストデータでの正答率も0.9899と高い。この結果からはこのConv2D_3が悪かったんかなぁという推測を生む以上の何かは得られなかった。
 その次に、イヌとネコの分類器を作るパートでも同様にサンプルコード通りに試してみた。そのノートブックがこちら
 モデルのサマリはこんな感じ。

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 512)               3211776   
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0

 実行結果はこんな感じ。

Epoch 1/30
100/100 [==============================] - 199s 2s/step - loss: 3.9247 - acc: 0.4875 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 2/30
100/100 [==============================] - 190s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 3/30
100/100 [==============================] - 197s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 4/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 5/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 6/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 7/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 8/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 9/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 10/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 11/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 12/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 13/30
100/100 [==============================] - 194s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 14/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 15/30
100/100 [==============================] - 191s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 16/30
100/100 [==============================] - 195s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 17/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 18/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 19/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 20/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 21/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 22/30
100/100 [==============================] - 195s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 23/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 24/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 25/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 26/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 27/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 28/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 29/30
100/100 [==============================] - 192s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000
Epoch 30/30
100/100 [==============================] - 193s 2s/step - loss: 7.9712 - acc: 0.5000 - val_loss: 7.9712 - val_acc: 0.5000

 2つの分類で正答率0.5なのでやはりダメダメである。何故サンプルコード通りでうまくいかないのかが全くわからない。それとlossが全く動いていないのが気になる。この手の「同じ値を出力し続ける」ようなことって、記憶が曖昧だがメモリ足りない時とかに起きるようなことがあった気がするなぁなんて思いながら結果を眺めていた。
 おそらくMNISTでConv2D_3が入るとうまくいかない理由と、イヌネコ分類器がうまくいかない理由は一緒なんだろうという気がしている。ただその原因が全く分からない。ググっても同じような人は見当たらない。どこかでタイポしているのだろうか。なんなのか。今日はずっとこれの原因を探していたがもう限界である。

(追記)
 ノートブック
 やはりオーバーフローの類が原因として濃厚だったためバッチサイズを10にしたところ、たまに学習がうまくいくようになった。ただやはりうまく学習するモデルとそうでないモデルの違いがよくわからず模索した跡が、ノートブックのmodel1~3の部分。
 そんな時に、学習と関係ないコマンドを追加しただけなのにうまく学習が進んでいたモデルがうまく学習しなかったところでハッと気づいた。そこで上のmodel2を10回繰り返したのが、ノートブック後半部。
 結果としてうまくいったりうまくいかなかったりするものとが出てきたので、なるほどなと。こういうこともあるのだな。

 しかし、「局所解にハマってlossがなかなか下がらない」ならともかく、「lossがドカンと上がってしまう」というのは、一体何が起きているのか直感的によくわからないですね。やっぱり詳しい人教えてください。

2019年5月1日水曜日

影が重なる時に伸びるように見えるやつ(2)

 今回のソースコードはこちら。

 まあ前回とほとんど変わってないです。変わったところは2点。

(1)integrate.quadをリストラし、単純な区分求積法で代用
 その結果、有限の連続光源の再現時に中央上に出現していた明るさ0未満の領域がなくなった。


 また、dが10^-2のオーダーだった時に出現していた明るい部分のボツボツの影も消えた。 


 そんなわけで、前回の反意図的な部分はintegrate.quadが影響していた可能性が高いです。

(2)照明パターンを追加
 (イメージとしては蛍光灯が間隔をあけて並んでいるような、)連続光源があったりなかったりするパターンを考えていなかったので、system_dottedクラスを作って実験。照明の長さ0.5、間隔0.5での結果。


 あっ、キレイ…( ^ω^)

 割と満足した結果が得られたのでこの話題は終わりかな。
 この記事書いてて思い出したものとして、「レンズの目の前にある物体の周りの像がゆがむ」のも再現できるかなーと思ったので、そのうち暇つぶしでやります。

(追記)
 新しい照明パターン、これあってんのか?a大きくしてると明るかった部分が明るくなったり暗くなったりするんか?

(追記2)
 まず画像をよく見ると、縞模様は縦と斜めの2つが出現している。思考実験上、縦の縞模様は光の当たり方(照明との距離、光の当たる角度)の影響で説明できそうなので、問題を簡単にするためにもまず縦の縞模様から取り組む。
 光の減衰の影響を減らして全体的にベターッと照らせば距離や角度の影響を除くことができそうなので、天井を上げてみる。これまでは天井の高さH=3としていたが、H=5, 10, 50と変えてみたところ次のようになった。




 H=5では変化は明らかでない。H=10で縦の縞模様が消えきるかと思ったが、意外にも斜めの模様だけ消えて(実際は見えないだけと思われる)、縦だけくっきり残っている。H=50にするとどちらも消えた(同)。
 この結果と思考実験から、縦模様に関してはもういいでしょう。
 積分のステップ幅の影響も考えて変えてみたが、模様に変化なし。
 光源の間隔が短くなったら分岐点が増えたりするかと考えて変えてみたが、模様に変化なし。
 触れたくない事実として、aはここまで0から1までしか動かしていない。なのでここまで見ていた影はかなり狭い範囲での出来事を確認していたことになる。これはaを大きくすると積分区間も計算しなければならないxも増えるからなのだが、他に考えることもなくなってしまったのでしぶしぶ着手する。ついでに座標軸もちゃんとググって正しくしたよ偉い!(1行追加するだけだったので早くググればよかったと後悔している(アホ))
 a=3, 5としたもの。




 どうもxの幅1くらいの範囲で起きているようである。板P2の左端通過0.5前~0.5後ってところやね。
 現時点でこの縞模様が正しいものなのかそうでないのかの区別がついていないのが問題である。今後は思考実験でこれを再現できるかどうかと、コードの確認と、2つを続けていかなければならなさそうである・・・。

2019年4月28日日曜日

影が重なる時に伸びるように見えるやつ


 影と影が重なる時に一方の影が伸びるように見えたり縞模様ができたりするのがすごい好きで暇つぶしによく使っていたのだが、これってプログラムで再現できるかな?とふと思ったのでやってみた。
 感覚一辺倒で「はっきりした影の外側のぼや~っとした影が重なることからなんやかんやでいろんな風に見えるんやろ」と思っていたことから、(1)光源に大きさがあること (2)光源が複数存在すること が影響していると予想し、色々考えた結果次の4パターンの再現を試みた。

(1)大きさ無限大の連続した光源
(2)大きさ有限の連続した光源
(3)一定間隔で無限に並ぶ点光源
(4)一定間隔で有限に並ぶ点光源

 なお3次元空間で考えるのは大変なので、2次元平面で考えた。

[設定]
 床と高さHの天井がある。-∞~0に高さh1で板P1が、a~∞に高さh2で板P2があり、いずれも水平で厚さはない。またh1>h2とする。
 天井のある点yが発する光が点xを照らす強さf(x, y)は、入射角θとした時のsinθに比例し、その点間の距離で求められる球の表面積Sに反比例するものとする。つまり定数lを用いて、f(x, y)=(l*sinθ)/Sとする。
 光源が連続の場合、天井の-d~dは発光している(蛍光灯がある)とする。光源が点の場合、-d~dの範囲の整数の点が発光している(電球がある)とする。無限に光源が続いていることを仮定する場合、d=∞とする。
 ごちゃごちゃ書いたけど図にすればこれだけのことです。


[実装にあたり]
・点xを照らす点yの条件を計算すると-x*(H-h1)/h1<y<x+(a-x)*H/h2なので、f(x, y)をこの範囲でyについて積分すると、点xの明るさI(x)が求まる。
・I(x)を計算するxの範囲は-1<x<h1*max(a)/(h1-h2)とした。上限は絶対に光が差さない領域との境界。下限は特に理由はないが、感覚としてx<0では特に面白いことは起きなさそうだと思ったため適当に決めた値である。
・yを与えられたときに|y|<dならlを、そうでなければ0を返す関数light_existをf(x, y)の定数lに突っ込むことで、汎用性をあげた。

[結果確認]
 結果はヒートマップ形式で見る。マス目の値の弄り方をググるのが面倒だったので、対応させていない点に注意。最上段はa=0であり、下に行くにつれてaが大きくなる(つまり板P2を右へ移動している)。左右はxの値であり、左端がx=-1、右端がx=h1*max(a)/(h1-h2)である。

(1)大きさ無限大の連続した光源


 上右端より上中ちょい左の方が暗いことになっているのさすがに解せないし、しかもその領域は明るさ0を下回っているあたりダメそう。その部分を華麗に無視して明るいところのパターンを見ても、そこまで面白い部分は見つからない。

(2)大きさ有限の連続した光源







 やはり無限の場合と同じく、0未満の領域が気になる。ダメそう。

(3)一定間隔で無限に並ぶ点光源


 直感的に縞模様はこれで納得できますな。また、この図の一部に具体的な数値を入れてみると、


となり、aが小さくなるとそれまで明るかった部分が一気に暗い側に近い値になることで、影が伸びるように見えると考えることもできそうである。

(4)一定間隔で有限に並ぶ点光源







 点光源が増え影の境界が増えることにより、シマシマも増えるってな具合ですな。

 と、こんなんで一応の解決を得たっぽいのだが、連続光源の場合の結果が気持ち悪い。今デスクスタンド(≒有限の連続光源)でマクロに実験してみたところ影は伸びるように見えたので、どうも再現に失敗しているようである。
 さらには、(2)の場合でdのオーダーを下げてみたところ次のようになった。





 この結果は、aを大きくしていくと、明るい部分にぼつぼつと影が出現し始める、ということを意味する。波動の干渉とかならともかく、今回の例でこれはナンセンス。このようにおかしな結果が得られたことに加え、明るさが負になったりしたあたりなども含めて、integrate.quad関数あたりが影響しているような気がする。ぼつぼつの影については、積分区間に対してdが小さすぎることで、ステップ幅的に光源の領域をスキップし、積分値がゼロになってしまったのではないか。その他なんやかんやで反意図的に負の値が出てきたのではないかなぁ~と思ったあたりで疲れたので今日はここまで。

2019年4月21日日曜日

英文をコピペでgoogle翻訳に突っ込む時、改行コードとかが邪魔なので一括で消したい

 この記事のソースコードはこちら。

 改行コードはtext.replace('\n', '')で消せた。
 改行コードの他に消したいものを考えたら、文章中でreferenceがその都度括弧で記載されていて読みにくい時があるのでそれも消したい。どうやらreモジュールで正規表現を使えるようなので使います。

def fixer(text):
    #改行コード削除
    text=re.sub('^\n', '', text)
    text=re.sub('\n$', '', text)
    text=text.replace(' \n', ' ')
    text=text.replace('\n', ' ')
 
    #括弧削除
    text=re.sub(' \(.*?\)', ' ', text)
    text=re.sub('\(.*?\)', '', text)
 
    #カンマ・ピリオド前の空白削除
    text=text.replace(' ,', ',')
    text=text.replace(' .', '.')
 
    return text


 正規表現で最短一致列が欲しい時には?マークを使うのは知らなかった。
 なにはともあれ正規表現で削除したいものを削除できるようになった。これで他にもいろいろ消したいものが出てきたときは消せそうです。

 上の関数を使って、コピペしたい文章をその都度jupyterに貼り付けてRun、でもいいのだが、もうちょっと使い勝手良くしたいなと思ったので、tkinterでGUIにした。

 paster.pyを実行すると、ただの2枠と中央のボタン1個のウインドウが出てくる。左に元の文章コピペして矢印ボタン押すと、右枠に処理済のテキスト出てくるようになっている。


 なおpyperclipをインストールしてあれば、paster_clip.pyが使える。こっちだと矢印ボタンを押した時点でクリップボードに処理済みテキストがコピーされる。

 今後の課題は以下。
・括弧は一括で削除しているが、本当はreferenceっぽいものだけを選択的に削除したい。正規表現の中に年号を含むようにすればいけそうではある。
・ボタン押さなくても変換してほしい。
・googletransというモジュールがある。これ使えば自動で翻訳してくれるような気もするが、インターネットで翻訳した方が精度が良さそうな気もする。そのあたりの検証も含めてgoogletransは近日中に触りたい。