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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

2118閲覧

コードがどのように動いて出力結果が出ているのか理解できなくて困っています。

mi56

総合スコア14

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2017/05/04 17:07

###前提・実現したいこと
Pythonでk平均法について勉強しようとしています。
本を見ながら進めており、参考コードは以下のURLのgithub上にあります。
https://github.com/joelgrus/data-science-from-scratch/blob/master/code/clustering.py
###発生している問題・エラーメッセージ

このコードを実行したところ、K=3とし3つのクラスタの中心点が結果として表示されているのですがコードがどのように動作して結果が導き出されてているのか分からなくて困っています。
基本的な文法については理解しているつもりです。

###該当のソースコード

from __future__ import division import math, random from functools import reduce import re, math, random from collections import defaultdict, Counter # # functions for working with vectors # def vector_add(v, w): """adds two vectors componentwise""" return [v_i + w_i for v_i, w_i in zip(v,w)] def vector_subtract(v, w): """subtracts two vectors componentwise""" return [v_i - w_i for v_i, w_i in zip(v,w)] def vector_sum(vectors): return reduce(vector_add, vectors) def scalar_multiply(c, v): return [c * v_i for v_i in v] # this isn't right if you don't from __future__ import division def vector_mean(vectors): """compute the vector whose i-th element is the mean of the i-th elements of the input vectors""" n = len(vectors) return scalar_multiply(1/n, vector_sum(vectors)) def dot(v, w): """v_1 * w_1 + ... + v_n * w_n""" return sum(v_i * w_i for v_i, w_i in zip(v, w)) def sum_of_squares(v): """v_1 * v_1 + ... + v_n * v_n""" return dot(v, v) def magnitude(v): return math.sqrt(sum_of_squares(v)) def squared_distance(v, w): return sum_of_squares(vector_subtract(v, w)) def distance(v, w): return math.sqrt(squared_distance(v, w)) class KMeans: """performs k-means clustering""" def __init__(self, k): self.k = k # number of clusters self.means = None # means of clusters def classify(self, input): """return the index of the cluster closest to the input""" return min(range(self.k), key=lambda i: squared_distance(input, self.means[i])) def train(self, inputs): self.means = random.sample(inputs, self.k) #print(self.means) assignments = None while True: # Find new assignments new_assignments = map(self.classify, inputs) # If no assignments have changed, we're done. if assignments == new_assignments: return # Otherwise keep the new assignments, assignments = new_assignments for i in range(self.k): i_points = [p for p, a in zip(inputs, assignments) if a == i] # avoid divide-by-zero if i_points is empty if i_points: self.means[i] = vector_mean(i_points) if __name__ == "__main__": inputs = [[-14,-5],[13,13],[20,23],[-19,-11],[-9,-16],[21,27],[-49,15],[26,13],[-46,5],[-34,-1],[11,15],[-49,0],[-22,-16],[19,28],[-12,-8],[-13,-19],[-41,8],[-11,-6],[-25,-9],[-18,-3]] random.seed(0) # so you get the same results as me clusterer = KMeans(3) try: clusterer.train(inputs) except: val =+ 1 print ("3-means:") print (clusterer.means) 3-means: [[-25.857142857142854, -4.714285714285714], [19, 28], [13, 13]]

###補足情報(言語/FW/ツール等のバージョンなど)
Python3.6を使用しています。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

まずそのアルゴリズムが何をしているかのイメージを掴んで下さい。 クラスタリングの定番アルゴリズム「K-means法」をビジュアライズしてみた がわかりやすいと思います。

次にアルゴリズムの詳細を理解してください。Wikipedia でいいと思いますが、これで分かりにくければもう少し調べてください。

アルゴリズムを把握すれば自分で作れると思います。自分で作れるなら人の書いたコードも読めます。

投稿2017/05/04 22:50

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

まずはじめに、このコードはPython3では

Python

1 new_assignments = map(self.classify, inputs)

の部分を

Python

1 new_assignments = list(map(self.classify, inputs))

としないと動作しない気がします。

で、どのように動作しているかですが、
ベクトル演算を行う為の関数群が前半を占めており複雑そうに見えますが、
後半の実際にK-Means法を行っている部分はそれほど複雑ではありません。

ベクトル演算処理を Numpy で行うと、前半部が不要になる為もう少しスッキリと記述できると思います。

後半部でやっていることは 一般的なK-Means法のアルゴリズムの通り

  1. 各クラスタの重心点をランダムに選出
  2. ノード毎に各クラスタの重心との距離を求め、一番近いクラスタを割り当てる
  3. クラスタに割り振ったノードを基に、各クラスタの重心を算出
  4. 新しく求めた各クラスタの重心を基に、ノードを再度クラスタに割り当てる
  5. 前回のクラスタ割り当てから変更が無かったら終了。変更があった場合は再度 3. 以降を繰り返す。

です。
上記の 2.4.で行っている、ノードをクラスタに割り当てる部分を KMeans.classify() が行っており、 classify()関数の呼び出しを含めて上記のアルゴリズム全体を行っているのが KMeans.train()となります。

KMeansクラスのコードにコメントを追加したものを下に記述したましたので、確認してみてください。

Python

1class KMeans: 2 def __init__(self, k): 3 self.k = k # CLUSTERの数 4 self.means = None # 各CLUSTERの重心 5 6 def classify(self, input): 7 # 入力されたノードのに一番近いCLUSTERの番号(0~k)を返す 8 return min(range(self.k), 9 key=lambda i: squared_distance(input, self.means[i])) 10 11 def train(self, inputs): 12 # 各CLUSTERの重心をランダムに選出 13 self.means = random.sample(inputs, self.k) 14 # 各ノードがどのCLUSTERに属しているかのリストを初期化 15 assignments = None 16 17 while True: 18 # 各ノードがどのCLUSTERに属しているかのリストを作成 19 new_assignments = list(map(self.classify, inputs)) 20 21 # 上記の所属CLUSTERリストが変更していなかったら終了 22 if assignments == new_assignments: 23 return 24 25 # 上記の所属CLUSTERリストを保持 26 assignments = new_assignments 27 28 # CLUSTER番号毎に重心を算出する処理を行う 29 for i in range(self.k): 30 # CLUSTER(i)に属しているノードリストを抽出 31 i_points = [p for p, a in zip(inputs, assignments) if a == i] 32 # 上のCLUSTER(i)のノードリストからCLUSTERの重心を算出 33 if i_points: 34 self.means[i] = vector_mean(i_points)

投稿2017/05/25 06:30

magichan

総合スコア15898

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問