废话少说先上代码🉑
我自己写的代码
def get_stock_list_from_local():
"""
从本地获取数据库中的所有股票列表
:return: stock_list
:rtype: pandas.DataFrame
"""
# 初始化DataFrame对象用于存储股票代码信息
column_name = "stock_code"
stock_list = pd.DataFrame(columns=[column_name])
# 获取file_root路径
file_root = "E:\\Code\\project_quantEx\\RFTrader\\data\\price"
# 获取路径下的所有文件名
files = os.listdir(file_root)
for file in files:
if file[0] != "_":
code_baostock = convert_stock_code_2baostock(file[:11])
stock_list = pd.concat([stock_list, pd.DataFrame([code_baostock], columns=[column_name])],
ignore_index=True)
return stock_list
chatGPT给出修改后的代码
def get_stock_list_from_local():
"""
从本地获取数据库中的所有股票列表
:return: stock_list
:rtype: pandas.DataFrame
"""
# 初始化DataFrame对象用于存储股票代码信息
column_name = "stock_code"
stock_list = pd.DataFrame()
# 获取file_root路径
file_root = "E:\\Code\\project_quantEx\\RFTrader\\data\\price"
# 获取路径下所有csv文件
files = glob.glob(file_root + "/*.csv")
for file in files:
try:
code_baostock = convert_stock_code_2baostock(file[:11])
if not stock_list.isin([code_baostock]).any().stock_code:
stock_list.loc[len(stock_list)] = code_baostock
except Exception as e:
print(f'Error occurred while processing {file} : {e}')
return stock_list
chatGPT给出的修改意见
- You could use the
glob
library to get a list of files in the directory, this way you can filter files based on the extension or a pattern, it will be more efficient than theos.listdir()
function. - You could use a try-except block around the
convert_stock_code_2baostock(file[:11])
function call, to handle any errors that might occur if the file name is not in the expected format, or if the conversion function is not working correctly. - You could add a check to make sure that the file is a CSV file, before trying to convert the file name to the baostock code format.
- You can use
isin()
function to filter the stock codes that are already in the stock_list dataframe, so you can skip reading the same file multiple times. - you can use
pd.DataFrame()
instead ofpd.DataFrame(columns=[column_name])
to initialize the dataframe, this way you can use.loc
to add data to the dataframe instead of usingpd.concat()
. - You could consider adding some logging to the function to log any errors or important events that occur during execution.
- If you have a large amount of data, you can consider using multi-threading or multiprocessing to speed up the process of reading and processing the files.
- You can use
pd.to_csv()
method to save the dataframe in a csv format.
逐条分析
可以看出chatGPT总共给出了8条修改意见😲,我都震惊了,仔细看来,每一条基本上都有一定的道理,感觉就像是一个非常懂行的老Python工程师在帮我修改代码一样,我感觉到如果不断用这种方式对代码进行学习,我的Python代码能力一定能够得到非常大的增长😆✔。
使用glob
库对目录中的文件进行读取,并且加入check是否文件是csv格式的功能
这里,chatGPT给出的建议是使用glob
中的方法对文件进行读取,使用这个的好处是,可以根据文件名进行筛选,可以不用在后面的代码中对读取到的字符串进行筛选。这里给出我写的和chatGPT给出的代码片段。
# 我写的
files = os.listdir(file_root)
# chatGPT写的
files = glob.glob(file_root + "/*.csv")
可以看到,这里的glob
方法直接对读取文件的形式进行了筛选,我在代码后面还使用了if分支用于判断文件名字符串是不是我想要的文件名,因为一般Python Package中一般会有一个额外的文件叫做__init__.py
,这个文件是起到什么作用的呢?
![__init__.py文件的作用](/2023/01/18/gen-zhao-chatgpt-xue-xi-python/80998c9c5f379496231cfd251555bf62b02aae3edf230e2e75eace61caad3531.png)
从图中我们可以看到,chatGPT说这个文件有三个作用:
- 作为一种标识用于表明本文件夹是一个Python包,当Python解释器遇到这种文件夹时,会认为这是一个包,并且将会在这个包中搜寻需要引入代码的模块。简单来说,就是一种包的标识符,告诉Python解释器,我是个包,快来分析我!😄
- 用来定义包的初始化代码,这种初始化代码将会在包第一次被引入的时候执行。在你想要给定一些全局变量或者进行一些初始化操作的时候非常有用。
- 同时这个文件也可以用来定义包的全局API。这个我暂时还不太懂,可以之后再看看。😢
说完了这个文件,我们再说回我们的代码,可以看到这里chatGPT给出了一个方法,帮助我们一步解决读取数据、筛选文件的操作,提升了效率,至于这个包读取文件的速度是不是更快,还需要进一步验证,这里我们可以问问chatGPT。
![chatGPT给出的os.listdir()与glob.glob()的区别](/2023/01/18/gen-zhao-chatgpt-xue-xi-python/77ac7b8d89154e1f55c709fb5d09c6160df50fea18c685278c7e2a203dc46455.png)
总结下来呢就是,在性能上,如果没有筛选要求的,使用os.listdir(file_root)
是最好的,因为这个更快一些,但是如果你有筛选需求的话,glob.glob(file_root + "/*.csv")
是更好的选择。
所以,这里,我们学到了一个筛选文件名获取文件的更好方式,就是使用glob.glob()
方法,其内部参数使用字符串的形式,内部加入通配符来进行匹配。
使用try-except模块来防止出现异常情况
这里chatGPT推荐我们使用try-except模块来对异常进行处理,具体的代码区别如下所示。
# 我写的
if file[0] != "_":
code_baostock = convert_stock_code_2baostock(file[:11])
stock_list = pd.concat([stock_list, pd.DataFrame([code_baostock], columns=[column_name])],
ignore_index=True)
# chatGPT写的
try:
code_baostock = convert_stock_code_2baostock(file[:11])
if not stock_list.isin([code_baostock]).any().stock_code:
stock_list.loc[len(stock_list)] = code_baostock
except Exception as e:
print(f'Error occurred while processing {file} : {e}')
那么为什么要使用try-except模块呢?我们可以继续来问问chatGPT。😄
![chatGPT解释为什么要使用try-except模块](/2023/01/18/gen-zhao-chatgpt-xue-xi-python/ce072fefca11b1c79f44e44573356fd75c7f1acf32c6579f74b96b4d2c5f67ef.png)
总结下来呢,就是两点,一个是使用抛出异常的操作可以方便你deBug的时候迅速定位错误的位置,方便你deBug。这一点我深有体会,我在deBug代码的时候,因为我自己设计的代码没有try-except操作,所以我通常会定位错位置。另一个重要原因是这会帮助你防止程序出错之后立马就停止运行了,帮助程序把错误的代码分离出来。总的来说呢,使用try-except模块是一个非常好的代码习惯。
使用isin()方法防止出现重复的stock_code问题
这里chatGPT推荐我们加入额外的判断语句,来判断我们要添加进stock_list的数据是不是会与已有数据产生重复,如果不重复,则执行在stock_list尾部添加股票代码的操作,这里贴出代码。
# 我写的
stock_list = pd.concat([stock_list, pd.DataFrame([code_baostock], columns=[column_name])],
ignore_index=True)
# chatGPT写的
if not stock_list.isin([code_baostock]).any().stock_code:
stock_list.loc[len(stock_list)] = code_baostock
可以看出,chatGPT加入了额外的分支,这是用于检查我们的code_baostock的code是否在我们的stock_list里的,具体可以看下面chatGPT给出的说明。
![chatGPT说明any()以及isin()方法的作用](/2023/01/18/gen-zhao-chatgpt-xue-xi-python/1eb272b7b87a66bf7156526d04e1674d08d0f8220f4b5fdbe958e3cb84db62ac.png)
总的来说呢,就是stock_list.isin([code_baostock])这个其实是生成了一个dataFrame
。我以前一直以为这个语句是产生一个值…😓,这里我也是看了好半天才看明白的。所以这个dataFrame
没有办法直接进行判断的,因为我们的逻辑是只要有一个符合,我们就不执行下面的操作,所以这里使用了any()
方法,是为了提取dataFrame
中的boolean
信息,进行逻辑判断,最后再选取对应的列.stock_code
,产生最终的布尔值。
使用.loc()
方法进行DataFrame数据的添加
这里chatGPT推荐我们使用.loc()
对DataFrame数据进行追加,下面是代码片段。
# 我写的
stock_list = pd.concat([stock_list, pd.DataFrame([code_baostock], columns=[column_name])],
ignore_index=True)
# chatGPT写的
stock_list.loc[len(stock_list)] = code_baostock
可以看到这里的逻辑是再最后一行添加code_baostock数据,这一部分值得好好研究,因为loc的用法我其实还不是特别清晰,之前使用这个方法的时候,经常会出现各种异常,所以这里应该要解决一下。
![picture 6](/2023/01/18/gen-zhao-chatgpt-xue-xi-python/902c4c7d04dda5a4761263db6fb65ede05c3ed4754f8460e58b335725643cbb1.png)
这里可以看到,官方源码中的注释给出了具体的解释说明,我总结成如下几点
df.loc['viper'] # 返回一行数据,数据格式为Series,注意这里数据为原先DataFrame中的一行数据,这里的入参也是行的标签
df.loc[['viper', 'sidewinder']] # 返回两行数据,数据格式为DataFrame,入参是两行标签组成的Series
df.loc['cobra', 'shield'] # 返回行标签为'cobra',列标签为'shield'的数据
一个重要的观察是,loc一般需要两个参数,才能指定DataFrame中的具体位置的数据,否则返回的肯定是数据阵列;并且loc方法的入参先输入的一定是行标签,所以需要注意的是不要搞错了。最后需要注意的是,loc方法数输入模式是[]
不要搞错成()
了。
最后三条(有了可以锦上添花,但没有实现)
- 可以再执行的过程中添加日志功能?这个好像不太需要,因为我们的方法非常简单,没有特别的对日志的需求。当然,你要说一定要添加的话,可以添加日志说明对csv文件进行更改的操作之类的,这个可以先不着急实现。
- 如果数据量大的话,可以加入多线程操作,来加快文件的调取。我觉得这个是有必要的,增加速度也是一个考虑之一,但是我之后可能考虑采用关系型数据库MySQL或者非关系型数据库MongoDB,目前csv文件存储数据的方案只是一个暂时的方案。
- 使用pd.to_csv()方法对数据进行存放。目前暂时没有这个需求❌