はじめに
最後から始めます。 これは、サラトフとエンゲルスの流通市場における不動産の平均コストを視覚化するWebマップのスクリーンショットです。
地図上の色は「伝説」の色と関連付けることができます。「伝説」の色は、数千ルーブルの総面積の平方メートルあたりの平均コストに対応します。
マップ上のポイントは、Avitoを使用したアパートの販売(流通市場)の1つのオファーに対応しています。 全体として、「伝説」に見られるようなポイントは、グラフ4943の作成に使用されました。
マップはGitHubでインタラクティブに利用できます 。
そして今、少し背景..
むかしむかし...
... mehmatの学生として不動産価格に関するデータの統計処理に関する論文を書いているときに、「サラトフの価格帯」のような、不動産価格に関する地区のグラデーションという有用な文書を偶然見つけました。 誰が作曲したかはわかりませんが、もっともらしいと分析されました(他の要因を考慮すると)近隣の価格帯のアパートの平方メートルあたりの平均コストの差は約10%でした。 それ以来、ほとんどの大人と同じように、(家計レベルで)不動産の鑑定人として複数回働く。
Jeff Kaufman Boston Apartment Price Mapsの出版を見たとき、この問題に再び「科学的に」アプローチするという考えが浮かびました。
同志は次のことを行いました。
- PadMapper Webサイトからの賃貸物件のコストに関するデータを解析するPythonスクリプト。
- 空間補間法を使用する別のスクリプトは、このデータをラスターイメージの形式でレンダリングします。
- この画像をGoogleマップのページにオーバーレイします
その結果、彼はボストンの地図上で家賃の分布のインタラクティブな地図を得ました:
最初の「うわー、クール」の後、次の考え-「そして、同じことをしようとしたら、しかし、ロシアのために、家賃だけでなく、不動産の広場の価格で」
すぐに言いますが、現在の職業では、私はむしろデータベース開発者であり、GISデータ、フロントエンド開発、統計、Pythonのいずれの専門家でもないので、親切にしてください...わかりませんが、私はこれがあまり多くないことを望み、あなたは私がそれを修正するためにそれについて書きます。
そして、ここに最初の主要なタスクがあります
AvitoによるWebスクレイピング。
当時Pythonで私は完全に「あなた」であったため、AvitoページのHTMLコードを研究した後、解析にAngleSharpを使用して、C#でライブラリの形式で自分の自転車を書くことにしました。
実装機能については詳しく説明しませんが、次のことに気付きます。
- すべてのAvito解析メタデータを個別の構成クラスに配置したため、将来、Avitoのマークアップ構造を変更するときに、構成ファイルの行を変更するだけで済みます。
- Avitoの禁止を回避するために、各広告の処理後に10秒の人為的な遅延を追加する必要がありました。 (同意する、これは最も賢い方法ではありませんが、プロキシIPの適切なプールをすぐに見つけることができませんでした)。
最後に、夜の小包を実行して、データベースのタブレットに必要なデータを記録しました
「旧基金」、「エリート」、および明らかに不十分な申し出からデータをクリアしたので、サラトフとエンゲルスの流通市場でアパートを販売する5000ポイントのサンプルを得ました。 これにより、次のステップに進むことができました。
GISデータ処理。
問題の一般的な声明:
-平面上の座標(X、Yまたは緯度、経度)を参照して、特定のメトリック(温度、高度、物質濃度など)の値の離散セットがあります。
-この平面上の任意のポイントでこのメトリックの値を予測できる必要があります。
-結果は、平面上の配色として視覚化されます。 配色については、便宜上、色をサンプリングできます。
例-ある時点で、さまざまな場所の温度センサーから測定値を取得し、処理し、次の写真を受け取りました。
実際、この種のタスクでは、「 空間補間 」の主題に没頭する必要があります。 たとえば、次のような独自の数学的装置があります。
- 逆距離重み付け(IDW)補間
- クリギング
また、人気のあるGISパッケージの一部としてツールキットがあります。たとえば、
-qGISポイントデータの内挿
-GeoStatistical Analyst ArcGIS
-SAGA GISを使用した空間補間(クリギング)
-Rのクリギング
qGISの一部としてIDWを試しました(最初のリンクを参照)-結果は私を満足させませんでした。
したがって、長い間新しいツールの開発にとらわれないために、Jeff Kraufmanの既製のPythonスクリプトを使用することを好みました。 距離を計算し、ラスター上のX、Y座標のGPS座標を変換するために、Google MapsWeb Mercatorで使用される投影法を使用せずに、単純化された線形式がここで使用されます 。
私が理解しているように、このスクリプトでは、逆距離重み付け(IDW)のテーマのバリエーションを使用しています。このスクリプトの主要な(計算上最も重い)部分は、このコードにあります。
Python空間補間コード
gaussian_variance = IGNORE_DIST/2 gaussian_a = 1 / (gaussian_variance * math.sqrt(2 * math.pi)) gaussian_negative_inverse_twice_variance_squared = -1 / (2 * gaussian_variance * gaussian_variance) def gaussian(prices, lat, lon, ignore=None): num = 0 dnm = 0 c = 0 for price, plat, plon, _ in prices: if ignore: ilat, ilon = ignore if distance_squared(plat, plon, ilat, ilon) < 0.0001: continue weight = gaussian_a * math.exp(distance_squared(lat,lon,plat,plon) * gaussian_negative_inverse_twice_variance_squared) num += price * weight dnm += weight if weight > 2: c += 1 # don't display any averages that don't take into account at least five data points with significant weight if c < 5: return None return num/dnm def distance_squared(x1,y1,x2,y2): return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)
データ配列を必要な形式に調整し、bedroom_category(部屋数)に関連する相関計算を行うスクリプトから一部を削除し、入力パラメーターを使用して計算を開始しました。 私のコンピューター(Intel®Core(TM)i7-6700 CPU、16GB RAM)では、入力で5,000ポイント、出力での画像解像度1000 * 1000で計算するのに約30分かかりました。
フロントエンドで少し動作するように残っています。 Jeff Kraufman のBostonのHTMLソースマップを調べます。 データに応じて少し変更すると、そのような「オイルの写真」が得られます。
ジオリファレンスで明示的な「ジャム」を確認するには、同じポイントセットをqGISにロードし、Googleマップをロードして、画像を比較します。
描かれた地図上のポイントがその場所にあり、その上の色が村のアパートの価格をもっともらしく反映していることを確認したら、呼べます。 うまくいきます!」
しかし、私たちがボルガに登った写真が少し動揺しています。 当然、計算アルゴリズムはVolgaについて何も知りません。
そのため、タスクが発生します。
海岸線地図上の画像の切り抜き
海岸線を考慮して都市の行政境界に関する地理データを検索する試みは失敗しました。 サラトフがあり、エンゲルスがあり、その境界はヴォルガの真ん中を通ります。 さて、私たちは他の方法で行きます。
オープンソースから海岸線のデータを含むシェイプファイルをダウンロードし、qGISのベクターレイヤーとしてロードし、必要なポリゴンを選択し、ファイル構造を整理し、サラトフ近くのヴォルガに属するポリゴンを引き出し、データベースにデータを入力します。
次のようなちょっとした魔法 :
DECLARE @Volga geography; select @Volga= PlacePolygon.MakeValid() from PlacesPolygons where PlacesPolygonID =1003 DECLARE @Saratov geography; select @Saratov= PlacePolygon.MakeValid() from PlacesPolygons where PlacesPolygonID =2 select @Saratov.STDifference(@Volga)
そして、海岸線を考慮して、都市の境界である適切な訓練場を取得します。
これをqGISにロードし、スクリプトによって生成された画像をラスターレイヤーとしてロードし、この画像のジオリファレンスを実行し、都市境界のあるポリゴンに沿ってトリミングします。すべて標準のqGISツールを使用します。 もし...私がそれをどうにかして素晴らしいなら。 しかし悲しいかな... qGISのジオリファレンスとは関係がありませんでしたが、Microsoft.SqlServer.Typesの空間関数を使用することを何度か試みた後、シャープのユーティリティの形で別のバイクを簡単に作成できるようになりました。
GEOポリゴンでパターンをトリミングするためのC#コード
string GPSPolygonVolga = " POLYGON ((46.2324447632 51…. ))"; private void CropByGPS() { Bitmap bmp = new Bitmap(inputFilePath); int w = bmp.Width; int h = bmp.Height; var GpsSizeOfPyxelX = (MAX_LON - MIN_LON) / w; var GpsSizeOfPyxelY = (MAX_LAT - MIN_LAT) / h; var VolgaPolygon = SqlGeography.STPolyFromText(new System.Data.SqlTypes.SqlChars(GPSPolygonVolga), SRID); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { Color c = bmp.GetPixel(x, y); var GPS = SqlGeography.Point(MAX_LAT - GpsSizeOfPyxelY * y, MIN_LON + GpsSizeOfPyxelX * x, SRID); if (VolgaPolygon.STContains(GPS)) // "" bmp.SetPixel(x, y, Color.Transparent); } } bmp.Save(outputFilePath, System.Drawing.Imaging.ImageFormat.Png); }
ラスターを含むソースファイルをダウンロードし、出力で同じファイルを受け取りましたが、ビット「ビット」形式です。 ブラウザのGoogleマップに表示します。Volgaには何も登りません、すばらしい! もう少し詳しく見てみると...ある場所では過剰を食い止めているようです。 手順にデータやカントを入れることはできませんか? 繰り返しますが、qGISでロードされたレイヤーを見てください。
はい...確かに、時々あちこちで...貯水池層からの埋立地は明らかに都市に登ります。 直接的なのはエンゲルスではなく、ある種のヴェネツィアです...完璧への欲求はありません。私たちは反対に行きます。
ベクターデータは信頼できないため、ラスターデータを使用します。 2つの画像を重ね合わせ、2番目の「青」に対応する最初のポイントから切り取る必要があります。
たとえば、 Google Maps静的APIは、地図付きの写真を提供できます。
API_KEYを取得しました。次のことがわかります。
-無料で使用できる最大解像度= 640 * 640
-BoundingBoxによるパラメーター化-BING静的マップAPIとは異なり、Googleはサポートしていません。また、座標平面内の2つの画像を追加で関連付ける必要があります。
さて、試してみてください。 Google Statics Map APIから、関心のあるエリアをカバーする地図を含む特定の画像を取得し、次のコードを記述します。
C#画像のトリミング
private static void CropByMapImage() { Bitmap bmp = new Bitmap(inputFilePath); Bitmap bmpMap = new Bitmap(mapToCompareFilePath); int w = bmp.Width; int h = bmp.Height; var GpsSizeOfPyxelX = (MAX_LON - MIN_LON) / w; var GpsSizeOfPyxelY = (MAX_LAT - MIN_LAT) / h; int wm = bmpMap.Width; int hm = bmpMap.Height; var cntAll = w * h; var cntRiver = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { var currentLatitude = MAX_LAT - GpsSizeOfPyxelY * y; var currentLongitude = MIN_LON + GpsSizeOfPyxelX * x; if (currentLongitude < MIN_LON_M || currentLongitude > MAX_LON_M || currentLatitude < MIN_LAT_M || currentLatitude > MAX_LAT_M) continue; //todo - something with this ugly code var CorrespondentX = (int)((currentLongitude - MIN_LON_M) / (MAX_LON_M - MIN_LON_M) * wm); var CorrespondentY = (int)((MAX_LAT_M-currentLatitude) / (MAX_LAT_M - MIN_LAT_M) * hm); Color cp = bmp.GetPixel(x, y); //color of current pixel Color mp = bmpMap.GetPixel(CorrespondentX, CorrespondentY); //color of correspondent Map pixel if (Math.Max(Math.Max( Math.Abs(mp.R - WaterMapColor.R) , Math.Abs(mp.G - WaterMapColor.G)) , Math.Abs(mp.B -WaterMapColor.B))< colorDiff /* && cp.A != Color.Transparent.A */)// blue water is not always the same blue { bmp.SetPixel(x, y, Color.Transparent); cntRiver++; } } } bmp.Save(outputFilePath, System.Drawing.Imaging.ImageFormat.Png); }
640 * 640の解像度のファイルを使用して、1000 * 1000の解像度でファイルを処理すると、次のような結果が得られます(ここでは、わかりやすくするために、透明にする必要があるポイントを黄色にします)。
また、Googleマップが個別にスケーリングされるため、結果として生成されたラスターに大きなマージンが重なるため、結果は悲しいものになります。 つまり、「クロップ」するのに適したスケールのカードをたくさん用意する必要があります。または、何らかの方法でカードを見つける必要がありますが、解像度は十分です。
I-2番目の方法、つまり、ブラウザから通常のGoogleマップの希望する投影のスクリーンショットを撮りました。 コーナーのGPS座標(マッピングバウンド)、これをJavaScriptコードに追加することでわかりました。
google.maps.event.addListener(map, 'bounds_changed', function () { console.log(map.getBounds());
保存したスクリーンショットの新しいマップを使用してラスターを処理し、ブラウザーにアップロードしました...結果は私を喜ばせそうでした。 かなり正確に(GPS測位の精度内で)地元の都市公園の小さな池でさえ「クリーンアップ」されました。
それだけです。クエストの最初の段階が完了したことを考慮し、GitHubの結果をホスティング用に共有されたフォルダーで満たし、ITコミュニティに自慢できます。
それでは、次は何ですか?
そして、多くの質問が発生しますが、それに対する明確な答えはまだわかりません。
- 他の空間補間法と比較して、ここで計算アルゴリズムはどの程度正しく機能しますか
- プロパティの場所に加えて、他のどのような要素が計算するか、計算モデルに入る方法を考慮するのが理にかなっています
- データベース内のデータを絶えず更新し、この種の不動産評価システムに基づいて構築することは可能ですか? (Avitoの代表者がこのサイトで私を読んでくれたら、コメントを聞きたいです)
まあ...この場所を読んでくれたみんなに感謝します。 私は実際の問題を解決することに興味がありましたが、それは「私にとってはかなり挑戦的」であることが判明しました。私の経験について読んでください。