1 '''
2 Created on June 1, 2011
3
4 @author: John Cornwell
5 @contact: JohnWCornwellV@gmail.com
6 @summary: Used to extract Compustat data from a csv dump and convert into individual pickle files
7 '''
8
9 import numpy as np
10 import datetime as dt
11 import pickle as pkl
12 import qstkutil.utils as utils
13 import qstkutil.DataAccess as da
14 import os
15 import dircache
16 import time
17 import csv
18 import sys
19
20
22 '''
23 @summary Helper function to store files on disk: files from dictionary dData, set of paths lSets
24 @param dData: Dictionary of indexes to data label strings.
25 @param lSets: List of symbol sets, each corresponds to a directory, e.g. NYSE, NASDAQ.
26 @param lsOutPaths: List of path strings, same indexes as lSets.
27 '''
28 lKeys = dData.keys()
29
30 for key in lKeys:
31 for i, symSet in enumerate( lSets ):
32 if key in symSet:
33 sFilename = lsOutPaths[i] + key + '.pkl'
34 break
35
36 fOut = open( sFilename, 'wb' )
37 pkl.dump( dData[key], fOut, -1)
38 fOut.close()
39
40
42 '''
43 @summary Helper function to analyzes the csv data to determine which columns are float values, and generate the set of valid labels.
44 '''
45 try:
46 rootdir = os.environ['QSDATA']
47 except KeyError:
48 print "Please be sure to set the value for QSDATA in config.sh or local.sh\n"
49
50 ''' Create lists of input and output paths '''
51 fFile = ( rootdir + "/Raw/Compustat/Compustat.csv")
52
53 spamReader = csv.reader(open(fFile, 'rb'), delimiter=',')
54
55 ''' Take first row as the labels '''
56 for row in spamReader:
57 lsLabels = row
58 break
59
60 used = [0] * len(lsLabels)
61 badSet = set()
62
63 ''' find non-float valued entities '''
64 for i, row in enumerate(spamReader):
65 for j, elem in enumerate(row):
66 if used[j] == 1:
67 continue
68
69 if elem == '':
70 elem = 0.0
71
72 try:
73 float(elem)
74 except:
75 badSet.add( lsLabels[j] )
76 used[j] = 1
77
78 if( i % 10000 == 0 ):
79 print (i / 1378625.0)*100, '%'
80
81 print 'Bad (non float) labels:'
82 print badSet
83
84 for i,label in enumerate(lsLabels):
85 if label in badSet:
86 del lsLabels[i]
87
88 print '\n\nGood (float) labels:'
89 print lsLabels
90
91 return
92
93
95 '''
96 @summary: Converts a Compustat CSV file to pickle files of numpy arrays.
97 '''
98
99 print "Starting..."+ str(time.strftime("%H:%M:%S"))
100
101 ''' Write every so often to save memory, 20k lines is usually < .5GB '''
102 lSaveMem = 20000
103
104 try:
105 rootdir = os.environ['QSDATA']
106 except KeyError:
107 print "Please be sure to set the value for QSDATA in config.sh or local.sh\n"
108
109 ''' Create lists of input and output paths '''
110 fFile = ( rootdir + "/Raw/Compustat/Compustat.csv")
111
112 listOfOutputPaths= []
113 listOfOutputPaths.append(rootdir + "/Processed/Compustat/US/AMEX/")
114 listOfOutputPaths.append(rootdir + "/Processed/Compustat/US/NASDAQ/")
115 listOfOutputPaths.append(rootdir + "/Processed/Compustat/US/NYSE/")
116
117
118 for path in listOfOutputPaths:
119 if not (os.access(path, os.F_OK)):
120
121 os.makedirs(path)
122
123
124
125 utils.clean_paths (listOfOutputPaths)
126
127 spamReader = csv.reader(open(fFile, 'rb'), delimiter=',')
128 ''' 1378625 rows typical '''
129
130 ''' Take first row as the labels '''
131 for row in spamReader:
132 lsLabels = row
133 break
134
135 ''' Generated from _Analyze() '''
136 lsBadLabels = set(['LOC', 'ADD4', 'ADD3', 'ADD2', 'ADD1', 'ACCTCHGQ', 'WEBURL', 'IDBFLAG', 'popsrc', 'DATACQTR', 'conm', 'COSTAT', 'FINALQ', 'fdateq', 'FAX', 'RP', 'PRIROW', 'dldte', 'indfmt', 'SPCSRC', 'BUSDESC', 'ipodate', 'PHONE', 'CURCDQ', 'pdateq', 'DATAFQTR', 'PRICAN', 'EIN', 'datadate', 'tic', 'ADDZIP', 'CONML', 'consol', 'datafmt', 'cusip', 'BSPRQ', 'OGMQ', 'COMPSTQ', 'COUNTY', 'STATE', 'CURNCDQ', 'CITY', 'rdq', 'apdedateq', 'STALTQ', 'INCORP'])
137
138 ''' get list of stocks in 3 US indexes '''
139 Access = da.DataAccess( 'Norgate' )
140 setNyse = set( Access.get_symbols_in_sublist("/US/NYSE") )
141 setNasdaq = set( Access.get_symbols_in_sublist("/US/NASDAQ") )
142 setAmex = set( Access.get_symbols_in_sublist("/US/AMEX") )
143
144 ''' If stock appears in more than one index, remove to avoid ambiguity '''
145 print 'Ignoring duplicate stocks:',
146 dup1 = setNyse.intersection( setNasdaq.union(setAmex))
147 dup2 = setNasdaq.intersection( setAmex )
148 print dup1.union(dup2)
149
150 setNyse = setNyse - dup1.union(dup2)
151 setAmex = setAmex - dup1.union(dup2)
152 setNasdaq = setNasdaq - dup1.union(dup2)
153
154 ''' Note the two lists below must be in the same order '''
155 lsOutPaths = []
156 lsOutPaths.append(rootdir + "/Processed/Compustat/US/AMEX/")
157 lsOutPaths.append(rootdir + "/Processed/Compustat/US/NASDAQ/")
158 lsOutPaths.append(rootdir + "/Processed/Compustat/US/NYSE/")
159
160 lSets = [setAmex, setNasdaq, setNyse]
161
162
163 for path in lsOutPaths:
164 if not (os.access(path, os.F_OK)):
165
166 os.makedirs(path)
167
168
169
170 utils.clean_paths (lsOutPaths)
171
172 lDateCol = 0
173 llUseCols = []
174
175
176 ''' We have first row (the labels), loop through saving good label indicies '''
177 for j, sElem in enumerate(lsLabels):
178 if( sElem not in lsBadLabels ):
179 llUseCols.append(j)
180
181 ''' Keep track of ticker column and date, specially handled later '''
182 if( sElem == 'datadate' ):
183 lDateCol = j
184 if( sElem == 'tic' ):
185 lTicCol = j
186
187
188 ''' Dict of ticker->numpy array mapping '''
189 dData = dict()
190 print ''
191
192
193 ''' Main loop, iterate over the rows in the csv file '''
194 for j, row in enumerate(spamReader):
195 lsLabels = row
196 sTic = row[lTicCol]
197
198 ''' Find out what index this belongs to '''
199 lIndex = -1
200 for i, symSet in enumerate( lSets ):
201 if sTic in symSet:
202 lIndex = i
203 break
204 if lIndex == -1:
205 continue
206
207 sFilename = lsOutPaths[lIndex] + sTic + '.pkl'
208
209 ''' If the file exists (temporary memory saving measure), read it in and delete file from disk '''
210 if( os.path.isfile(sFilename)):
211 if dData.has_key(sTic):
212 print 'File should not be both on disk and in dict'
213 sys.exit("FAILURE")
214
215 fIn = open( sFilename, 'rb' )
216 dData[sTic] = pkl.load( fIn )
217 fIn.close()
218 os.remove( sFilename )
219
220 fDate = float( dt.datetime.strptime( row[lDateCol], "%m/%d/%Y").strftime("%Y%m%d") )
221
222 ''' convert blanks to nans '''
223 for i in llUseCols:
224 if row[i] == '':
225 row[i] = 'nan'
226
227 ''' Add row if data exists, if not, create new array '''
228 if dData.has_key(sTic):
229 dData[sTic] = np.vstack( (dData[sTic], np.array([fDate] + [row[i] for i in llUseCols], dtype=np.float)) )
230 else:
231 dData[sTic]= np.array( [fDate] + [row[i] for i in llUseCols], dtype=np.float )
232
233 if( (j+1) % 1000 == 0):
234 fDone = (j / 1378625.0) * 100
235 print '\rApprox %.2lf%%'%((j / 1378625.0) * 100),
236
237 if( (j+1) % lSaveMem == 0):
238 ''' Write all the pickle files we currently have '''
239
240 print '\nWriting %i lines to pickle files to save memory\n'%(lSaveMem)
241 _dumpFiles( dData, lSets, lsOutPaths)
242 ''' Remember to delete! '''
243 del dData
244 dData = dict()
245
246
247
248
249 print ''
250 print 'Writing final pickle files\n'
251 _dumpFiles( dData, lSets, lsOutPaths)
252 del dData
253
254 print "Finished..."+ str(time.strftime("%H:%M:%S"))
255
256 return
257
258 if __name__ == '__main__':
259
260
261 convert()
262