9000+次曝光

#分享 清洗 14 年台灣實價登錄資料,我踩到的坑

我本來以為這是個週末專案。 內政部的不動產實價登錄是公開資料,每旬(每月 1、11、21 日)釋出批次檔,涵蓋民國 101 年第 3 季至今、約 14 年、22 個縣市。我想做的事很單純:把它變成一張能查的表。結果這個「單純」的目標,讓我把 4,824,149 筆買賣交易翻過來又翻過去,最後隔離率壓到 0.094% 才算勉強滿意。 這篇記錄我踩到的坑。如果你也打算自己處理這份資料,希望能幫你少走一點路。 一、表面上的坑:一眼就看得到的那種 下載下來先是一堆分縣市、分期別的 zip,每個 CSV 打開都有兩列表頭——第一列中文、第二列英文。你要是直接 read_csv,第一筆「資料」會是英文表頭。 再來是日期。1130520 不是西元,是民國 113 年 5 月 20 日,得 +1911。樓層更妙,「移轉層次」寫的是中文:「十八層」「地下二層」「全」。你得自己把中文數字解析成整數,還要處理「地下」變負數、「全」對應到總樓層。 這些都煩,但都是機械性的煩,寫一次就過了。真正讓我停下來想很久的,是下面這些。 二、六碼日期之謎:資料自己證明了假說 清洗交易日期時,絕大多數是七碼 YYYMMDD,但冒出一批六碼的,像 990728。 第一反應:髒資料,丟掉。但數量不對——有四千多筆,集中在最早那幾期。丟掉之前我多看了一眼:990728,如果補一個 0 變成 0990728,就是民國 99 年 7 月 28 日,一個完全合理的日期。這些是把民國年寫成兩位數的舊格式。 問題是,交易日期怎麼會早於制度上線(2012,民國 101 年)?一筆「民國 99 年」的交易出現在後來才釋出的批次裡,說得通嗎? 我做了一件事驗證:把這批補 0 救回的列(民國 90–99,共 4,128 筆)拿出來,比對它們的「交易日期」與「建築完成日期」。結果是——97%(3,302 / 3,415)的建築完成日晚於交易日。 這一刻我知道假說對了:這些是預售屋。買方簽約在前(交易日),房子蓋好移轉登記在後(完工日),而申報時填的交易日期是當年的原始契約日。所以一筆民國 99 年簽約、102 年完工的預售屋,會帶著「民國 99 年」的交易日,出現在後來的批次裡。資料不是髒,是我一開始沒讀懂。 最後的處理:六碼補 0 還原,只接受還原後民國年落在 [90, 99] 且月日有效的;還原不了的(342 筆)標記 R-03-YYMM-AMBIG 隔離,不硬湊日精度。民國 90–94 的尾端只有 124 筆(佔全庫 0.0026%),不是雜訊叢集,就留著。 三、備註欄的語意陷阱:字面比對會出事 備註欄是這份資料的靈魂,也是地雷。它記載了特殊交易——親友間交易、法拍、瑕疵屋、持分……這些交易的價格通常偏離市場,統計時得標記出來甚至排除。 最直覺的做法是關鍵字比對:備註含「海砂屋」就標記為瑕疵。但我差點掉進一個坑:有些備註寫的是「賣方不負物之瑕疵擔保責任,海砂、輻射、兇宅除外」。這句話的意思是「這間不是海砂屋」,你要是命中「海砂」就標記,剛好標反。 我的裁決方式是做「免責句式分析」:統計某個詞出現在正向斷言還是除外免責句的比例。實測「海砂屋」有 96%、「輻射屋」有 97% 是正向陳述(直接說這間是),只有約 2% 落在除外句。於是我用帶「屋」字的 海砂屋/輻射屋 當關鍵字,剛好避開那句「海砂、輻射……除外」的免責樣板。 另一個兩義的詞是「夾層」。夾層可能是合法登記的樓層,也可能是灌進坪數、拉高單價的違建增建。我一樣做了分析:27,129 筆命中裡,明確否定的(無夾層、夾層除外)只有 16 筆(0.06%),99.94% 是正向陳述,而且多半跟「增建」「頂樓」同時出現。所以我把它歸到「未登記」類。 至於那些出現在 8–9% 交易裡的「土地及建物分件登記案件」之類——那是登記結構的樣板文字,不是特殊交易,不給任何標籤。分得清哪些是雜訊、哪些是訊號,才是這欄的重點。 四、為什麼你不能直接拿官方 CSV 算中位數 這是我覺得最值得講的一點。 全庫有 18.51% 的交易被標記為特殊交易。這些交易系統性地偏離市場價——親友交易通常偏低、含車位捆綁的坪價被稀釋。如果你直接拿官方原始資料算某區的中位數,等於把這些雜訊一起算進去。 具體有多少差?拿樣本(臺北市 + 板橋,最近四季)裡「大安區・住宅大樓」的每坪中位數: 直接算(含特殊交易):1,074,759 元/坪 排除 is_special 之後:1,144,507 元/坪 差 6.09%,大約每坪 7 萬元。你以為你在看市場行情,其實被親友交易之類的低價案往下拉了。(全庫尺度同計約 5.5%。)車位捆綁也一樣:板橋排除車位捆綁的列之後,每坪中位數上修約 3.15%。 這不是要你信我的數字——這正是我把清洗欄位(is_special、special_tags、parking_bundled)全部留在資料裡的原因。你可以自己排除、自己驗算。 五、工程紀律(一段就好) 處理這種會迭代的清洗規則,我守三條:隔離不丟棄(任何被剔除的列都進 quarantine、可回放,不靜默消失)、冪等重建(同一份原始檔重跑,資料庫內容雜湊不變)、逐期自動驗收(56 期每期跑列數恆等、主鍵唯一、孤兒率等檢查)。 老實說,這三條不是我一開始就做好的。我曾經有個 bug:主檔最終型別轉換用了 strict cast,某一列的房間數是 3,333,333(明顯是誤植)撐爆了 int16,整個轉換拋例外,然後被外層的 try/except 靜默略過——整個檔案被跳過。三個檔案、約三萬列就這樣無聲消失,還連帶讓它們的明細變成八萬多筆「孤兒」。是驗收裡的孤兒率異常把它抓出來的。修法是把 cast 改成非 strict(溢位變 NULL、保留該列),並讓任何 transform 例外整檔進 quarantine 而不是消失。 我把這段寫出來,是因為「隔離不丟棄」這條紀律的價值,恰恰是在它幫我抓到自己的 bug 時才真正體現。 六、成果與後續 最後這份資料:4,824,149 筆買賣、56 期無缺、隔離率 0.094%、每旬更新(官方發布後 T+1,非即時)。 我把臺北市全區 + 板橋、最近四季共 16,857 筆整理成一個可以直接跑的樣本 repo,JSONL 和 CSV 都有,附資料字典和上面那幾個範例的可執行程式碼: 樣本 repo:
完整 14 年、22 縣市、482 萬筆的全量資料集(現貨,單次購買:全台 NT$4,900 / 單縣市 NT$990 早鳥)——預購登記:
;API 查詢端點開發中,同處登記候補。 聲明一下:這是我自己做的第三方加值,不是內政部官方服務,跟內政部沒有任何合作或授權關係。資料就是官方的公開資料,我只是把坑填一填。 如果你也在跟這份資料搏鬥,歡迎交流。
愛心
跪
驚訝
22
4
全部留言
這就是 Data Engineer 嗎?跪了
服了。
快點來宣傳 來產品集發表你的的產品
感謝分享!超猛! 資料清理真的是大學問。