質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.61%

格子状データの軸投影によるデータ処理の方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,232

score 22

初めまして,python初心者です.
以下のようなデータ処理を行いたいのですが,プログラムのアイディアが浮かばず困っています.
どなたかお力をかしてもらえませんでしょうか.
python3.7.1 , Anaconda3ディストリビューションを利用しています.
なお,csvファイルを読み込みリスト型にするなどの,超基本的なことは一応できます.

図(a)に示す例のように格子状正方領域にデータがcsv形式で保存されています.このデータを読み込み,各ピクセルのデータをx軸上に投影するとともに,同軸を幅1ピクセルに区切り,投影されたデータ,並びにデータの平均値を取得したいです.図(a)の例のように軸が全く回転していない場合, x = 3 での投影データが[8,5,1,2,9,8]で平均値が5.5, x = -2 での投影データが[4,8,8,4,2,2]で平均値が4.67のようになることは容易に理解でき,計算できます.しかし,(b)に示すように軸を任意の角度Θ[deg]だけ回転させた場合,x軸に投影されるデータの所得方法がわかりません.以上のような作業を回転角0~180[deg]まで,1[deg]間隔で行い,データ処理をしたいのです.

稚拙な説明で申し訳ありませんが,以上が行いたいデータ処理となります.また,初心者でも理解できるように,コードの説明もしていただけたら大変助かります.

お願いばかりで申し訳ありませんが,ご回答をよろしくお願いします.

〈追記〉
説明不足で申し訳ありません.図(c)を追加しています.図のx'y'座標系の場合,x' = 1での中の緑帯の領域に含まれる格子点のデータを参照するという意味です.各格子点の少しでも緑帯の領域に含まれていたら対象です.同様にx=-3での投影とは,図中の赤帯の領域に含まれる格子点のデータを参照するということです.また,xの座標値は整数値とします.(x = 0.5などでの投影は行いません.)

〈追記2〉
重ね重ね質問ばかりですいません.
回答者様のソースコードを参考にして,プログラムを作成できたのですが,以下の二点をさらに変更する必要が出てきました.

(1) 前回は「格子点の一部でも帯状領域に含まれていたら,そのx'座標上に投影する」としていましたが,図(d)のように「各格子点の中心(図中緑の点)が帯状領域に含まれていたら,そのx'座標上に投影する」というデータ処理を行えるように変更したいです.

(2) データを投影する対象となる点は,格子の一辺長さを直径とする円に含まれるものであること(図中の灰色部分は対象外となります.)

以上,2点を変更したいと考えていますが,どのように円形領域に変更し,中心座標を参照すればよいのかわかりません.
ご回答お待ちしております.

イメージ説明
イメージ説明イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • tiitoi

    2019/03/29 11:04

    一旦クローズした質問で新たな質問が生じた場合は、再度 Open にするのではなく、新規に質問を立て直してください

    キャンセル

  • fana

    2019/03/29 13:28 編集

    「〈追記2〉」に関しては,その文面だけだと
    (1)は中心位置のx'に基づき集計すれば良いだけの話になった(以前と比較して内容が単純化された)と見えるし,
    (2)は中心位置の原点からの距離で集計対象とするか否かを判定すれば良いだけの話に見えるので,
    問題点が一体何なのかが私には読み取れませんでした.
    別途質問する場合には,一体何が困り事なのか?という点を明確にすると良いかと思います.

    キャンセル

  • monkey.0435

    2019/03/29 14:07

    ご指摘ありがとうございます!

    もう少し問題を具体的にした後,再度ご質問させていただきます.
    ありがとうございました.

    キャンセル

回答 2

checkベストアンサー

+1

各データの座標を,回転した後の座標軸(x',y')の世界で読み変えたときのx'値に基づき集計すれば良い,という話かと思います.
(解りやすく言い換えれば,元のデータを -θ だけ回転したデータを(その結果のx座標に基づき)集計すれば良い.)

図内x'軸方向の単位ベクトルを元の(xとyの)世界の座標系で書けば( cosθ, sinθ ) です.
各データの(x,y)座標とこの単位ベクトルとの内積を取ればx'方向成分が得られます.


質問文の追加に対して:
各データに関して【その正方形のごく一部分であっても(質問内の図(c)の)帯に含まれていたらその帯に集計する】という条件であれば,
「格子点(正方形の四隅)のx'を求めて,そのいずれかが帯に含まれているか」で判定できるでしょう.

一般に,1つの正方形は二つの複数の帯にまたがることになるでしょうから,4隅のx'値のうちの最小値と最大値が所属する2つの帯最小値が所属する帯~最大値が所属する帯の範囲に集計すれば良いかと思います.(両者が同一の帯になる場合は1回だけ集計する,という判定は当然必要)

例)
回転角θ=30度のとき,正方形の四隅のうちx'が最小になるのは左下で最大になるのは右上.
図内で最も右上にあるデータ値が8の正方形について考えると,
左下頂点の座標は (x,y)=(2,2)
右上頂点の座標は (x,y)=(3,3)

x' = x*cos(30) + y*sin(30)

からそれぞれのx'値を求めると,
左下のx' ≒ 2.732
右上のx' ≒ 4.098

よって,このデータは,3つの帯{ x'=3, x'=4, x'=5 }に集計される(…でいいのかな?)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/05 02:00

    ご回答ありがとうございます.
    参考にさせていただきます!

    キャンセル

0

fana さんが回答してくださった方法で実装してみました。


import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np

mat = np.array([[3, 4, 6, 9, 0, 8],
                [2, 8, 5, 0, 3, 5],
                [4, 8, 1, 0, 3, 1],
                [0, 4, 3, 7, 1, 2],
                [4, 2, 6, 1, 0, 9],
                [7, 2, 1, 6, 2, 8]], dtype=float)
angle = 10  # x 軸の回転角度 (degree)
radius = np.array(mat.shape) // 2  # 半径

def create_rotation_matrix(angle):
    t = np.deg2rad(angle)
    return np.array([[np.cos(t), -np.sin(t)],
                     [np.sin(t), np.cos(t)]])
R = create_rotation_matrix(-angle)  # x 軸の回転方向とは逆の方向に回転する回転行列
# 各セルの4点の座標を作成する。
cells = []
id_ = 0  # あとでどのセルが含まれているのか確認するための識別子 (デバッグ用)
for (i, j), value in np.ndenumerate(mat):
    # セルの左下の点 (x, y) を定義する。
    x, y = j - radius[1], radius[0] - i - 1
    # セルを構成する4点を作成する。
    rect = np.array([[x, y], [x + 1, y], [x + 1, y + 1], [x, y + 1], [x, y]])
    # セルを構成する4点を回転させる。
    rect = rect.dot(R.T)

    cells.append((id_, rect, value))
    id_ += 1

各区別 [x_i, x_{i + 1}] に含まれるセルを見つけて、その値の平均を計算する。

edges = np.arange(-radius[1], radius[1] + 1)

for left, right in zip(edges, edges[1:]):
    values = []
    for id_, rect, value in cells:
        if np.any((left <= rect[:, 0]) & (rect[:, 0] <= right)):
           values.append((id_, value))  # セルの4隅の1点でも [left, right] に含まれる場合
    values = sorted(values, key=lambda x: x[0])  # 見やすいように id でソートする。
    mean = np.mean([x[1] for x in values])  # 平均
    print('{} <= x <= {}:\n    value: {}\n    mean: {}'.format(
        left, right, values, mean))
-3 <= x <= -2:
    value: [(0, 3.0), (6, 2.0), (12, 4.0), (18, 0.0), (19, 4.0), (24, 4.0), (25, 2.0), (30, 7.0), (31, 2.0)]
    mean: 3.111111111111111
-2 <= x <= -1:
    value: [(0, 3.0), (1, 4.0), (6, 2.0), (7, 8.0), (12, 4.0), (13, 8.0), (18, 0.0), (19, 4.0), (20, 3.0), (25, 2.0), (26, 6.0), (31, 2.0), (32, 1.0)]
    mean: 3.6153846153846154
-1 <= x <= 0:
    value: [(1, 4.0), (2, 6.0), (7, 8.0), (8, 5.0), (13, 8.0), (14, 1.0), (15, 0.0), (19, 4.0), (20, 3.0), (21, 7.0), (26, 6.0), (27, 1.0), (32, 1.0), (33, 6.0)]
    mean: 4.285714285714286
0 <= x <= 1:
    value: [(2, 6.0), (3, 9.0), (8, 5.0), (9, 0.0), (14, 1.0), (15, 0.0), (16, 3.0), (20, 3.0), (21, 7.0), (22, 1.0), (27, 1.0), (28, 0.0), (33, 6.0), (34, 2.0)]
    mean: 3.142857142857143
1 <= x <= 2:
    value: [(3, 9.0), (4, 0.0), (9, 0.0), (10, 3.0), (15, 0.0), (16, 3.0), (17, 1.0), (22, 1.0), (23, 2.0), (28, 0.0), (29, 9.0), (34, 2.0), (35, 8.0)]
    mean: 2.923076923076923
2 <= x <= 3:
    value: [(4, 0.0), (5, 8.0), (10, 3.0), (11, 5.0), (16, 3.0), (17, 1.0), (23, 2.0), (29, 9.0), (35, 8.0)]
    mean: 4.333333333333333
# デバッグ用
###################################################################
fig, ax = plt.subplots(figsize=(6, 6))

# 各セルを描画する。
for id_, rect, value in cells:
    ax.plot(rect[:, 0], rect[:, 1], c='gray', alpha=0.5)   # セルの枠線
    cx, cy = (rect[0] + rect[2]) / 2  # セルの中心
    s = '{}:{}'.format(id_, value)
    ax.text(cx, cy, s, ha='center', va='center', rotation=-angle)  # 値
ax.autoscale(False)
# x 軸、y 軸を引く。
ax.axhline(), ax.axvline()
# 垂直線を引く
ax.vlines(edges, *ax.get_xlim(), alpha=0.5)

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/05 14:55

    fana さんが回答してくださった方法でできるはずですよ。
    サンプルコードを載せました。

    キャンセル

  • 2019/03/05 16:40

    -3 <= x <= -2:
    のように(両側が<=で)表示されていますが,無回転状態のことを考慮すると一方の境界を含まないようにしないとダメなのかも?

    (ところで,元々の方法の側の話も残してあっても良かったのではないでしょうか.)

    キャンセル

  • 2019/03/06 11:05

    みなさん,ご回答ありがとうございました!
    今回はfanaさんのご回答をベストアンサーとさせていただきます.

    また何かありましたらよろしくお願いします!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る