python

楕円状にpythonで並べる

お久しぶりです。未経験エンジニアのいすいです。

最近Webサイトで要素をカッコよく配置したいなって時に、楕円状に並べたらカッコよくね!?って思い、要素を楕円状で等間隔に並べようと試みました。

そしたら、楕円状に並べるのって意外とむずい!😱

というのは、楕円って弧の長さ求めるのすごく大変みたいなんです。

楕円の弧の長さは以下の公式になります。

a:長半径、b:短半径

 

この積分は楕円積分と言って、既知の初等関数であらわすことが出来ないことがわかっています。つまり、これ以上簡単な式に直せないんです。

とはいえ積分をプログラミングでやると数値計算のアルゴリズムとか考えなくちゃいけなくて大変そう。。

 

うーん

どうしよ。。

 

と、ここで、pythonにはすごい便利なライブラリがあったことを思い出しました。

それがScipyです。

このscipyを使うと簡単に積分が解けちゃいます。🤪

(ぶっちゃけ最強です。)

というわけで、本日はpython(scipy)を用いて楕円状に物を配置するやり方をご紹介します。

 

楕円の前提

楕円状に物を配置するとは?

ということに関してですが、以下のような図を想定しています。

オレンジの線が楕円で、長半径a、短半径b

オブジェクト(今回は円を想定してますが、別にどんな形でもOKです。ただし、中心点は考える必要があります。)の中心点が楕円上にあるようにします。

正直オブジェクト自体(形、大きさ等)は重要ではなく、楕円上の等間隔の点の座標を求めることが今回の目的になります

 

求めるもの

今回は、長半径a、短半径b、オブジェクト数nから

各オブジェクトの中心点の座標を求めます。

UIは触れないのかよ!と思われた方もいるかと思いますが、それは各々で好きなように反映させてください。🙇🏻‍♂️

得られた座標は一番上から半時計まわりに順番に並べたものになります。

ちなみに長半径と短半径は逆でも問題ありません。

座標は楕円の中心を原点(0,0)としています。シチュエーションに応じて左上を原点にしたりとカスタマイズしてください。

 

コード

あらかじめ scipy は pip でインストールしておいてください。

# NumPyをインポート
import numpy as np

# mathをインポート
import math

# SciPy積分パッケージをインポート
from scipy import integrate

# 長半径
A = 440

# 短半径
B = 290

# オブジェクトの個数
N = 10

# 0からradiamまでの弧の長さ取得
def getArcLengthDiff(radian1, radian2):
    # 関数を定義
    y = lambda x: np.sqrt((A * np.cos(x))**2 + (B * np.sin(x))**2)

    # yを0からpiまで数値積分
    integ = integrate.quad(y, radian1, radian2)

    return integ[0]

# 楕円一周分の長さ
L = getArcLengthDiff(0, 2* np.pi)

# オブジェクト間の距離
d = L / N

# 計算結果のラジアンリスト
radian_list = [np.pi/2]

# 1つ前のラジアン(一番上スタート)
radian_prev = np.pi/2

# 1周後のラジアン
radian_end = 5 * np.pi /2

# 一つ目のラジアン(一番上スタート)
r1 = np.pi/2

# 2つ目のラジアン
r2 = radian_end

for i in range(N-1):
    while(True):
        if r2 - r1 < math.pow(10, -10):
            radian_list.append(r2)
            radian_prev = r2
            r1 = r2
            r2 = radian_end
            break

        midium = r2 + (r1 - r2)/2
        arc = getArcLengthDiff(radian_prev, midium)
        if arc < d:
            r1 = midium
        else:
            r2 = midium

# 各オブジェクトの座標
location_list = []

for radiam in radian_list:
    location_list.append((A*math.cos(radiam)+450, 300-B*math.sin(radiam)))

print([(int(l[0]),int(l[1])) for l in location_list])

コード解説

まず最初に長半径a、短半径b、オブジェクト数nを設定してください。

実行するとint型にパースされた座標(x,y)のリストが出力されると思います。

コードの処理の流れは以下です。

scipyを用いて、弧の長さを求める関数を定義

長半径a、短半径bから楕円の一周の長さ取得

1周の長さをオブジェクトの個数nで割って、オブジェクト間の距離を計算

2分探索を用いて、オブジェクト間距離になるラジアンを計算 
✖️(かける) オブジェクト個数

求めたラジアンから座標を取得

 

以上となります。

 

Pythonでファイル分割しよう

どうもいすいです。

前に紹介したPythonでテキストファイルを分割する方法のコードだけ紹介します。

詳しい解説は以下を参照ください。↓↓↓

コード紹介

separate.py

import sys

count = 0
idx = 0

in_file = sys.argv[1]
num_file = int(sys.argv[2])
out_files = []

# 分割ファイルをリストに入れる
for i in range(num_file):
    out_files.append('{0}_{1}'.format(i+1, in_file))

# ファイルの行数をカウントし、countに入れる
with open(in_file, 'r', encoding='utf-8') as f:
    for line in f:
        count += 1

line_count = count / num_file
n = 0

# ファイルの総行数を表示する
print('line_count : {0}'.format(count))


#分割実行部分
with open(in_file, 'r', encoding='utf-8') as i:
    for out_file in out_files:
        f = open(out_file, 'w', encoding='utf-8')
        while True:
            line_i = i.readline()
        
            if idx <= line_count:
                f.writelines(line_i)
                print('now file : {0}, current line : {1} / {2}'.format(out_file, int(idx + n * line_count), count))
            else:
                idx = 0
                break
        
            idx += 1
        n += 1
        f.close()

あとはコマンドプロンプトなどで

python separate.py <分割したいファイル> <分割したい数>

などと入力して2~3分待てばOKです。

テキストファイルをpythonで分割する

テキストファイルがでかすぎて開けない時pythonの簡単な分割コードを紹介します。

テキストファイルが開けない事件

開発中では以下のようなログファイルというのを吐き出すようにしています。

ログファイル

アプリ開発でしたら、「どんな操作した」「どんな通信をした」「どんなエラーがでた」などが記載されていきます。

このファイルを開いて中身を確認するのですが、だいたい1GBを超えたあたりから、VScodeでも開けず、メモ帳でも開けなくなってしまいます。

こうなったらファイルを分割するしかありません。


テキストファイルの分割方法

ネットで調べた結果、テキストファイルの分割には以下の方法があるみたいです。

  • フリーソフトを使う
  • windowsPowerShellでコマンドを入力する

フリーソフトですが、結構古いものが多いです。Windows10がサポート対象かチェックする必要があります。

WindowsPowerShellでは1行で処理ができますが、正直ちょっと難しいです。

結果、よくわかんないし、pythonでやっちゃえばいいじゃん的な感じで書いてみました。


pythonでテキストファイルを分割

前提として、テキストファイル(画像や動画、バイナリデータではないファイル。.txtなど)を行数に対して2分割や3分割にするコードになります。

python3.8を使っており、標準モジュールで完結します。(モジュールの追加なし)

pythonが入ってない人は、pythonの最新バージョンをインストールしてから初めてください。ネットでpythonホームページに行って、インストールするだけです。(マックブックには標準で入ってます)

➀メモ帳にコードを保存する

メモ帳を開き、

import sys

count = 0
idx = 0

in_file = sys.argv[1]
num_file = int(sys.argv[2])
out_files = []

# 分割ファイルをリストに入れる
for i in range(num_file):
    out_files.append('{0}_{1}'.format(i+1, in_file))

# ファイルの行数をカウントし、countに入れる
with open(in_file, 'r', encoding='utf-8') as f:
    for line in f:
        count += 1

line_count = count / num_file
n = 0

# ファイルの総行数を表示する
print('line_count : {0}'.format(count))


#分割実行部分
with open(in_file, 'r', encoding='utf-8') as i:
    for out_file in out_files:
        f = open(out_file, 'w', encoding='utf-8')
        while True:
            line_i = i.readline()
        
            if idx <= line_count:
                f.writelines(line_i)
                print('now file : {0}, current line : {1} / {2}'.format(out_file, int(idx + n * line_count), count))
            else:
                idx = 0
                break
        
            idx += 1
        n += 1
        f.close()

上記コードを張り付けて、separate.pyなどと名前を付けて保存します。

②フォルダに移動する

分割したいファイルとseparate.pyを同じディレクトリ(フォルダ)に移動します。(移動させなくてもよいが、相対パス・絶対パスを書く必要がなくなる)

エクスプローラー

コマンドプロンプトを起動します。windowsなら絶対入っているので、スタートメニューから探してください。マックブックはターミナルってやつですかね。

③ファイルのあるフォルダに移動する
cd <ファイルのあるフォルダのパス>

上記をコマンドプロンプト上で実行します。cdコマンドで移動したいフォルダに移動できます。以下が例になります。(マックブックの人はマックブックのコマンドに置き換えてください)

フォルダに移動

⑤separate.pyを実行する
python separate.py <分割したいファイル> <分割したい数>

上記をコマンドプロンプト上で実行します。以下が例になります。例ではsample.txtを2分割しています。

separate.py実行

ここで注意してほしいのが、分割する境界の基準が行数になっているので、ファイルのサイズが均等に分割されるわけではありません。

⑥処理が完了するまで待つ

separate.pyを実行したら以下の表示がされるはずです。

実行中の表示

line_countはファイルの総行数です。now_fileは分割先のファイル名(<数字>_<もともとのファイル名>となっています)です。current lineは実行中の行数です。

処理が終わると、以下のように番号が付けられたファイルが作成されているはずです。番号は昇順でつけられています。お好みでファイル名を変えてください。

実行後ファイルが作成されている

ちなみに、ソースコードの12行目でファイル名のフォーマットは変えられます。

以上で分割完了です!おつかれさまでした。

もしうまくいかなかったら

以下の問題がないか確認してみてください

  • pythonがインストールされていない
  • コマンドのスペルミス(空白が多い, ファイル名ミス, 引数の数があっていない)
  • フォルダに分割対象ファイルとseparate.pyが一緒に入っていない
  • フォルダに移動できていない

PAGE TOP