第一週:資料清洗+重置格式+寫入資料庫
實習第一週的主要工作是協助基本面研究員把資料庫源上的資料搞到我們公司的資料庫裡。(使用者名稱和密碼我都打碼處理啦)
下載安裝pycharm,然後配置環境用了好久。
首先,根據老闆提示,教育郵箱可以申請pycharm專業版的使用,(期限好像是一年?)具體教程可以在網上搜得到。專業版有很多好處,比如說view as dataframe這個好看的功能。
然後還遇到了很多問題,我現在都不太記得了。比如說開啟檔案一定要在專案裡開啟,要不然就沒辦法執行。然後在建立新專案的時候注意要勾選繼承全域性的什麼什麼,作用是通過pip安裝的那些庫都可以直接用,要不然還得重新在它那個虛擬環境裡新增。
然後就是pip安裝一個ipython,這樣配合ALT+SHIFT+E可以實現像R一樣,在控制檯執行選中的程式碼。
中間我巧妙地爬下了所有api的資料標籤寫進了excel裡,這樣可以簡單明瞭看出存在哪些坑,獲得了老闆的表揚qwq這一步操作程式碼存在test檔案裡,以下是主要片段。GetToken和GetData函式在再下面那個完整版程式碼裡有。
1 io = r'D:\dataapi.xlsx' 2 sdatetime = '2014-11-01' 3 edatetime = '2020-07-07' 4 file = pd.read_excel(io, sheet_name = 0) 5 token = GetToken(username, passwd) 6 count = 0 7 # ff = open("D:\data.txt", 'w+')8 labelist = [] 9 for i in range(0, len(file)): 10 label = [] 11 if file.loc[i, 'flag'] == 1: 12 api_name = file.loc[i, 'api_name'] 13 data = GetData(token, api_name, sdatetime, edatetime) 14 count = count + 1 15 # print(data.columns, file = ff) 16 # print(str(count) + " " + api_name)17 label = ' '.join(data.columns.tolist()) 18 labelist.append(label) 19 file['label'] = labelist 20 file.to_excel('dataapi_update.xls') 21 print("finish!")
獲取資料啥的都是現成的程式碼。我做的改動有:
1.通過excel獲取每一個數據的apiname,然後在for裡一個一個處理
2.去重,這個是因為資料網站上有特別坑的情況,比如同比、環比這種列會不止出現一次,這在寫入資料庫會出問題,所以需要加註腳。
python裡真是啥都有,這個功能都有現成的函式,被老闆發掘出來了,就是
data.columns = pd.io.parsers.ParserBase({'names': data.columns})._maybe_dedup_names(data.columns)
3.因為需要把時間做成索引,所以要把時間都改成時間戳的格式。而時間資料的標籤有很多種可能,比如年份、月份、日期啥的,因此我就肉眼從我預處理的那個excel裡過了一遍,列出了所有可能,然後每次迴圈判斷有其中哪一個。這裡又有一個特殊情況,就是有一個api(就只有它一個),同時出現了日期和時間兩個標籤,因此特殊處理了一下,把這兩列加到了一起。時間戳又是個很神奇的函式
data['index'] = pd.DatetimeIndex(pd.to_datetime(data['index']))
不需要指定格式(也可以指定),它能自動識別各種格式的時間,甚至包括季度(比如2019Q3)。
但這裡又有一個坑,資料裡有半年度資料,寫成了2019H1這種形式,這個它就沒法識別了。所以我取巧了一下,把H1改成Q1,H2改成Q3,這樣就可以直接識別成日期了。方法是:
data['index'] = data['index'].apply(lambda x: x.replace('H2', 'Q3') if 'H2' in x else x)
用了apply函式+lambda表示式。感覺也很神奇。
4.最後一個地方是把其他資料格式從文字改成浮點數格式。首先原資料的千位上有逗號,要先replace掉。然後呢,python裡似乎不像C有atof這種直接把文字轉換成浮點數,轉換錯誤便返回0的智慧函式,直接用float強制轉換的話,如果轉換不了會丟擲異常。因此我們就手寫了一個Myfloat作為轉換函式。
5.中間經常出現忘了寫inplace = True而導致的錯誤,這句話是讓操作應用於當前的dataframe。
1 import pandas as pd 2 import requests 3 import sqlalchemy 4 import xlwt 5 6 username = '135********' 7 passwd = '**********' 8 auth_url = 'https://platform.smm.cn/usercenter/auth' # for all 9 data_url = 'https://dataapi.smm.cn/GetData/' # 中文版本 10 # data_url = 'https://dataapi.smm.cn/GetDataEn/' # english edition 11 con = sqlalchemy.create_engine('mysql+pymysql://root:[email protected]/futures?charset=utf8', \ 12 pool_recycle=-1, pool_pre_ping=True) 13 14 def GetToken(username, passwd): 15 form_data = { 16 'user_name': username, 17 'password': passwd, # after md5 18 'source': 'dataapi' 19 } 20 21 resp = requests.post(url=auth_url, data=form_data) 22 data = resp.json().get('data') 23 token = data.get('token') 24 return token 25 26 27 def GetData(token, api_name, sdatetime, edatetime): 28 form_data = { 29 'token': token, 30 'sdatetime': sdatetime, 31 'edatetime': edatetime 32 } 33 34 reqUrl = data_url + api_name 35 resp = requests.post(reqUrl, data=form_data) 36 data = None 37 code = resp.json().get('Code') 38 if code == 200: 39 data = resp.json().get('Data').get('Content') # content 40 col = [dic['Name'] for dic in resp.json().get('Data').get('Field')] 41 data = pd.DataFrame(data, columns=col) 42 elif code == 400: 43 print('錯誤:無效請求!') 44 elif code == 401: 45 print('錯誤:未認證!') 46 elif code == 403: 47 print('錯誤:拒絕請求!') 48 elif code == 404: 49 print('錯誤:頁面丟失!') 50 elif code == 422: 51 print('錯誤:無法傳遞的實體!') 52 elif code == 500: 53 print('錯誤:伺服器內部錯誤!') 54 return data 55 56 def myfloat(string): 57 try: 58 return float(string) 59 except ValueError: 60 return string 61 62 datelist = ['日期', '年度', '月份', '年份', '季度', '時間', 'Date', '起始日期', '調研時間'] 63 # 以下記得要改 64 io = r'D:\dataapi.xlsx' 65 sdatetime = '2014-11-01' 66 edatetime = '2020-07-07' 67 # 以上記得要改 68 file = pd.read_excel(io, sheet_name = 0) 69 token = GetToken(username, passwd) 70 count = 0 71 for i in range(0, len(file)): 72 if file.loc[i, 'flag'] == 1: 73 api_name = file.loc[i, 'api_name'] 74 data = GetData(token, api_name, sdatetime, edatetime) 75 #標籤去重 76 ''' 77 試圖手動去重,但是並沒有寫完 78 to_unique_list = ['a','b','c','d','a','c'] 79 dict_count = {} 80 for word in to_unique_list: # 清零 81 dict_count[word] = 0 82 for word in to_unique_list: 83 dict_count[word] = dict_count[word]+1 84 for i in range(len(to_unique_list), -1): # 倒序遍歷 85 ''' 86 # 處理重複標籤 87 data.columns = pd.io.parsers.ParserBase({'names': data.columns})._maybe_dedup_names(data.columns) 88 # 把日期格式改成時間戳 89 for label in datelist: 90 if label in data.columns: 91 if label == '日期' and '時間' in data.columns: # 特判既有時間又有日期的情況 92 data['index'] = data['日期']+' '+data['時間'] 93 # data.drop(['時間'], axis = 1, inplace = True) 94 else: 95 data['index'] = data[label] 96 # data.rename(columns={label: 'index'}, inplace = True) 97 break 98 # 處理半年度日期 99 if 'H' in data.loc[1, 'index']: 100 data['index'] = data['index'].apply(lambda x: x.replace('H1', 'Q1') if 'H1' in x else x) 101 data['index'] = data['index'].apply(lambda x: x.replace('H2', 'Q3') if 'H2' in x else x) 102 103 # 改成時間戳並排序 104 data['index'] = pd.DatetimeIndex(pd.to_datetime(data['index'])) 105 data.set_index("index", inplace=True) 106 # df.drop(['單位', '最高價', '最低價'], axis=1, inplace=True) 107 data.sort_index(inplace=True) 108 109 # 改剩下的數字格式 110 for colname in data.columns: 111 data[colname] = data[colname].apply(lambda x: myfloat(x.replace(',', ''))) 112 print(data) 113 114 # print(data.columns, file = ff) 115 # print(str(count) + " " + api_name)
通過這個小專案呢,我熟悉了pandas裡最基本的資料結構Dataframe,並且知道了一些簡單(和高階一點)的操作。感覺很有收穫!