背景
背景就是師傅讓我寫一個推薦算法,來推薦我們組空閑的移動設(shè)備給需要使用的人。我們的痛點在于:
1、 不知道誰手頭的手機空閑,往往挨個輪詢借手機;
2、 不知道自己的需求要用什么設(shè)備去測試;
針對這兩個痛點,我們給出了基于語義分析的機型推薦系統(tǒng)和基于用戶行為的設(shè)備推薦系統(tǒng)。暫時我們第一個階段只做設(shè)備推薦系統(tǒng)。
數(shù)據(jù)收集以及清洗:
作為一個推薦系統(tǒng),我們首先要確定數(shù)據(jù)來源。我們使用了日常設(shè)備登記的平臺——設(shè)備登錄系統(tǒng)的后臺數(shù)據(jù)庫。選擇了其中的login表作為數(shù)據(jù)來源。然后,就到了令我痛不欲生的數(shù)據(jù)清理過程。。。
源數(shù)據(jù)分析
首先我來了一句
select count(username) from login;
好家伙,有4000多個用戶?我們組人口流動這么大?4000人縮減到30?WTF?那我們看看到底有哪一些人吧。
select distinct(username) from login;
我去,怎么這么多莫名奇妙的玩意啊?什么用戶名后加一些莫名奇妙的東西的數(shù)據(jù)?什么web JavaScript
?老哥,你們當初數(shù)據(jù)都不校驗就直接扔數(shù)據(jù)庫的嗎?你的后臺能力很強。
但又能怎么辦呢?洗唄,我洗,我洗,我洗洗洗。
怎么洗呢?
數(shù)據(jù)清洗
我發(fā)現(xiàn)好像系統(tǒng)有過幾個版本,一種username
后加一個\n
的,一種加-%
的,還有加Customer Header
的。來來來,update login set username=substring(username,'\\',1)
了解一下。但是手動update總是令人頭疼,怎么破?Python
啊,pymysql
啊。
核心思想就是:
1、 將所有username
按照字符串的順序進行order by
,這樣相同開頭的字符串就會在一起
select username from login order by username
2、 提取最長公共子串,可以采用歸并的想法。def getCommonStr(str1, str2)
,最后獲得commonStr
,然后用下面的代碼:
sql_statement = 'update login set username=\'%s\' where username like \'%s%%\''
cusor.excute(sql_statement % (common_str, common_str))
cusor.commit()
然后就基本清洗了所有用戶名的數(shù)據(jù)。
數(shù)據(jù)分析
數(shù)據(jù)分析相對簡單,就是一些常規(guī)的pandas
,matplotlib
的操作。但是這些數(shù)據(jù)沒有離散話,源數(shù)據(jù)中username
,device_code
都是varchar
類型的,如果需要離散化,需要在讀取數(shù)據(jù)的時候維護兩個dict
或者兩個set
兩個list
。后來我就想干脆就在login表上進行分解操作吧,將原表分解為devices
和users
。
sql代碼如下:
insert into devices(code) select distinct(code) from login where code like 'TKMB%';
insert into users(username) select distinct(username) from login where code like 'TKMB%';
然后更新原表就出了大問題。
原表更新
第一種方案:
Database changed
mysql> insert into my_login_table (user_id, device_id, applogintime)
-> (select tmp_table.user_id, devices.id, tmp_table.applogintime
-> from (select users.id as user_id, login.applogintime as applogintime, login.code as code from users, login where users.username = login.username and login.code like 'TKMB%') as tmp_table, devices
-> where devices.code = tmp_table.code);
Query OK, 584591 rows affected (2 hours 21 min 12.56 sec)
Records: 584591 Duplicates: 0 Warnings: 0
好家伙,兩個小時,再看看效果
select count(*) from my_login_table;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 27
Current database: ptest
+----------+
| count(*) |
+----------+
| 1169182 |
+----------+
1 row in set (0.29 sec)
多了一倍的數(shù)據(jù)量。有問題有問題。
這時候我想是不是應(yīng)該建立索引并采用連接?
果斷嘗試:
mysql> alter table devices add index unique(code);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'unique(code)' at line 1
mysql> alter table devices add unique devices_index(code);
Query OK, 0 rows affected (0.43 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table users add unique users_index(username);
Query OK, 0 rows affected (0.20 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> delete * from my_login_table;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '* from my_login_table' at line 1
mysql> delete from my_login_table;
Query OK, 1169182 rows affected (4.10 sec)
mysql> show index from devices;
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| devices | 0 | PRIMARY | 1 | id | A | 412 | NULL | NULL | | BTREE | | |
| devices | 0 | devices_index | 1 | code | A | 412 | NULL | NULL | | BTREE | | |
+---------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)
mysql> insert into my_login_table (user_id, device_id, applogintime)
-> (select tmp_table.user_id, devices.id, tmp_table.applogintime
-> from (select users.id as user_id, login.applogintime as applogintime, login.code as code from users inner join login
-> on users.username = login.username) as tmp_table inner join devices
-> on devices.code = tmp_table.code);
Query OK, 584591 rows affected (2.93 sec)
Records: 584591 Duplicates: 0 Warnings: 0
3秒,完勝!!!
原因分析
內(nèi)連接的中間過程數(shù)據(jù)規(guī)模不是兩張表的笛卡爾乘積;而多表where查詢采用兩張表的笛卡爾積,在where的時候才過濾數(shù)據(jù)。