2022年9月28日水曜日

Pythonでヒストグラムを生成する【3/3】

 前回



前回はデータの分類をやったので今回はグラフの作成


3.グラフの作成



こういったグラフにするためには、「階級数」だけリストを作成し、それらの要素数は「最大の階級の値」する。そのうえで、各リストを最後尾から「■」で埋めて、最後に縦軸を合成すればいい。つまり、階級数が10、最大の階級の値が10であれば、10個の空白の要素を持つ10個のリストを用意すればよい。
  1. #sep_listは階級(横軸)
  2. #separatedは元データを階級別に分けたもの
  3. longest = 0
  4. for i in separated:
  5. if len(i) > longest:
  6. longest = len(i)
  7. y = longest + 1
  8. x = len(sep_list)
  9. graph = [[' ']*x for _ in range(y)]
  10. sep_list = list(map(lambda x: (map(lambda y: str(y),x)), sep_list))
  11. #sep_listは[int, int]の形なのでstrに変換
  12. graph[y-1] = list(map(lambda x:'~'.join(x), sep_list))
  13. #グラフのリスト末尾に軸を追加
  14. for i in range(x):
  15. volume = len(separated[i])
  16. for k in range(volume,0,-1): #後ろから■で埋めてく
  17. graph[y-1-k][i] = "■"

最後にグラフの縦軸を作成する。
まず、グラフの縦軸の数字を含むリスト用意する。縦軸は、グラフを含むリストの一次元目の要素数~1までをとる。ここでは次のステップの都合上0まで含むリストを「要素数」、「要素数-1」... 「0」といった形で作成する。
その後、このリストをグラフリストの一次元目の要素(リスト)の先頭に入れる形で結合する。
最後にグラフリストの[0][0]を半角スペースに置換する。


  1. #graphはグラフを格納しているリスト
  2. num = [[i] for i in range(len(graph)-1,-1,-1)]
  3. for i in range(len(graph)):
  4. graph[i] = num[i] + graph[i]
  5. self.graph[len(graph)-1][0] = ' '
最後にこれまでのコードをまとめてクラス化するとこう
  1. class histogram:
  2. """docstring-
  3. data_array -> ヒストグラムにしたいデータ(一次元リスト)
  4. sep -> ヒストグラムの階級
  5. drawメソッドで実行
  6. output_to_csvメソッドでワーキングディレクトリにcsv書き出し
  7. """
  8. def __init__(self, data_array, sep):
  9. self.data = data_array
  10. self.s = min(data_array)
  11. self.e = max(data_array)
  12. self.sep = sep
  13. self.separated = []
  14. self.sep_list = []
  15. def draw(self):
  16. self.distribute_data()
  17. self.select_list()
  18. self.draw_a_histogram()
  19. self.draw_vertical_axis()
  20. def draw_a_histogram(self):
  21. longest = 0
  22. for i in self.separated:
  23. if len(i) > longest:
  24. longest = len(i)
  25. y = longest + 1
  26. x = len(self.sep_list)
  27. self.graph = [[' ']*x for _ in range(y)]
  28. self.sep_list = list(map(lambda x: (map(lambda y: str(y),x)), self.sep_list))
  29. self.graph[y-1] = list(map(lambda x:'~'.join(x), self.sep_list))
  30. for i in range(x):
  31. volume = len(self.separated[i])
  32. for k in range(volume,0,-1):
  33. self.graph[y-1-k][i] = "■"
  34. def distribute_data(self):
  35. n = (self.e - self.s+1)//self.sep
  36. if (self.e - self.s +1)%self.sep != 0:
  37. n += 1
  38. self.separated = [[] for _ in range(n)]
  39. for i in range(n):
  40. if i == 0:
  41. self.sep_list.append([self.s, self.s + self.sep -1])
  42. else:
  43. previous = self.sep_list[i-1][1]
  44. nxt = previous + self.sep
  45. if nxt >= self.e:
  46. nxt = self.e
  47. if previous + 1 == nxt:
  48. self.sep_list.append([previous+1])
  49. else:
  50. self.sep_list.append([previous+1, nxt])
  51. def select_list(self):
  52. for i in self.data:
  53. counter = (i - self.s) // self.sep
  54. self.separated[counter].append(i)
  55. def draw_vertical_axis(self):
  56. num = [[i] for i in range(len(self.graph)-1,-1,-1)]
  57. for i in range(len(self.graph)):
  58. self.graph[i] = num[i] + self.graph[i]
  59. self.graph[len(self.graph)-1][0] = ' '
  60. def output_to_csv(self):
  61. import csv
  62. with open("./histogram.csv",encoding = "utf-8",newline='',mode="w") as f:
  63. writer = csv.writer(f)
  64. writer.writerows(self.graph)

2022年9月23日金曜日

Pythonでヒスグラムを生成する【2/3】

 前回

https://beyondthe20th.blogspot.com/2022/09/python13.html


前回は1のステップをやったので今回は2から

2.データの分類

前回の投稿で、横軸の数は、「(最大値 - 最小値+1) / 横軸の幅」で算出した。データを格納すべきリストの数と横軸の数は同じなので、同数のリストを作成する。
    	distributed_list = [[] for _ in range(n)] 
        #nは横軸の数「(最大値 - 最小値+1) / 横軸の幅」 
ここで作成したリストにデータを格納していく。
これは、そのデータが何番目のリストに格納されるかを調べるためには、「(対象のデータ-データリストの最小値)//横軸の幅」で求められる。つまり、10区切り、1~100の場合に、8は、(8-1)//10 = 0となり、0番目のリストに格納される(つまりindex = 0)。
これをPython 3のコードに直すと以下のようになる。
  1. for i in data_array:
  2. counter = (i - start) // sep
  3. distributed_list[counter].append(i)
これでdistributed_listには次のようにデータが格納された。
distributed_list = [[1番目の軸のデータ], [2番目の軸のデータ]...]
これでデータの分類が完了した。あとは、各配列に格納されているデータの個数をカウントし、縦軸を生成したうえでグラフ化、CSV出力を行えば、作成完了になる。


次回


2022年9月19日月曜日

Pythonでヒストグラムを生成する【1/3】

ヒストグラムは、品質管理の七つ道具として有名で、これを作成するライブラリやソフトウェアは、数多くある。Pythonのライブラリにもmatlabがあるし、自分で作るものでもないのだが、Pythonistaたるものライブラリに頼らず自分で作っていきたいところである。
今回は、ExcelやLibre Office Calcで表示することを前提にCSVでヒストグラムを生成するコードを書いてみた。
出来栄えはこんな感じ、完璧なヒストグラムと言っても過言ではないではないだろう。


さて、これを作るにあたっては、元データを読み込んでから、いくつかステップを踏まねばならない。今回は以下の手順で行った。
  1. 横軸の作成
  2. データの分類
  3. グラフ化
以下では順を追ってみていきたい。

1.横軸の作成

横軸の作成の作成のためには、元データから最初の項目と最後の項目を確認したうえで、いくつのリストが必要かを確認し、横軸のデータを格納するリストを用意する必要がある。従って、以下のような処理が必要になる。
  1. start = min(data_array)
  2. end = max(data_array)
  3. n = (end - start+1)// sep #(最大値-最小値+1)//横軸の幅
  4. if (end - start+1)/sep != 0:
  5. n += 1
ここでは、「(最大値 - 最小値+1) / 横軸の幅」の切り上げた値が用意すべきリストの数になることになる。つまり、1~100まで5刻みで横軸を用意したいのであれば、(100-1+1)/5 = 20、つまり20個のリストが必要となり、最後のリストには91~100までが格納される。また、1~101であれば、(101-1+1)/5 = 20.2となり、同様に21個のリストが必要となり、最後のリストには、101が格納される。
次に、軸の値を格納する必要がある。つまり、1~100まで10刻みであれば、1~10、11~20、21~30...といった形で軸の値をリストに格納していく必要がある。これは次のようになる。
  1. #sep_listは軸の値を格納するためのリスト、sepは刻みの値、nは必要なリスト数
  2. for i in range(n):
  3. if i == 0:
  4. sep_list.append([start, start + sep -1])
  5. else:
  6. previous = sep_list[i-1][1]
  7. nxt = previous + sep
  8. if nxt >= end:
  9. nxt = end
  10. if previous + 1 == nxt:
  11. sep_list.append([previous+1])
  12. else:
  13. sep_list.append([previous+1, nxt])
これは、前工程で確認したn個のリストの数だけループを回し、ループの最初で「[最小値,最小値+刻み-1,]」、それ以降は、「[前の値+1, 前の値+刻み]」といった形で処理を行っている。つまり、1~100を10刻みで軸とした場合は、最初のループで「[1, 1+10-1]」、二番目のループは「[10+1, 10+10]」という処理になっている。
これで横軸の作成が完了したので、次に収集したデータを分類して、ヒストグラム本体を作成していく。

2/3
3/3

2022年9月10日土曜日

File APIを用いたCSVファイルの入出力

VBAを使いたくないので、HTML+JavaScriptでCSVファイルを処理したいものの、File APIに関する記述がそこら中に散らばっていて、入出力をまとめた記事がないので今回作成してみた。

ファイルオブジェクトの読み書きに関してはPromiseとイベント駆動のものがあるが、今回は、わかりやすいのでイベント駆動のものを採用した。
EventListenerのコールバック関数に引数を与えるのはどうもうまくいかなかったので、グローバルオブジェクトにファイルの内容をロードする形にして読み込み→グローバルオブジェクトで読み書きとした。


  1. let loaded_csv = []; //ファイル読み込み用のグローバルオブジェクト
  2. function load(e){
  3. for (let file of e.target.files){
  4. let reader = new FileReader();
  5. reader.onload = event => {
  6. let csv = event.target.result.trim(/\r\n/).split(/\r\n/);
  7. csv = csv.map( x => x.split(','));
  8. loaded_csv.push(csv);
  9. }
  10. reader.readAsText(file);
  11. }
  12. }
  13. function create(write_array, filename){
  14. let csv_string = "";
  15. for (let i of write_array){
  16. csv_string += i.join(',');
  17. csv_string += '\r\n';
  18. }
  19. let b = new Blob([csv_string], {type: "text/csv"});
  20. let url_link = URL.createObjectURL(b);
  21. let link = document.createElement("a");
  22. link.download = filename;
  23. link.href = url_link;
  24. document.body.appendChild(link);
  25. link.click();
  26. }