【ゲーム日記】ELDEN RING #3

前回「忌み王モーゴット」にボコボコにされたので、レベル上げ兼探索しつつ、リベンジをした。

今回の記事では、①ラダーン祭り→②永遠の都ノクローン→③産まれ直し→④「忌み王モーゴット」へのリベンジ、といった流れ。

ステータス・装備

「忌み王モーゴット」にボコボコにされ、色々と探索したためLV.75→LV.85になった。

今回「名刀月隠」を使うため、生まれ変わりをして知力特化へ変更。

攻略の流れ

前述の通り「忌み王モーゴット」にボコボコにされてから、①ラダーン祭り→②永遠の都ノクローン→③産まれ直し→④「忌み王モーゴット」へのリベンジ、の順で進めた。

地図上の動き

ラニのイベントを進めるため、ケークリッドの「赤獅子城」から始まり、目的地「永遠の都ノクローン」に到達。その後、産まれ直しをしてから王都ローデイルへ。

f:id:guarana001:20220328012240j:plain
f:id:guarana001:20220328012301j:plain
f:id:guarana001:20220328012311j:plain
地図上の動き

①ラダーン祭り

ラニの指示で「ノクローン」を目指し、前々回「シーフラ河」へ行ったが、結局「ノクローン」には辿り着けなかった…

セルブス・セレンに話を聞くと「星砕きのラダーン」を倒すことで、「ノクローン」に行けるらしい。そこで、「星砕きのラダーン」のいる「赤獅子城」へ突撃。

「赤獅子城」では、「ラダーン祭り」としてNPCが集まっており、NPCと協力して「星砕きのラダーン」を討伐した!

ボス「星砕きのラダーン」
  • 戦い方:馬に乗りながら、NPC(やられても時間経過で復活)を召喚しヘイトがこちらに向いていない時に攻撃するHit &Away戦法。
  • 感想:10回ぐらい死んだ。最初の遠距離攻撃から始まり、近距離では双剣にボコボコされ、後半の初見殺しの隕石攻撃にも見事にやられた。ただ、NPCをひたすら召喚して、ヘイト誘導でゴリ押せばどうにかなった。(正直、正当法がよく分からない…)
f:id:guarana001:20220328014321j:plain
f:id:guarana001:20220328014314j:plain
ラダーン祭り

②永遠の都ノクローン

「星砕きのラダーン」を倒すと、隕石が落ち「ノクローン」へ続く巨大な穴が出現。隕石が落ちるムービーもカッコいい。

「ノクローン」は、前回の「シーフラ河」同様、景色が良く、屋根を伝って進んでいく感じも良かった。進んでいくと、かの有名な「写し見の雫」、そしてラニからのお使い「ノクローンで秘宝:指殺しの刃」をゲットできる。

「ノクローンで秘宝:指殺しの刃」をラニに届けると、ラニがどこかへ行ってしまう。

f:id:guarana001:20220328013813j:plain
f:id:guarana001:20220328013805j:plain
f:id:guarana001:20220328013759j:plain
f:id:guarana001:20220328013903j:plain
永遠の都ノクローン
ボス「写し身」
  • 戦い方:普通の対人戦
  • 感想:報酬として「写し見の雫」貰えると思っていたので、倒してから「あれ?」となっていた。
ボス「祖霊の王」
  • 戦い方:「写し身の雫」を召喚して、「血の斬撃」でゴリ押し。
  • 感想:途中回復されるが、「血の斬撃」でゴリ押しできた。「写し身の雫」強い!
f:id:guarana001:20220328013839j:plain
f:id:guarana001:20220328013829j:plain
ボス「写し身」「祖霊の王」

③産まれ直し

レベルも上がり、厨二武器「名刀月隠」を装備できるようになったが、かっこいいけど、火力が出ない。調べてみると、知力が低いと火力が伸びないらしい。

そこで、魔術学院レアルカリアの「満月の女王レナラ」に「産まれ直し」してもらうことにした。「産まれ直し」した結果、知力型へ変更し、「名刀月隠」の火力上昇+魔術が使えるようになった。

f:id:guarana001:20220328013154j:plain
f:id:guarana001:20220328013157j:plain
産まれ直し

④「忌み王モーゴット」へリベンジ

「助っ人NPC:メリナ」と「写し身の雫」にヘイトが向いている間、「魔術:輝石の大つぶて」で遠距離から攻撃する簡単なお仕事。びっくりするほど難易度が下がり、達成感がない…
 

ボス「忌み王モーゴット」
  • 戦い方:「助っ人NPC:メリナ」と「写し身の雫」にヘイトが向いている間、「魔術:輝石の大つぶて」で遠距離から攻撃。
  • 感想:近距離で戦っていた時は、30回ぐらいやられたのに、遠距離から魔術戦法に切り替えた途端にすぐ倒せた。プレイヤースキルに応じて、戦術を変えて難易度を変更出来るので、かなりいいゲームデザインだと思う。達成感ないけど…
f:id:guarana001:20220328013437j:plain
f:id:guarana001:20220328013430j:plain
f:id:guarana001:20220328013424j:plain
ボス「忌み王モーゴット」

次回のゲーム日記

以下のような項目に挑戦したい!

  • 王都ローデイルから先のストーリー
  • ラニイベント進める
  • ひたすら探索(これが一番楽しい!)

【ゲーム日記】ELDEN RING #2

前回、「最初の王ゴッドフレイ」にボコボコにされたので、そのリベンジをした。しかし、次の「忌み王モーゴット」にもボコボコにされて、レベル上げ兼探索した。

今回の記事では、①「最初の王ゴッドフレイ」へのリベンジ→②「忌み王モーゴット」にボコボコ→③古遺跡断崖→④日陰城、といった流れで進める。

ステータス・装備

「忌み王モーゴット」にボコボコにされ、色々と探索したためLV.70→LV.75になった。現在、「名刀月隠」を使うため、知力にステ振り中。

武器は、変わらず打刀+10で「戦灰:血の斬撃」。

攻略の流れ

前述の通り「最初の王ゴッドフレイ」にボコボコにされてから、①「最初の王ゴッドフレイ」へのリベンジ→②「忌み王モーゴット」にボコボコ→③古遺跡断崖→④日陰城、の順で進めた。

地図上の動き

王都ローデイルから始まり、アルター高原の未開の領域を開拓をした。結論、アルター高原から移動してない…

f:id:guarana001:20220322003652j:plain
f:id:guarana001:20220322003621j:plain
地図上の動き

①「最初の王ゴッドフレイ」へのリベンジ

「最初の王ゴッドフレイ」本当に強すぎる…リアルな話30回ぐらいやられた。

ボス「最初の王ゴッドフレイ」
  • 戦い方:自分ができる選択肢「血の斬撃」「回復」「回避」を意識して、極力無駄な隙を生まないようにした。以下のようなイメージ。あと「霊クラゲ」も召喚したが、あまり意味はなかったかも…
    • 「血の斬撃」:敵の大振りの攻撃後のみ
    • 「回復」:敵が後ろに下がってからの地ならし攻撃の時のみ
    • 「回避」:それ以外(YouTubeが参考になる)
  • 感想:アクションゲームなら当然だが、自分ができる選択肢を誤ると、そこを皮切りにからやられてしますので、隙を生まないように意識した。
f:id:guarana001:20220322010920j:plain
f:id:guarana001:20220322010924j:plain
ボス「最初の王ゴッドフレイ」

②「忌み王モーゴット」にボコボコ

「最初の王ゴッドフレイ」から少し進んだところで出会える「忌み王モーゴット」。最初のボス「忌み鬼マルギット」の親戚?みたいなボス。ムービーはかっこいいが、めちゃくちゃ強い。

ボス「忌み王モーゴット」
  • 戦い方:?
  • 感想:「メリナ」と遺灰にヘイトが向いてる間しか攻撃できない…槍投げるデレイ、本当にやめて…半分削った後の後半戦よく分からない…
f:id:guarana001:20220322011254j:plain
f:id:guarana001:20220322011246j:plain
ボス「忌み王モーゴット」

③古遺跡断崖

「忌み王モーゴット」にボコボコにされ、レベル上げ兼探索で、アルター高原の未開の領域を開拓した。アルター高原に進むもう一つのルートである古遺跡断崖に挑戦した。レベルが比較的高かったので、そこまで苦労せず突破できた。

ボス「溶岩土竜マカール」
  • 戦い方:「霊クラゲ」を召喚して、後ろから「血の斬撃」でひたすら攻撃。(ワンパターンだな…)
  • 感想:一回死んだが、レベルの暴力で無事倒せた。
f:id:guarana001:20220322011552j:plain
f:id:guarana001:20220322011629j:plain
ボス「溶岩土竜マカール」

④日陰城

アルター高原で行ったことないエリアに進んだところ、毒沼にそびえる「日陰城」に辿り着いた。毒沼に建っているだけあって、城内の敵は毒系の敵ばかり、状態異常対策を全くしてないので、通常のモブにもやられた…特に、次の写真の強モブ、急に始まるラッシュパンチがキツい…3回ぐらい殺された…

f:id:guarana001:20220322011913j:plain

日陰城の強モブ
ボス「鉄茨のエレメール」

数多のモブを乗り越え、聖杯瓶が無くなったところで、待ってましたボス戦。

  • 戦い方:「民兵のスケルトン」を召喚、スケルトンにヘイト向いてる間に「血の斬撃」で攻撃してヘイト誘導で、スケルトンが倒されないように努めた。この敵も隙を見せると、殺されるので、Hit&Awayを徹底した。
  • 感想:5回ぐらいやられた…突進攻撃と血で操作する剣がメチャクチャかっこいいが、何回も殺された。有効な回避方法が分からなかったが、レベルの暴力で突破できた!
f:id:guarana001:20220322011953j:plain
f:id:guarana001:20220322011946j:plain
ボス「鉄茨のエレメール」

次回のゲーム日記

以下のような項目に挑戦したい!

  • ボス「忌み王モーゴット」討伐(勝てるかな???)
  • ラニイベント進める
  • ひたすら探索(これが一番楽しい!)

【ゲーム日記】ELDEN RING #1

2022年2月25日にフロム・ソフトウェアから発売された「ELDEN RING」のゲーム日記#1。#1と書いてるが、もう既に40時間ぐらい遊んでおり、今更感しかないが個人的なゲーム日記を記載。

アルター高原・王都城壁前の「竜のツーリガード」にボコボコにされて、レベル上げ兼探索で、①カーリアの城→②シーフラ河→③大壺の騎士→④竜のツリーガードのリベンジ→⑤王都ローデイル、といった流れで進めた。

www.eldenring.jp

ステータス・装備

「竜のツリーガード」のボコボコにされ、色々と探索したためLV.60→LV.70になった。「竜のツールガード」の適正レベルLV.50らしい…

今まで、生命力・筋力・技量しか上げてなかったが、手に入れた武器が全然使えないため、知力・神秘にステ振り中。

武器は、打刀+10で「戦灰:血の斬撃」。

侍の初期装備だが、出血+「戦灰:血の斬撃」が優秀すぎて、なんとか乗り越えられている。

f:id:guarana001:20220314010152j:plain

ステータス

攻略の流れ

前述の通り「竜のツーリガード」にボコボコにされて、①カーリアの城→②シーフラ河→③大壺の騎士→④竜のツリーガードのリベンジ→⑤王都ローデイルの順で進めた。

①カーリアの城

手のモンスターが怖すぎる…しかも、そこそこ強い。ただ、Lv.60あったので、道中のモンスターには、レベルの暴力で基本的にはそこまで苦労しなかった。

ボス「親衛騎士ローレッタ」
  • 戦い方:「遺灰:民兵のスケルトン」召喚、スケルトンにヘイト向いてる間に後ろから「血の斬撃」
  • 感想:1回死んだが、レベルの暴力で比較的余裕で倒せた。

f:id:guarana001:20220314010344j:plain

ボス「親衛騎士ローレッタ」討伐

②シーフラ河

「カーリアの城」の裏手にある「ラニの魔術師塔」の「ラニ」に指示されるまま、「半狼ブレイヴ」に会うため「シーフラ河」へ行った。地下は広大で、天井が星のようにキレイ!毎回思うことだが、地下まで作り込まれており、エルデンリングの作り込みに感動!

ただ、地下が広大過ぎて、ボス「祖霊」と戦うための祭壇探しが大変だった…

ボス「竜人兵」
  • 戦い方:馬に乗っている状態で、後ろから攻撃
  • 感想:3回ぐらい死んだが、いつも巨人と同じやり方で倒せた
ボス「祖霊」
  • 戦い方:「戦灰:血の斬撃」でゴリ押し
  • 感想:「遺灰:民兵のスケルトン」召喚しましたが、炎?攻撃で早々に退場。だが、レベル差のおかげか、「戦灰:血の斬撃」でゴリ押せた。

f:id:guarana001:20220314010434j:plain

ボス「祖霊」討伐

③大壺の騎士

シーフラ河にある昇降機を使うと、めちゃくちゃでかい大壺が登場。話しかけると、「大壺の騎士」の召喚サインが表示され、そのまま戦闘。

大壺の騎士

あまりにも勝てなかったので、ググったところ、非常に有効な戦法が紹介されていた。

  • 戦い方:必殺「しゃがみ」戦法
  • 感想:3人連続対決は、かなり厳しい。毎回3人目でやられ、心が折れた。そこで、ググったところ、以下のYouTubeを発見!紹介されていた「しゃがみ」戦法を使うと、めちゃくちゃ余裕で倒せた笑(動画、感謝です!)

www.youtube.com

f:id:guarana001:20220314010609j:plain

「大壺の騎士」討伐

④竜のツリーガードのリベンジ

前回やられ苦手意識があったが、どうにか倒せた。「竜のツリーガード」のHPが半分近くなると、雷を纏い、雷攻撃が追加されて回避が難しくなった。

ボス「竜のツリーガード」
  • 戦い方:馬に乗りながら、色気を出さずHit&Awayを徹底
  • 感想:やっぱり強い。「今、攻撃行けるかな?」なんて色気を出すとボコボコにされるので、そんな色気は出さずHit&Awayを徹底したところ、どうにかなった!基本は大事だな…

f:id:guarana001:20220314010651j:plain

ボス「竜のツリーガード」討伐

⑤王都ローデイル

「竜のツリーガード」を乗り越え、「王都ローデイル」に到達。

第1印象は、めちゃくちゃ街がキレイ!!!ただ、そこら辺のモブが強く、結構ボコボコにされた…

そして、ボス「最初の王ゴッドフレイ」と遭遇したが、ボコボコにされた。次回は、ボス「最初の王ゴッドフレイ」討伐ですな!

ボス「最初の王ゴッドフレイ」
  • 戦い方:?
  • 感想:ボスの攻撃が全然避けられない。微妙なデレイ、本当にやめて欲しい。あと、回避してると、攻撃するタイミングがない…

f:id:guarana001:20220314010721j:plain

f:id:guarana001:20220314010725j:plain

王都ローデイル

次回のゲーム日記

以下のような項目に挑戦したい!

  • ボス「最初の王ゴッドフレイ」討伐(勝てるかな???)
  • ラニイベント進める
  • ひたすら探索(これが一番楽しい!)

【Python】iPhoneの画像(JPGファイル)を撮影日時でリネーム

iPhoneの画像ファイルは、4桁の連番で命名されているため、 ファイル名だけで管理することは難しく、稀に重複が発生する。

そこで、画像ファイルのメタデータである撮影日時をPythonで取得し、リネームすることで、画像ファイルの管理をスムーズにすることを目指す。

背景

iPhoneの画像ファイルの命名規則

iPhoneの画像ファイルは、以下のように4桁の連番で命名されている。 また、命名されたファイル名が「IMG_9999.JPG」まで行くと「IMG_0001.JPG」に戻るため、稀にファイル名が重複する。

f:id:guarana001:20210809000121p:plain:w400
iPhoneの画像ファイルの命名規則

参考

iPhoneで写真ファイルの番号が「9999」を超えたら「0001」になった!(iOS7)

JPGのメタデータ(Exif)

JPGやPNGなどの特定の画像ファイルは、Exif(Exchangeable image file formatの略)と呼ばれる画像ファイルフォーマットでメタデータを管理している。 メタデータとしては、撮影日時、位置情報(ジオタグ)、撮影方向、撮影機器のメーカー名などがある。

参考

Exchangeable image file format - Wikipedia

本記事の目的

iPhoneの画像ファイルを、以下のように撮影日時でリネームし、画像ファイルの管理をスムーズにすることを目指す。

f:id:guarana001:20210809000124p:plain:w600
本記事の目的

iPhoneの画像を撮影日時でリネーム

事前準備

Pythonにおける画像ファイルの処理では、定番のPillowを用いる。

pip install Pillow

JPGのメタデータ(Exif)の取得方法

以下のソースコードのように、撮影日時や撮影機器などのメタデータを取得することができる。

メタデータを取得する際には、Exifのキーが必要である。 ちなみに、Exifのキーの一覧は、以下のリンク先に記載されている。

Exiv2 - Image metadata library and tools

from PIL import Image
import PIL.ExifTags as ExifTags
 
file = "/path/to/images/IMG_0001.JPG"
im = Image.open(file)
 
# Exifのメタデータを辞書型で取得する
exif_dict = {
    ExifTags.TAGS[k]: v
    for k, v in im._getexif().items()
        if k in ExifTags.TAGS
    }
 
# 撮影日時
print(exif_dict["DateTimeOriginal"])
 
# 撮影機器メーカー名とモデル名
print(exif_dict["Make"])
print(exif_dict["Model"])
参考

iPhoneの画像を撮影日時でリネーム

前節のメタデータ(Exif)の取得方法を用いて、 以下のように所定のフォルダに保存された画像ファイルを一括でリネームできる。

from PIL import Image
import PIL.ExifTags as ExifTags
from pathlib import Path
import datetime
 
# 画像ファイルから辞書型でメタデータを取得する
def get_exif_of_image(file):
    im = Image.open(file)
    exif_dict = {
        ExifTags.TAGS[k]: v
        for k, v in im._getexif().items()
            if k in ExifTags.TAGS
        }
    return exif_dict
 
for filename in Path("/path/to/images").glob("IMG_*.JPG"):
    exif_dict = get_exif_of_image(filename)
    if "DateTimeOriginal" in exif_dict:
        #撮影日時に基づく新規ファイル名を準備
        file_dateTime = datetime.datetime.strptime(exif_dict["DateTimeOriginal"], "%Y:%m:%d %H:%M:%S")
        file_dateTime = file_dateTime.strftime("IMG_%Y-%m-%d_%H-%M-%S.JPG")
        new_name = Path(filename).with_name(file_dateTime)
        #リネーム
        filename.rename(new_name)
参考

【論文メモ】Louvain法

Louvain法について、提案論文(下記リンク)を元に調べてみました。

Louvain法は、ネットワーク内のコミュニティ(密に結合しているノードの集合)を抽出する手法であり、従来の手法に比べ高速で抽出することができます。 また、アルゴリズムもとてもシンプルで、プログラムへの実装も容易にできます。

※私はグラフ理論が専門ではないので、間違いがある可能性も多分にあります。 参考にして頂く際には、ご注意下さい。

arxiv.org

概要

近年、SNSやWebの流行を背景にグラフデータ(ネットワーク)への関心が高まっています。 グラフデータの活用方法として、グラフデータからコミュニティ(密に結合しているノードの集合)を抽出するという方法があります。 しかし、SNSやWebの発達に伴いグラフデータの規模が年々拡大しており、従来のコミュニティ抽出手法では、常識的な時間で計算が完了できないことが増えています。

本稿では、以下の2点により計算量を抑え、従来の手法に比べ高速にコミュニティ抽出できる手法を提案します。

  1. 最適化の計算を局所的に行う
  2. コミュニティごとにノードを集約

様々な規模のグラフデータにおいて、既存手法との比較実験を行いました。 その結果、いずれのグラフデータでも、品質が高いコミュニティを高速で抽出することできました。

背景知識

グラフとネットワーク

グラフとは、下画像のようにノード(頂点)とノード同士を繋ぐエッジ(辺)で構成されたものを指します。特に、エッジの結びつき(重み、コスト)が一律ではなく、エッジによって異なるグラフをネットワーク(重み付きグラフ)と呼びます。SNSの場合、ノードがユーザー、エッジがユーザー同士の関係性を表します。

f:id:guarana001:20200810123942p:plain:w450
グラフ説明

参考URL: グラフ理論 - Wikipedia

コミュニティ

下図のように、密に結合しているノードの集合をコミュニティと呼びます。 ただし、コミュニティ同士は疎な結合をしていることが多いです。 コミュニティは、ノード同士の関係性を基にノードを分類(クラスタリングした結果を表します。 SNSの場合、コミュニティに着目することで、密なユーザー同士を抽出することができるようになります。

f:id:guarana001:20200810124135p:plain:w450
コミュニティの説明

モジュラリティ

モジュラリティは、wikiにおいて以下のように説明されています。

モジュラリティ(英: Modularity)は、コンピューターネットワークや、ソーシャルネットワークなどのネットワークやグラフの解析に用いられる効果関数。ネットワークから、モジュールやコミュニティへの分割の「質」を定量化するものである。

高いモジュラリティを持つような高レベルな分割は、モジュール内部でのノード間の密なネットワークを持つ反面、異なるモジュール間で疎なネットワークを持つ。

モジュラリティ - Wikipedia

つまり、モジュラリティは、抽出されたコミュニティの質を評価する指標です。 モジュラリティが高いグラフは、コミュニティ内が密で、コミュニティ同士が疎な関係になります。逆に、モジュラリティが低いグラフは、コミュニティ内が疎で、コミュニティ同士が密な関係になります。

モジュラリティ Qは、以下の式で表されます。

f:id:guarana001:20200810144616p:plain:w300
ただし、数式内で使用される記号は下表の通りです。

記号 意味 数式
 A_{ij} ノード iとノード jのエッジの重み
 c_{i} ノード iが所属するコミュニティ
 k_{i} ノード iに結合しているエッジの重みの合計  k_i = \sum_j A_{ij}
 m 全てのエッジの重みの総合計  m = \sum_i \sum_j A_{ij}
 \delta クロネッカーのデルタ (省略)

課題

上記のモジュラリティ Qは、最適化する目的関数として用いられます。 しかし、大規模なネットワークを扱う場合、モジュラリティの計算が困難となるので、近似的な計算が必要となります。

既存の近似手法1には、以下の2つの問題点があります

  1. シミュレーテッドアニーリングと比較して、モジュラリティの値が大幅に低い傾向があります
  2. 生成されたコミュニティは、ノードの大部分を含むスーパーコミュニティになる傾向があります。 このスーパーコミュニティの影響により、計算速度が遅くなり、100万以上のノードを持つネットワークには適用できません

Louvain法(提案手法)

アルゴリズム

Louvain法がコミュニティを抽出する流れは、下図で表されます。
Louvain法のアルゴリズムは、大きく2つのフェイズに分かれます。 一つ目のフェイズではモジュラリティの最適化(手順2)を行い、二つ目のフェイズではコミュニティの集約を行います。 また、2つのフェイズを合わせて「パス」と呼びます。

f:id:guarana001:20200817121741p:plain:w450
アルゴリズムの説明(元論文より引用)

手順1) 初期値の設定
各ノードごとに個別のコミュニティを設定します。

手順2) モジュラリティの最適化
ノードをランダムに選択し、《終了条件》を満たすまで以下の手順2-1), 手順2-2)の操作を行います。

《終了条件》全てのノードにおいて、コミュニティを変化によるモジュラリティの変化量が \Delta Q \leq 0を満たす場合。
つまり、モジュラリティが極大値をとり、モジュラリティがこれ以上改善できない時です。
※同じノードであったとしても複数回操作する可能性があります

手順2-1) 選択されたノード iに隣接した全てのコミュニティにおいて、モジュラリティの変化量 \Delta Qを計算する。 このモジュラリティの変化量 \Delta Qは、ノード i元々所属するコミュニティから隣接したコミュニティへ移動することにより生ずるモジュラリティの変化量のことを指します。

ここでは、移動前のコミュニティを C_1、移動後のコミュニティを C_2とした時、移動に伴うモジュラリティの変化量を次の式で表すことができます。

f:id:guarana001:20200816164215p:plain

この時、右辺の第一項を \Delta Q_r、第二項を \Delta Q_aと置いた場合、  \Delta Q_aを次式のようになります。 また、 \Delta Q_rも符号を反転させた同様な式で表すことができます。

f:id:guarana001:20200816170842p:plain:w550

ただし、数式内で使用される記号は下表の通りです。
(コミュニティ C_2にはノード iは含まれません)

記号 意味 数式
 k_{i, in} ノード iと結合しているノードのうち
コミュニティ C_2に所属するノードとのエッジの重みの合計
f:id:guarana001:20200816182446p:plain:w130
 \sum_{in} コミュニティ C_2に所属するノード同士のエッジの重みの合計 f:id:guarana001:20200816190611p:plain:w130
 \sum_{tot} コミュニティ C_2に所属するノードと
結合のあるノードとのエッジの重みの合計
f:id:guarana001:20200816191050p:plain:w130

手順2-2) 隣接するコミュニティの \Delta Qの最大値を \Delta Q_{max}をおきます。 この時、下表のように \Delta Qの最大値によってノード iが所属するコミュニティを変化させます。

ただし、 C_{max} \Delta Qの最大値をとるコミュニティを指します。

条件 意味 ノード iが所属する
コミュニティ
 \Delta Q_{max} > 0 ノード i C_1から C_{max}に移動させた方が
モジュラリティが増加する
 C_{max}
 \Delta Q_{max} \leq 0 ノード i C_1から移動させない方が
モジュラリティが高い
 C_1

手順3) コミュニティごとの集約
手順3) では、手順2) によって抽出したコミュニティごとに集約し新たなグラフを構築します。 このグラフの構築が完了次第、手順2) に戻ります。

具体的なグラフの構築方法は、それぞれのコミュニティのノードを単一のノードに集約します。 その際、集約後のノード同士のエッジの重み集約後のノード内のエッジの重みは次のように定義します。

f:id:guarana001:20200817165358p:plain:w500
手順3) の説明

この時、上図のようにコミュニティ C_A C_Bを考えます。 また、集約後のノードもそれぞれノードa、ノードbとおきます。

集約後のノード同士のエッジの重み
コミュニティ C_A C_Bのエッジは、集約後のノードa,bのエッジとなり、エッジの重みは次の式で表せます。

f:id:guarana001:20200817164812p:plain:w200

集約後のノード内のエッジの重み
コミュニティ C_A内のエッジは、集約後のノードaのセルフループ(自己閉路)となり、エッジの重みは次の式で表せます。

f:id:guarana001:20200817165024p:plain:w200

Louvain法の特徴

Louvain法は、以下のような特徴があります。

  • 各手順が直感的で、実装が容易です。
  • アルゴリズム非常に高速です。
  • アルゴリズムを繰り返すことでコミュニティ数が大幅に減少するため、実行時間のほとんどが最初のパス(反復作業)に集中します。
  • アルゴリズムのマルチレベルの性質のおかげで、モジュラリティの resolution limit problem(分解制限問題?,*1)を回避できるようになります。
  • アルゴリズムが停止して得られた最終的なコミュニティだけではなく、中間的なコミュニティにも意味がある可能性があります。

*1 resolution limit problem:ネットワークが巨大になることでノード同士が相対的に疎になること(ヌルモデルの仮説を保てない)により、小規模なコミュニティを抽出できない問題。詳細は以下のリンクを参照して下さい。

参考URL: Modularity (networks) - Wikipedia

実験

様々なグラフにおいて、Louvain法を既存手法と比較する実験を行います。 今回利用するグラフは、下表の通りです。 (グラフの詳細は、元論文等を確認して下さい。)

名称 説明 ノード リンク
Karate 小規模なソーシャルネットワーク 34 77
Arxiv 科学論文とそれを引用する論文のネットワーク 9k 24k
Internet インターネットのサブネットワーク 70k 351k
Web nd.edu nd.eduドメインのサブネットワーク 325k 1M
Phone 携帯電話の利用客のネットワーク(ウェイトは電話回数) 2.6M 6.3M
Web uk-2005 .ukドメインのサブネットワーク 39M 783M
Web WebBase 2001 Stanford WebBase crawlerによって得られたネットワーク 118M 1B

今回比較する既存手法は、以下の3つ手法です。

  • Clauset, Newman, and Moore (脚注1参照)
  • Pons and Latapy 2
  • Wakita and Tsurumi 3

実験結果は、下表の通りです。
表の2つの数値は、(モジュラリティ)/(実行時間)を表します。 ただし、実行時間が24時間を超えた場合、"-"と表記します。

いずれのグラフでも、モジュラリティ・実行時間の両方の観点において、Louvain法が最良な結果を得られました。 特に、Web uk-2005、Web WebBase 2001などの巨大なネットワークに対しても適用できるのは、Louvain法の優れている点です。

名称 CNM PL WT Louvain法
Karate .38/0s .42/0s .42/0s .42/0s
Arxiv .772/3.6s .757/3.3s .761/0.7s .813/0s
Internet .692/799s .729/575s .667/62s .781/1s
Web nd.edu .927/5034s .895/6666s .898/248s .935/3s
Phone -/- -/- .56/464s .769/134s
Web uk-2005 -/- -/- -/- .979/738s
Web WebBase 2001 -/- -/- -/- .984/152nm

まとめ

本記事では、Louvain法について、提案論文を元に調べました。
Louvain法は、既存手法に比べ、品質が高いコミュニティを高速で抽出することができ、巨大なネットワークに対しても適用可能です。 SNSやWebの発達に伴いグラフデータの規模が年々拡大することが予測できるため、Louvain法への関心がますます高まると思います。

【Python】DICOM画像(DCM)をJPG画像へ変換

DICOM画像は、医療用の画像フォーマットです。 DICOM画像の特徴は、通常の画像フォーマットと比較して高階調であり、一般的に使用される画像ビューアでは表示できないケースも多いです。 本記事では、DICOM画像を使用頻度が高いJPG画像へ変換することを試みます。 また、変換する際に必要となるウィンドウ処理に関しても記載します。

※私は医療 or 画像処理の専門ではないので、間違いがある可能性も多分にあります。 参考にして頂く際には、ご注意下さい。

DICOM画像

DICOM画像とは

wikiでは、DICOM画像を次のように説明しています。

DICOM(ダイコム)とは、CTやMRI、CRなどで撮影した医用画像のフォーマットと、それらを扱う医用画像機器間の通信プロトコルを定義した標準規格である。 (引用元:DICOM - Wikipedia)

また、DICOM画像は画像データだけではなく、患者の情報(名前・性別・身長など)や画像の撮影状況などのメタ情報を保持しています。 メタ情報の詳細は、下リンクが参考になると思います。

https://www.liberworks.co.jp/407/dicom.html

DICOM画像は、他の画像フォーマットと比較して『①高階調』・『②通常のビューアーでは表示できない』の2つがあります。

①高階調

高階調な画像とは、画素値(各ピクセルの色の濃淡や明るさを表す値)が幅広いレンジの画像のことを指します。 DICOM画像では、一般的な階調表現である8bitを超える10~16bitであることが多いです。

f:id:guarana001:20200725191714p:plain:w600
左図:高階調 / 右図:一般的な階調表現

②通常のビューアーでは表示できない

通常のビューアーでは、DICOM画像を表示できないケースが多いです。

f:id:guarana001:20200725192937p:plain:w600
通常のビューアーでは表示できないケース

サンプルとして使用するDICOM画像

下記リンクより取得できる『CT_JPG_IR6a.dcm』を使用します。

f:id:guarana001:20200725214935p:plain:w600
左図:カラー(未処理) / 右図:グレースケール

www.jira-net.or.jp

DICOM画像を表示するためのソースコード

次のソースコード は、『DICOM画像を扱う前の事前準備』を実施後に実行にして下さい。

from matplotlib import pyplot as plt
import pydicom
  
dcm_sample = pydicom.dcmread("CT_JPG_IR6a.dcm")
plt.imshow(dcm_sample.pixel_array) #カラー(未処理)
plt.imshow(dcm_sample.pixel_array, cmap = 'gray') #グレースケール
plt.show()

PythonでDICOM画像を扱う前の事前準備

PythonでDICOM画像を扱う前にpydicomをインストールする必要があります。 また、JPEGJPEG-LS・JPEG2000を解凍する必要がある場合は、gdcmをインストールして下さい。

pydicomをインストール

pip install pydicom

gdcmをインストール

conda install gdcm -c conda-forge

参考URL

DICOM画像をJPG画像へ変換する方法

DICOM画像のメタ情報にWindowCenter・WindowWidthが存在する場合は、ウィニング処理をした方が適切です。 理由としては、撮影時に重要視した対象()を鮮明に表現できるからです。

※恐らくWindowCenter・WindowWidthの値は、画像撮影時に設定されるものだと思っているので、上のように記述しました。 しかし、WindowCenter・WindowWidthの値を設定する正確なタイミングを把握できていないため、あくまで推察です。

ちなみに、WindowCenter・WindowWidthの有無を確認するソースコードは次の通りです。 WindowCenter・WindowWidthの有無に依り、5行目の結果が変化します。 存在する場合はTrue、存在しない場合はFalseとなります。

from matplotlib import pyplot as plt
import pydicom
  
dcm_sample = pydicom.dcmread("CT_JPG_IR6a.dcm")
print(('WindowCenter' in dcm_sample) and ('WindowWidth' in dcm_sample))

WindowCenter・WindowWidthがない場合

次のソースコード を用いると、DICOM画像をJPG画像(下図)に変換できます。

f:id:guarana001:20200726002239j:plain:w300
CT_NoWindow.jpg

import pydicom
import cv2
  
dcm_sample = pydicom.dcmread("CT_JPG_IR6a.dcm")
dcm_img = dcm_sample.pixel_array 
cv2.imwrite("CT_NoWindow.jpg", dcm_img)

WindowCenter・WindowWidthがある場合

DICOM画像をJPG画像(下図)に変換する際に、ウィニング処理を考慮した場合のソースコード は、次のようになります。 ウィニング処理に関しては、次の節をご確認下さい。

f:id:guarana001:20200726002242j:plain:w300
CT_Window.jpg

import pydicom
import cv2
  
dcm_sample = pydicom.dcmread("CT_JPG_IR6a.dcm")

dcm_wc  = dcm_sample.WindowCenter
dcm_ww  = dcm_sample.WindowWidth
dcm_img = dcm_sample.pixel_array 
  
#ウィンドウ処理
window_max = dcm_wc + dcm_ww/2                     
window_min = dcm_wc - dcm_ww/2
dcm_img = 255*(dcm_img - window_min)/(window_max - window_min)    
dcm_img[dcm_img > 255] = 255
dcm_img[dcm_img < 0] = 0 
cv2.imwrite("CT_Window.jpg", dcm_img)
参考URL

医療情報学研究室: Pyゼミ1.02 DICOMからJPEGなどへの変換

ウィンドウ処理

ウィンドウ処理とは

ウィンドウ処理とは、下図のように幅広いレンジの画素値(各ピクセルの色の濃淡や明るさを表す値)を持つ画像において、特定の範囲の画素値(Window Center ± 0.5*Window Width)一般的な階調表現 (例えば 8 bit)に変換する処理のことを指します。 ウィンドウ処理をすることで、特定の範囲の画素値を鮮明に表現できるようになります。

f:id:guarana001:20200725191710p:plain:w500
ウィンドウ処理

ウィンドウ処理(数式)とは

変換前の画像(高階調)のピクセルの画素値xとし、特定の範囲の画素値に関する情報であるWindow CenterWindow Widthをそれぞれwcwwとおきます。 また、変換後の画像の階調nbit 、変換後のピクセルの画素値yとします。 この時、ウィンドウ処理は、次のような数式で表現できます。

f:id:guarana001:20200726030018p:plain:w500
ウィンドウ処理 (数式)

ウィンドウ処理の効果

上記のDICOM画像が、ウィンドウ処理の有無で、どのように変化するかを比較すると次のようになります。 ウィンドウ処理ある場合の方が、画像が鮮明になり分かりやすくなります

f:id:guarana001:20200726031937p:plain:w500
左図:ウィンドウ処理なし / 右図:ウィンドウ処理あり

参考URL

まとめ

本記事では、医療用の画像フォーマットとして利用されるDICOM画像を、使用頻度が高いJPG画像へ変換する方法を記載しました。 加えて、階調の変換する際に必要となるウィンドウ処理についても記載しました。

【Python】面グラフ・層グラフを描画する方法

Pythonのmatplotlibを使った、面グラフ(Area Graph)層グラフ(Stacked Area Charts)を描画する方法を調べてみました。 面グラフとして有名な積み上げ面グラフ積み上げ100%面グラフ の特徴を比較した後、それぞれを描画するソースコードを記載します。 加えて、モノクロ印刷時にグラフを見やすくするハッチング(グラフの面ごとの模様・柄を設定)の方法も記載します。

使用データ

政府統計の総合窓口e-Statから取得できる統計名「成果物卸売市場調査 長期累年」を使用します。具体的に使用するデータ項目は、次の通りです。

  • 表章項目:卸売数量
  • 青果物品目:だいこん・かぶ・にんじん・ごぼう・たけのこ・キャベツ・ねぎ(7種類)
  • 都市分類:主要都市
  • 時間軸:1997年〜2016年(20年間)

www.e-stat.go.jp

面グラフ・層グラフとは

本記事では、面グラフ(Area Graph)・層グラフ(Stacked Area Charts)のうち積み上げ面グラフ積み上げ100%面グラフを対象とします。

上記のデータを面グラフで表現すると、次のようになります。 ちなみに、描画にはExcelを使用しました。

f:id:guarana001:20200404234922p:plainf:id:guarana001:20200404234918p:plain
左図:積み上げ面グラフ/右図:積み上げ100%面グラフ

それぞれの特徴をまとめると、下表のようになります。いずれのグラフもデメリットは存在しますが、双方を組み合わせることでお互いのデメリットを補うことができます

本題から脱線しますが、たけのこの卸売量は極端に少ないですね笑 高級品として扱われる意味が分かります。

項目 積み上げ面グラフ 積み上げ100%面グラフ
左図 右図
表現する対象 変化量構成 構成比率変化
メリット 全体、構成要素ごとの変化量が分かりすい 構成比率・変化の要因が分かりやすい
デメリット 全体が変化した要因が分かりにくい 変化量が分からない

Pythonによる面グラフの描画方法

ここでは、Pythonによる面グラフの描画方法をまとめています。

内容としては、上で登場した積み上げ面グラフ積み上げ100%面グラフ積み上げ面グラフ(ハッチング)です。 ハッチングとは、面グラフの面に模様・柄を設定するもので、モノクロ印刷時にとても見やすいです。(弊社はカラー印刷に制限があるので。。。)

積み上げ面グラフ

具体的なソースコードと積み上げ面グラフは、次の通りです。 また、グラフの画質や日本語表記などのグラフの整形方法の詳細は、参考URLをご確認下さい。

f:id:guarana001:20200405144252p:plain:w500

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import japanize_matplotlib
 
df = pd.read_excel('/野菜の卸売数量.xlsx')
df = df.set_index('YEAR', drop=True)
yasai_list = list(df.columns)
 
plt.stackplot(df.index, df[yasai_list].values.T, labels=yasai_list)
 
#グラフの整形
plt.gca().get_xaxis().set_major_locator(ticker.MaxNLocator(integer=True))
plt.legend(loc = 'upper right', bbox_to_anchor=(0.3, 0, 1, 1),title="野菜")
plt.title('野菜の卸売数量[t]')
plt.savefig('/野菜の卸売数量.png', dpi=200, bbox_inches='tight')
参考URL

積み上げ100%面グラフ

積み上げ100%面グラフは、積み上げ面グラフと異なり、描画する前に数量を割合に変換(11行目)します。 逆に11行目以外は、積み上げ面グラフと同じソースコードです。

f:id:guarana001:20200405144408p:plain:w500

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import japanize_matplotlib
 
df = pd.read_excel('/野菜の卸売数量.xlsx')
df = df.set_index('YEAR', drop=True)
yasai_list = list(df.columns)
 
df = df.apply(lambda x:x/sum(x),axis=1) #ここを追加
plt.stackplot(df.index, df[yasai_list].values.T, labels=yasai_list)
 
#グラフの整形
plt.gca().get_xaxis().set_major_locator(ticker.MaxNLocator(integer=True))
plt.legend(loc = 'upper right', bbox_to_anchor=(0.3, 0, 1, 1),title="野菜")
plt.title('野菜の卸売数量[t]')
plt.savefig('/野菜の卸売数量.png', dpi=200, bbox_inches='tight')
参考URL

積み上げ面グラフ(ハッチング)

ハッチング(hatch)とは、面グラフの面に模様・柄を設定するもので、モノクロ印刷時にとても有効です。 上記の積み上げ面グラフと比較すると、変更点が分かりやすいと思います。

f:id:guarana001:20200405145456p:plain:w500

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import japanize_matplotlib
 
df = pd.read_excel('/野菜の卸売数量.xlsx')
df = df.set_index('YEAR', drop=True)
 
yasai_list = list(df.columns)
hatch_list = ['xx', '\\', '//', '||', '--', '..', '++']
fig, ax = plt.subplots()
stacks = ax.stackplot(df.index, df[yasai_list].values.T, labels=yasai_list)

for (stack, hatch) in zip(stacks, hatch_list):
    stack.set_hatch(hatch)
 
#グラフの整形
plt.gca().get_xaxis().set_major_locator(ticker.MaxNLocator(integer=True))
plt.legend(loc = 'upper right', bbox_to_anchor = (0.3, 0, 1, 1),title="野菜")
plt.title('野菜の卸売数量[t]')
plt.savefig('/野菜の卸売数量.png', dpi=200, bbox_inches='tight')
参考URL

まとめ

Pythonのmatplotlibを使った、面グラフ(Area Graph)層グラフ(Stacked Area Charts)を描画する方法を調べました。 面グラフとして有名な積み上げ面グラフ積み上げ100%面グラフ の特徴を比較した後、それぞれを描画するソースコードをまとめました。 加えて、モノクロ印刷時にグラフを見やすくするハッチング(グラフの面ごとの模様・柄を設定)の方法もまとめました。