Briswell Tech Blog

ブリスウェルのテックブログです

Elastic Beanstalk のエラー

暑い日々が続いておりますね。
みなさま、いかがお過ごしでしょうか。

今回はElastic Beanstalk(以下EBと記載します) のサポート対象から Node.js 12 が消滅するので、16にあげる作業を行った際に、よくわからないエラーにぶち当たったので、備忘録含め、記載します。


Node.js 12 running on 64bit Amazon Linux 2 から
Node.js 16 running on 64bit Amazon Linux 2 にアップデートを行おうと、少し調整を入れ、Circle CIからEBへデプロイをかけたところ、

CIエラー

ERROR: TimeoutError - The EB CLI timed out after 10 minute(s). The operation might still be running. To keep viewing events, run 'eb events -f'. To set timeout duration, use '--timeout MINUTES'.

タイプアウトでデプロイが失敗しました。
ログを出力してみると何も出ていない...

よくわからないので、ダメ元で再度CIを実行しました。

CIエラー2

2022-08-22 04:12:40 ERROR Instance deployment: 'npm' failed to install dependencies that you defined in 'package.json'. For details, see 'eb-engine.log'. The deployment failed.
2022-08-22 04:12:40 ERROR Instance deployment failed. For details, see 'eb-engine.log'.
2022-08-22 04:12:41 ERROR [Instance: i-0b7da73e780dfead2] Command failed on instance. Return code: 1 Output: Engine execution has encountered an error..
2022-08-22 04:12:41 INFO Command execution completed on all instances. Summary: [Successful: 0, Failed: 1].
2022-08-22 04:12:41 ERROR Unsuccessful command execution on instance id(s) 'i-0b7da73e780dfead2'. Aborting the operation.
2022-08-22 04:12:41 ERROR Failed to deploy application.



今度はエラーが出ていたので、詳細確認のため、ログを見てみると

2022/08/22 04:12:40.550548 [ERROR] An error occurred during execution of command [app-deploy] - [Use NPM to install dependencies]. Stop running the command. Error: Command /bin/sh -c npm --production install failed with error signal: killed. Stderr:npm WARN config production Use --omit=dev instead.

2022/08/22 04:12:40.550567 [INFO] Executing cleanup logic

2022/08/22 04:12:40.615852 [INFO] CommandService Response: {"status":"FAILURE","api_version":"1.0","results":[{"status":"FAILURE","msg":"Engine execution has encountered an error.","returncode":1,"events":[{"msg":"Instance deployment: 'npm' failed to install dependencies that you defined in 'package.json'. For details, see 'eb-engine.log'. The deployment failed.","timestamp":1661141560538,"severity":"ERROR"},{"msg":"Instance deployment failed. For details, see 'eb-engine.log'.","timestamp":1661141560558,"severity":"ERROR"}]}]}



このようなログが吐き出されていました。
Local で
npm --production install

を実行してもエラーは起こりませんでした。

少しGoogle先生に質問をしたところ、

stackoverflow.com

この記載を見つけました。
インスタンスのスペックが低すぎるのが原因ではないか。
確かに上記エラーが発生したEBのスペックは t3.micro でした。

藁にも縋る気持ちで、t3.medium に更新を行い、再度チャレンジしました...

結果は成功。


ログだけ見てもよくわからない系のエラーは悩ましいですね。解決できてよかったです。

Pythonで3D地形図化

弊社のオフィスのある芝大門は、江戸時代より増上寺門前町として多くの商店もあり賑わっています。以前、ご紹介しましたが江戸の大名屋敷もすぐそばにありました。戻れるものならこの目で見てみたいものです。

このあたりは、江戸時代に埋め立てがされ、それ以前は海の下(日比谷入江)だったようです。
国土地理院標高タイルの数値データより3D地形図を作成し、その事実を探っていきたいと思います。

matplotlibで国土地理院標高タイルから3D地形図を描いてみる - プログラム日記φ(..)

こちらの記事を参考にさせていただきました。ありがとうございました。

では、行って参る。

1. モジュールのインポート

よく使われるモジュールですね。

import pandas as pd #csvファイル読み取り用
import requests #HTTP通信用
import numpy as np #数値計算用
import matplotlib.pyplot as plt #グラフ作成用
from io import StringIO #文字列をファイルのように扱える

2. 標高データの読込

こちら国土地理院標高タイルについて、芝大門周辺の4つのタイルを取得します。

url_1 = 'http://cyberjapandata.gsi.go.jp/xyz/dem/13/7276/3226.txt' #芝大門を含むタイルNo.1
url_2 = 'http://cyberjapandata.gsi.go.jp/xyz/dem/13/7275/3226.txt' #西側のタイルNo.2
url_3 = 'http://cyberjapandata.gsi.go.jp/xyz/dem/13/7276/3227.txt' #南側のタイルNo.3
url_4 = 'http://cyberjapandata.gsi.go.jp/xyz/dem/13/7275/3227.txt' #南西側のタイルNo.4

urlList = [url_1, url_2, url_3, url_4]
z_result = []

for url in urlList:
  response = requests.get(url)
  maptxt = str.replace(response.text, u'e', u'-0.0')
  Z = pd.read_csv(StringIO(maptxt), header=None)
  z_result.append(Z)

3. 標高データを結合

読み込んだ4つのタイルの標高データを結合します。

z1 = np.concatenate((z_result[1],z_result[0]), axis = 1) #No.1とNo.2を結合
z2 = np.concatenate((z_result[3],z_result[2]), axis = 1) #No.3とNo.4を結合 

z_mix = np.concatenate((z1,z2), axis = 0) #No.1&2とNo.3&4を結合

4. 3Dグラフの設定

3Dグラフのデザイン部分です。

X, Y = np.meshgrid(np.linspace(0,255,512), np.linspace(255,0,512))
fig = plt.figure(figsize=(10, 8), dpi=80, facecolor='w', edgecolor='k') #w:白(White), k:黒 (Black)
ax = fig.gca(projection='3d') #3Dグラフ
ax.set_aspect('auto') #Axes3D currently only supports the aspect argument 'auto'.
ax.view_init(84, -67.5) #視点の設定

5. 3Dグラフを描画

標高値に応じて曲面を描きます。また、弊社のオフィス位置あたりに★を出力します。

surf = ax.plot_surface(X, Y, z_mix, cstride=1, rstride=1, cmap='rainbow', antialiased=False)
ax.text(140, 195, 2, "★", 'z', color='white', size='xx-large')
cb = plt.colorbar(surf, shrink=0.5, aspect=10)

6. 3Dグラフを保存

さて、どうでしょうか。

plt.savefig('mix_map.jpg')

7. 結果

江戸の見晴らしの名所と言われた愛宕山や、芝公園辺りから標高が高くなっているのが確認できますね。

Pythonで旅する東海道五十三次

東海道五十三次のルートは、日本橋(江戸)〜三条大橋(京)の全長約490kmの街道です。

江戸時代の人は、徒歩で約二週間かけて旅していたとのこと。一日の移動距離は平均すると35km(一歩が70cmとすると5万歩)となります。すごいですね。

私もその昔、東海道五十三次を自転車でトライしたのですが、小田原〜箱根間で転倒し、箱根の坂を前に挫折しました。

今回、Pythonで再チャレンジしたいと思います。

1. モジュールのインポート

import folium #地図作成用
from folium.features import DivIcon #文字列表示用
from folium import plugins #機能拡張用

2. 東海道五十三次の宿場を定義

shukuba_list = \
[['日本橋',139.774444444444,35.6836111111111], 
['品川宿',139.739166666667,35.6219444444444], 
['川崎宿',139.707777777778,35.5355555555556], 
['神奈川宿',139.632277777778,35.4727777777778], 
['保ヶ谷宿',139.595555555556,35.4440277777778],
['戸塚宿',139.529861111111,35.3950277777778], 
['藤沢宿',139.486305555556,35.3456666666667], 
['平塚宿',139.337805555556,35.3272777777778], 
['大磯宿',139.315305555556,35.309],
['小田原宿',139.161027777778,35.2487222222222], 
['箱根宿',139.026361111111,35.1904166666667], 
['三島宿',138.914472222222,35.11925], 
# 40宿場省略
['草津宿',135.960638888889,35.0174444444444], 
['大津宿',135.861416666667,35.0059722222222], 
['三条大橋',135.774361111111,35.0103333333333]]

※緯度・経度の引用元
東海道五十三次 - Wikipedia

3. 地図を作成

tokaido_map = folium.Map(location=[35.360626, 138.727363], zoom_start=8, tiles='stamenterrain') #富士山の座標を中心に地図を作成

for i in range(len(shukuba_list)):
    length=len(shukuba_list[i][0]) #宿場名の文字数を取得 
    if i == 0 or i == 54: #始点と終点は★を表示
        folium.Marker(location=[shukuba_list[i][2], shukuba_list[i][1]], icon=DivIcon(
            icon_size=(25, 25),
            icon_anchor=(0, 0),
            html='<div style="text-align: center; font-size: 11pt; color : black width: 25px; height: 25px; background: skyblue; border:2px solid #666; border-radius: 50%; ">'+"★"+'</div>'),
            popup=shukuba_list[i][0]+(' '*length)).add_to(tokaido_map) #全角スペースを入れないとポップアップの文字が改行されてしまう
    else: #始点と終点以外は宿場順を表示
        folium.Marker(location=[shukuba_list[i][2], shukuba_list[i][1]], icon=DivIcon(
            icon_size=(25, 25),
            icon_anchor=(0, 0),
            html='<div style="text-align: center; font-size: 11pt; color : black width: 25px; height: 25px; background: skyblue; border:2px solid #666; border-radius: 50%; ">'+str(i)+'</div>'),
            popup=str(i)+'.'+shukuba_list[i][0]+(' '*(length+1))).add_to(tokaido_map) #宿場名をポップアップ表示

4. フルスクリーン機能を追加

plugins.Fullscreen(
    position="topright",
    title="拡大する",      
    title_cancel="元に戻す",
    force_separate_button=True,
).add_to(tokaido_map)

5. HTMLに保存

tokaido_map.save("tokaido-53.html")

結果

自分の足で箱根越えをしたいですね。

File:NDL-DC 1309891-Utagawa Hiroshige-東海道五拾三次 吉原・左富士-crd.jpg - Wikimedia Commons

また、こちらの浮世絵に描かれているように、13.原宿〜14.吉原宿の間で左側に富士山が見える場所があります。箱根越えの後の楽しみです。

文章から画像を生成してみよう

こんにちは.AI,IoT担当の大澤です.

最近AI界隈では文章から画像を生成するText to Imageタスクの分野で大変な盛り上がりを見せていました. OpenAIのDALL-E2GoogleImagenなど,高画質で高精度な画像を生成するモデルが相次いで発表されたことが大きいのではないでしょうか.

Text to Imageとは読んで字のごとく,入力された文字列をもとに画像を生成するタスクのことです.下の画像を見てみましょう.

麦わら帽子とサングラスを身に着けたサボテンですね.Photoshopで作られた作品のように見えますが…実はこちら,Googleが発表したImagenというAIが作成したものです.

こちらはまずAIへの入力として「A small cactus wearing a straw hat and neon sunglasses in the Sahara desert.」という文章が与えられます.日本語訳では「サハラ砂漠で麦わら帽子をかぶり、ネオンサングラスをかけた小さなサボテン。」です.

するとこの画像が出力されるわけです.しっかりと文章の意味を理解し,的確に画像へと変換されています.

「A photo of a Corgi dog riding a bike in Times Square. It is wearing sunglasses and a beach hat.(タイムズスクエアで自転車に乗っているコーギー犬の写真。サングラスとビーチハットをかぶっています。)」という文章を与えると次のような画像が生成されるようです.

面白いですね.実は今までにもこのようなタスクができるモデルはあったのですが,transformerや拡散モデルなど,近年生まれた技術の組み合わせでここまでクオリティの高い画像生成を行うことができるようになったようです.

ただ,これらは膨大な計算リソースを必要とするため,一般的なPCなどでは実行できません.代わりに計算量を大幅に削減したモデルで,画像生成を体験してみましょう.

こちらのサイトに掲載されているgooglecolabのdemoコードを使用させていただきました.

www.12-technology.com

まずはimagenのサイトにもあった文章と同じものを入れてみましょう.

A photo of a Corgi dog riding a bike in Times Square. It is wearing sunglasses and a beach hat.

流石にGoogleのようにはいきませんが,それっぽく見える画像が生成できました.

他にも色々試してみましょう.

A photo of snowman sunbathing on a beach in summer. Snowman is wearing sunglasses and a beach hat.

真ん中上部のガッツポーズが最高ですね.

Photo of a cat wearing sunglasses at the wheel of a convertible.

左下のボケ感がじわじわきますね.

Illustration of a sunflower wearing sunglasses eating ice cream.

イラスト風にもできました.

とてもおもしろいですね.夏なのでサングラス多めでお送りしました. それでは.

Pythonで夏を感じる

厳しい暑さが続きますね!
この1週間、ずっと30℃超えが当たり前で梅雨はどこ?って思っておりましたが、やはり梅雨明けしたようです。

OpenWeatherMapAPIにより全世界の天気データを取得できます。
PythonからこのAPIを呼び出し、取得した天気データをグラフ化して、一足先に夏を感じたいと思います。

1. モジュールのインポート

import requests
import pandas as pd
import plotly.graph_objects as go
from pytz import timezone
from datetime import datetime

APIKEY = "" #APIキー
LATITUDE = "" #緯度
LONGITUDE = "" #経度
BG_COLOR = "#100500" #グラフ背景色
FONT_COLOR = "#fff5f3" #グラフ文字色
TITLE = "東京都港区の体感気温と湿度" #グラフタイトル

2. 天気情報を取得

url = "http://api.openweathermap.org/data/2.5/onecall"
payload = {"lat": LATITUDE, "lon": LONGITUDE, "lang": "ja", "units": "metric", "APPID": APIKEY}
weather_data = requests.get(url, params=payload).json()
tz = timezone(weather_data['timezone'])
wd = weather_data['hourly'] # 現在から48時間先までの1時間毎のデータを取得

3. 日時、体感気温、湿度をリスト化

weather_data_list = []
for w1 in wd:
    tmp_dict = {}
    tmp_dict['日時'] = datetime.fromtimestamp(w1['dt'], tz=tz).strftime("%m/%d %H時")
    tmp_dict['体感気温℃'] = int(w1['feels_like'])
    tmp_dict['湿度%'] = int(w1['humidity'])
    weather_data_list.append(tmp_dict)

df_temp = pd.json_normalize(weather_data_list)

4. グラフ化

# X軸:日時、Y軸:体感気温
trace1 = go.Scatter(
    x = df_temp['日時'],
    y = df_temp['体感気温℃'],
    mode = 'lines',
    name = '体感気温℃',
    yaxis='y1',
    line=dict(color='#da70d6')
)

# X軸:日時、Y軸:湿度
trace2 = go.Scatter(
    x = df_temp['日時'],
    y = df_temp['湿度%'],
    mode = 'lines',
    name = '湿度%',
    yaxis='y2',
    line=dict(color='#87cefa')
)

# Y2軸グラフ化
layout = go.Layout(dict(margin=dict(l=0, r=0, t=30, b=0), paper_bgcolor=BG_COLOR, plot_bgcolor=BG_COLOR,
            title=dict(text=TITLE, x=0.5, y=1.0, font=dict(color=FONT_COLOR, size=24), xanchor='center', yanchor='top', pad=dict(l=0, r=0, t=5, b=0)),
            font=dict(color=FONT_COLOR, size=18),
            xaxis=dict(title='日時', showgrid=False),
            yaxis=dict(title='体感気温℃', side = 'left', showgrid=False, range = [10, 40]),
            yaxis2=dict(title='湿度%', side = 'right', overlaying = 'y', showgrid=False, range = [40, 100])
))
fig = go.Figure(dict(data = [trace1,trace2], layout = layout))

# グラフ画像を保存
fig.write_image("weather_graph.png", height=400, width=1600, scale=1)

5. 結果

夏ですね!
ただ日中の湿度はそこまで高くないのでハワイのようにカラッとして過ごしやすそうです。

Pythonで銀河観測

先日、私たちが住んでいる「地球」が属している「太陽系」が属している「天の川銀河」(地球 ⊂ 太陽系 ⊂ 天の川銀河)の中心にあるブラックホールの姿が初めて撮影されました。すごい時代になったものです。

「Uppsala General Catalogue of Galaxies (UGC)」という北半球から見える約1万3千個の銀河のカタログがあります。そのカタログの中に、積分記号( ∫ )のような形の渦巻銀河「UGC 3697」があります。今回はその銀河をPythonで確認してみましょう。

銀河の画像を複数枚タイル状に表示する方法 - Qiita
pythonを用いて銀経銀緯で複数のfits画像を並べてプロットする方法 - Qiita

こちらの記事を参考にさせていただきました。ありがとうございました。

1. モジュールのインポート

from astroquery.vizier import Vizier #Vizierカタログのデータ取得用
from astroquery.skyview import SkyView #fitsファイル取得用
from astropy.wcs import WCS #天球上の座標取得用
from astropy.io import fits #fitsファイル読込用
import glob #ディレクトリのファイル取得用
import matplotlib.pyplot as plt #グラフ描画用

2. カタログ存在確認

catalog = "UGC"
code = "3697"
v = Vizier(catalog="VII/26D/catalog",column_filters={catalog:"="+code})
data = v.query_constraints()
print(data[0])

出力結果は以下です。UGCカタログ(VII/26D/catalog)に存在していますね。

UGC   A   RA1950  DE1950      MCG      MajAxis MinAxis  Hubble Pmag  i 
         "h:m:s" "d:m:s"                arcmin  arcmin         mag     
---- --- ------- ------- ------------- ------- ------- ------- ---- ---
3697     07 05.6  +71 55 MCG+12-07-028    3.30    0.20 INTEGRL 13.1  --

3. fitsファイルを保存

def save(p,name,obs): 
    for onep,oneo in zip(p,obs):
        onep.writeto(name+"_"+oneo+".fits",overwrite=True)

path_fits = "./"
galaxy = catalog+code
olist = ["DSS"] #Digitized Sky Survey

paths = SkyView.get_images(position=galaxy,survey=olist)
save(paths,path_fits+galaxy,olist)

4. fitsファイルを元に画像を作成

# fitsファイルを取得
dss = glob.glob("./*_DSS.fits")

# グラフ描画領域を作成
F = plt.figure(figsize=(6,8))

# fitsファイルの読込
dssfilename = dss[0]
dssname = dssfilename.replace(".fits",".png")
dsshdu = fits.open(dssfilename)[0]
dsswcs = WCS(dsshdu.header)
dssdata = dsshdu.data

# グラフタイトルを定義
plt.figtext(0.45, 0.93, galaxy, size="large")

# オリジナル画像表示
plt.subplot(211, projection=dsswcs) #2行1列の1番目
plt.imshow(dssdata, origin='lower')
plt.grid(color='white', ls='dotted')

# 拡大画像表示
dssxlen, dssylen = dssdata.shape
dsscx = int(0.5 * dssxlen)
dsscy = int(0.5 * dssylen)
dssdx = int(dssxlen * 0.15)
dsswcscut = dsswcs[dsscx-dssdx:dsscx+dssdx,dsscy-dssdx:dsscy+dssdx]

plt.subplot(212, projection=dsswcscut) #2行1列の2番目
plt.imshow(dssdata[dsscx-dssdx:dsscx+dssdx,dsscy-dssdx:dsscy+dssdx], origin='lower')
plt.grid(color='white', ls='dotted')

# 画像保存
plt.savefig(galaxy + ".png")
plt.close()

5. 結果

宇宙に浮かぶ ∫ なんとも素敵ですね...

Pythonで降らせる流星群

4月23日は「4月こと座流星群」の極大でした。

明け方から活動ピークを迎えるということで、夜空を見上げましたが...
やはり都会の明るい夜では、星はかけらも見当たりません。ライブカメラの観測に切り替えました。

今年も数は少なかったですが、明るい流れ星もあり、楽しむことができました。あのキラッと流れるのも見た瞬間は心が躍ります。

さて、今回はその感動を分かち合いたく、満天の星空に流星群を降らせてみます。

GitHub - yu4u/shooting-star-generator: Generate synthetic shooting star images

こちらのコードを参考にさせていただきました。ありがとうございました。

1. 流れ星の形状決め

numpy.random.randint(min,max) # min〜maxの範囲の整数
numpy.random.rand() # 0.0以上、1.0未満

# 80〜180の範囲の整数値を取得する場合
length = 80 + np.random.randint(0,100)

# ランダムな角度を取得する場合
theta = 0.7 * pi * np.random.rand() + 0.2 * pi

NumPyのrandomモジュールを使用して、流れ星の表示時間、長さ、明るさ、ヘッドと尾の部分の大きさ、流す角度等を決める乱数値(ランダムな値)を取得します。

2. 流れ星の画像生成

cv2.line(image, start_point, end_point, color, thickness) # 対象画像、開始座標、終了座標、線の色、線の太さ

OpenCVのline関数を使用して、流れ星のヘッドと尾の線を引きます。

画像を繋げたもの(アニメーションGIF)がこちらになります。

Pythonの画像処理ライブラリPillowのsave関数を使うと、アニメーションGIFを作成することができます。

# 画像imに、画像リスト[im1, im2, ...]を追加してアニメーションGIFを作成する場合
im.save('anime.gif', save_all=True, append_images=[im1, im2, ...])

3. 流れ星を回転・移動

cv2.warpAffine(image, affine, (size_x, size_y)) # 対象画像、アフィン変換の行列、出力のサイズ

放射状に見える流星群のイメージに近づけるために、OpenCVのwarpAffine関数を使用して、流す角度や位置を変えます。

4. 背景を透過

# 黒色(R0 G0 B0)のαチャンネル(画素の不透明度)を0にする場合
image[:, :, 3] = np.where(np.all(image == 0, axis=-1), 0, 255)

満天の星空に降らすために、黒色の背景を透過します。

5. 満天の星空と合成

明日の朝、砂浜にほしのかけら落ちているかしら。

RDS for MySQL 5.6 → 5.7 でのアップデートエラー

RDS Aurora For Mysql 5.6 のサポートが来年2月に切れるとメールが入ったので、いくつかの案件で対応を始めました。
基本的にはコンソールから、バージョンを変更して適用すれば良いのかな、と思っていたのですが、1プロジェクトで、下記画像のエラーが出てしまい、躓きました。

f:id:Toshi_bw:20220331184431p:plain
Error


申し訳ありませんが、DBクラスター xxx を変更するリクエストが失敗しました。
Cannot modify engine version because Instance xxxxx has Performance Insights enabled and the new version doesn't support this feature.

はぁ...
Performance Insights が有効になっているので、更新できないよ。
と言われているのはわかってるのですが、RDSコンソール画面で Performance Insights の有効 / 無効の項目が出てない...


わからないので、AWS サポートに問い合わせを行ったところ、下記回答をいただきました。

調査した結果、お客様の場合は Performance Insights をサポートしていないインスタンスクラス(db.t3.small)をご利用いただいているため、マネジメントコンソール上に Performance Insights メニューが表示されないことが判明しました。恐らく DB インスタンスの変更メニューより何かを変更される際にも、以下のようなメッセージが表示されるかと存じます。 Performance Insights not supported for this configuration, please disable this feature. Performance Insights をサポートするインスタンスクラスで Performance Insights を有効にした状態で、インスタンスクラスを Performance Insights 非サポートのものに変更する場合に、同様な問題が発生することが報告されております。


迅速なご回答に感謝しつつ、 AWS CLI で対応してね、とのことだったので下記ドキュメントを参考しました。

docs.aws.amazon.com

AWS CLI 自体の設定はこちら

docs.aws.amazon.com

結果、実行したコマンドは
aws rds modify-db-instance --db-instance-identifier instance-name --no-enable-performance-insights --profile projectName


こちらで無事無効化を行えました。
その後、RDS コンソールから5.7 へのバージョンアップも無事行えました!!

対応期限にはまだ時間はあるものの、どういったところで躓くかわからないので、色々なプロジェクトに対し早め対応を行っていこうと思います!!

より良いシステム開発・運用を求めて...

Pythonで観る宇宙

桜が見頃になってきましたね!
ついこの間咲き始めたと思ったら、いつの間にやらもう満開。季節の巡りは早いものです。

今回は天体観測をします。
春の夜には早々と消えていきますが...
私の大好きなオリオン座の星雲を観てみます。

赤外線天文衛星「あかり」などの望遠鏡によって撮影された写真をいじってみたり - Qiita

こちらの記事を参考にさせていただきました。ありがとうございました。

Astroqueryというモジュールを使うことで、SkyViewの天文写真をPythonで取得することができます。望遠鏡を覗き込まずに、プログラムから天体観測ができるなんてとても素敵です。

では、いってみましょう。

1. モジュールのインポート

import numpy as np #数値計算用
import matplotlib.pyplot as plt #描画用
from astroquery.skyview import SkyView #天文写真取得用
from astropy import units as u #取得範囲設定用
from astropy.wcs import WCS #天球座標変換用
from astropy.visualization import ZScaleInterval,ImageNormalize #画像補正用

astropyは天文データ解析用のパッケージです。

2. 設定値

target = 'M42' #オリオン大星雲
survey = ['2MASS-K','2MASS-H','2MASS-J'] #近赤外線波長域での天文観測プロジェクト
radius = 30*u.arcmin #取得する写真の範囲
pixels = 600 #取得する写真の大きさ
figsize = [6,6] #画像表示エリアのサイズ

オリオン座の散光星雲を取得する場合は、targetに「NGC 2024」を設定します。

3. 天文写真取得

hdu = SkyView.get_images(target,survey=survey,radius=radius,pixels=pixels) #天文写真を取得
wcs = WCS(hdu[0][0].header) #WCS座標を取得
plt.figure(figsize=figsize).gca(projection=wcs,title=target) #取得した座標をプロットする
plt.imshow(np.stack([ImageNormalize(hdu[i][0].data,interval=ZScaleInterval())(hdu[i][0].data) for i in range(3)], axis=2)) #強調化・合成して画像を表示 
plt.savefig(target + '.jpg') #画像保存
plt.close() #クローズ

天体の天球上の位置は、WCS座標で表すことができます。

4. 結果

感動です。

Power Automate DesktopでCSVファイルをダウンロードしてSQL文を生成してみた

f:id:ri_86:20220322180109p:plain

こんにちは、「ラクして速く」がモットーの辻本です。

このWebサイトに月1でアップされるCSVファイルの情報を、DBに取り込んでください。

そのようなとき、みなさんどうしますか?

CSVファイルのデータ形式がDBと一致しており、新規追加のみであれば、何かツールを使ってDBに取り込むことができるでしょう。

ただ、データが分割されていたり、桁数やフォーマットが違っていたり、DB側で固定値が必要だったり... はたまた、INSERT(追加)ではなく、UPDATE(更新)またはDELETE(削除)の場合は、何かしら加工した上、SQL文を作成して実行する必要があります。

その際、手動でExcelにデータを貼り付けて、加工した上、ごりごりSQL文を作るのはちょっとスマートではないですね。時間もかかります。

そこで登場するのがRPAです。今回はPower Automate Desktopを使ってその作業を完全自動化してみましょう。



【1】フロー完成図

f:id:ri_86:20220317165237p:plain f:id:ri_86:20220317165240p:plain ▲作成するフローの完成図になります。


【2】WebサイトからCSVファイルをダウンロード

f:id:ri_86:20220317182702p:plain ▲今回は「レコーダ」を使用し、WEBサイトからCSVをダウンロードするフローを作成します。

f:id:ri_86:20220323123422p:plain ▲上図のようにWEBサイトのダウンロードボタンを押下する処理をレコーダーで追加します。

f:id:ri_86:20220316170659p:plain ▲ダウンロードしたCSVファイルの中身です。


【3】Excel の起動

f:id:ri_86:20220317142915p:plainExcelカテゴリの中にあるExcel の起動」アクションを追加します。

f:id:ri_86:20220317150759p:plain「ドキュメントパス」は【2】でダウンロードしたCSVを指定してください。


【4】Excel ワークシートから最初の空の列や行を取得

f:id:ri_86:20220317144031p:plainExcelカテゴリの中にあるExcel ワークシートから最初の空の列や行を取得」アクションを追加します。

f:id:ri_86:20220317151059p:plainExcelインスタンスは【3】で作成された変数「ExcelInstance」を指定します。

Excel ワークシートから最初の空の列や行を取得」についてですが、
取得したCSVのデータのある最後の行数・列数の+1を取得するアクションです。
今回の場合、下記のように取得できます。
FirstFreeColumn = E すなわち 5
FirstFreeRow = 6

f:id:ri_86:20220317154104p:plain


【5】Loop処理を追加

f:id:ri_86:20220317170630p:plain「ループ」カテゴリの中にある「Loop」アクションを追加します。
「End」アクションは「Loop」アクションを追加すると自動で追加されます。

f:id:ri_86:20220317160536p:plain ▲「終了」についてですがFirstFreeRow = 6 なので
6 - 1 = 5 となり、5回ループすることになります。


【6】Excel ワークシートに書き込み

f:id:ri_86:20220317170624p:plainExcelカテゴリの中にあるExcel ワークシートに書き込み」アクションを追加します。

ループの中にこのアクションを設定することにより、Excel ワークシートに書き込み」アクションをループさせることができます。

f:id:ri_86:20220317160527p:plain

Excelインスタンスは【3】で作成された変数「ExcelInstance」を指定します。

「書き込む値」についてですが、作成するSQLクエリやDBの構成によって変更してください。 今回はエクセルの参照機能を使用し、INSERT文を作成しています。
また文字列の中に「%LoopIndex%」を使用することでループのたびに1,2,3,4,5と値を設定することができます。

「書き込みモード」は「指定したセル上」

「列」「行」は書き込みたいセルを設定する箇所になります。
今回は F 列の1行目、F 列の2行目...と書き込みたいので
「列」= 「F」
「行」= 「%LoopIndex%」
と指定しています。

書き込みイメージ

Loop1回目

="INSERT INTO user (id, login_id, user_name, user_type)VALUES('"&A1&"','"B1&"','"&C1&"','"D1"');"
Loop2回目
="INSERT INTO user (id, login_id, user_name, user_type)VALUES('"&A2&"','"B2&"','"&C2&"','"D2"');"
...

Excel書き込み後の文字列イメージ

Loop1回目

INSERT INTO user (id, login_id, user_name, user_type)VALUES('1','satou','佐藤','2');
Loop2回目
INSERT INTO user (id, login_id, user_name, user_type)VALUES('2','suzuki','鈴木','2');
...


【7】Excel ワークシートから読み取り

f:id:ri_86:20220317170641p:plainExcelカテゴリの中にあるExcel ワークシートから読み取り」アクションを追加します。

f:id:ri_86:20220317175914p:plain Excelインスタンスは【3】で作成された変数「ExcelInstance」を指定します。

「取得」は「単一セルの値」

「先頭列」「先頭行」は読み取りたいセルを設定する箇所になります。
【6】と同じように F 列の1行目、F 列の2行目...と書き込みたいので
「先頭列」= 「F」
「先頭行」= 「%LoopIndex%」
と指定しています。

読み取りイメージ

Loop1回目

INSERT INTO user (id, login_id, user_name, user_type)VALUES('1','satou','佐藤','2');
Loop2回目
INSERT INTO user (id, login_id, user_name, user_type)VALUES('2','suzuki','鈴木','2');
...

【8】テキストをファイルに書き込みます

f:id:ri_86:20220317170647p:plain「ファイル」カテゴリの中にある「テキストをファイルに書き込みます」アクションを追加します。
f:id:ri_86:20220317160546p:plain「ファイルパス」は作成したい場所に追加してください。
※ファイルパス先にファイルが無い場合は新規でファイルが作成されます。

「書き込むテキスト」は【7】で作成された変数「%ExcelData%」を指定します。

「ファイルが存在する場合」は「内容を追加する」にします。


【9】Excel を閉じる

f:id:ri_86:20220317170636p:plainExcelカテゴリの中にあるExcel を閉じる」アクションを追加します。

f:id:ri_86:20220317160011p:plain ▲ドキュメントパスは【3】で作成された変数「ExcelInstance」を指定します。


【10】作成されたSQLファイルを確認

f:id:ri_86:20220317155803p:plain ▲完成したSQLファイルです。
問題なくクエリが作成されていますね。


【11】最後に

最後まで読んでいただきありがとうございました。
重要なことに時間をかけて、その他のことはできるだけ自動化していきましょう。

その作業、自動化しませんか?