数据分析项目案例
第一步,我们先导入tushare:
#导入tushareimporttushareastspro=ts.pro_api沪深港通标的很多投资者倾向于投资沪深港通的标的,因为这些股票得到了更多国际投资者的青睐。在tushare中,我们通过stock_basic接口获取股票列表,其中is_hs参数用于筛选沪深港通标的列表,具体规则如下:
沪股通标的:is_hs="H";深股通标的:is_hs="S";其他:is_hs="N".#沪股通股票列表pro.stock_basic.head
#深股通股票列表pro.stock_basic.head
#非沪深港通股票列表pro.stock_basic.head
上市状态在stock_basic接口中,我们可以用list_status参数筛选不同上市状态的股票。
上市状态:list_status="L";退市状态:list_status="D";暂停上市状态:list_status="P".#正常上市股票pro.stock_basic.head
#已退市股票pro.stock_basic.head
交易所我们还可以看分别在上交所和深交所上市的股票列表,只需要在stock_basic中传入exchange参数即可。
上交所:exchange="SSE";深交所:exchange="SZSE:.#上交所股票pro.stock_basic.head
行业除了参数中提供的筛选维度,我们还可以获取所有股票列表数据之后,用一些字段进行筛选。比如说行业:
df=pro.stock_basicdf.query.head
上市板块我们还可以按照上市板块来挑选股票,这一点也很有用,有些投资人侧重于某个板块的股票,比如中小板、创业板等。
df=pro.stock_basicdf.query.head
地区我们还能挑选属于某个特定地区的股票。当我们需要寻找地域联动效应或者某个地区爆出了某个利好时,这个筛选方法会很有帮助。
df=pro.stock_basicdf.query.head
上市公司基本信息在获取了股票列表之后,我们可能还想要了解一下它们的基本信息,比如注册资本、所在城市、经营范围等,这时我们可以使用stock_company接口。
接口默认仅提供了部分字段,需要更多字段的可以用fields参数来指定所需要的列。
获取深圳本地股票我们来看一个案例,比如说之前有国家帮助中小企业解决质押风险,深圳国资委率先响应,然后我们想看一下可能受益的股票有哪些,因此我们想要先找到深圳的本地股列表,并且查看他们的主营业务、员工人数。
#获取深圳本地上市公司股票列表importpandasaspd#获取上市公司信息fields=df=pro.stock_companydf.query.head
获取员工数超过十万的上市公司股票可能我们还想通过员工数来筛选出大型的企业,这时我们可以通过employees字段完成筛选。
是不是很简单?不过tushare的强大远不止于此————————————————
从Tushare获取数据写入Mysql导入Excel后续,我会加入一些复杂的功能,比如定时更新、logging日志等等。
"""
接口:stock_basic
描述:获取基础信息数据,包括股票代码、名称、上市日期、退市日期等
更新频率:one week or long
"""
import tushare as ts
import pymysql
import pandas as pd
# 连接数据库,传参数的,返回df
def get_mysql_data(sql):
db = pymysql.connect(host="localhost",
user="root",
password="root",
db="STOCK",
charset="utf8")
cur = db.cursor()
cur.execute(sql)
data = cur.fetchall()
columnDes = cur.description # 获取连接对象的描述信息
columnNames = [columnDes[i][0] for i in range(len(columnDes))]
df = pd.DataFrame([list(i) for i in data], columns=columnNames)
cur.close()
db.commit()
db.close()
return df
# 将已完成的数据表,导出到excel中
def to_excel():
sql = "select * from stock_basic"
df = get_mysql_data(sql)
df.to_excel(r"D: .STOCKExcel_Filestock_basic.xlsx",index=False)
print("<=== !!!导入成功!!! =====>")
# 返回相应接口的数据
def get_data():
# 只需要在第一次或者token失效后调用
ts.set_token("your token")
pro = ts.pro_api()
# 查询当前所有正常上市交易的股票列表
data = pro.stock_basic(exchange="", list_status="L",
fields="ts_code,symbol,name,area,industry,market,exchange,list_status,list_date,is_hs")
return data
# 连接数据库,清洗掉原表,并重新写入数据
def write_data():
db = pymysql.connect(host="localhost",
user="root",
password="root",
db="STOCK",
charset="utf8")
cur = db.cursor()
cur.execute("truncate TABLE stock_basic")
para = []
data = get_data() # 获取接口的数据
print("<=== !!!获取成功!!! =====> 共有", data.shape[0], "个股票数据")
print(data.head())
for i in range(0, len(data)):
para.append((i+1, data.loc[i, "ts_code"], data.loc[i, "symbol"], data.loc[i, "name"], data.loc[i, "area"],
data.loc[i, "industry"], data.loc[i, "market"], data.loc[i, "exchange"],
data.loc[i, "list_status"],
data.loc[i, "list_date"], data.loc[i, "is_hs"]))
sql = """
insert into stock_basic(id,ts_code,symbol,name,area,industry,market,exchange,list_status,list_date,is_hs) values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
"""
cur.executemany(sql, para)
# 查询写入的条数
sql_query = "SELECT ts_code from stock_basic"
cur.execute(sql_query)
sql_data = cur.fetchall()
sql_num = len(sql_data)
print("Mysql数据库写入成功,共写入", sql_num, "个股票数据")
cur.close()
db.commit()
db.close()
if __name__ == "__main__":
write_data() # 清除到原表,重新写入
to_excel() # 导出到表格中
股票分析
需求:
使用tushare包获取某股票的历史行情数据。
输出该股票所有收盘比开盘上涨3%以上的日期。
输出该股票所有开盘比前日收盘跌幅超过2%的日期。
#需求假如我从2010年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?
需求使用tushare包获取某股票的历史行情数据。
#获取行情
df=ts.get_k_data#保存到本地
df.to_csv#读取本地csv文件数据
df=pd.read_csv#删除Unnamed:0这一列,将date列转为时间类型,并设置为index列
df["date"]=pd.to_datetime
df.set_indexprint)#查看整个数据集合中各个数据类型
需求输出该股票所有收盘比开盘上涨3%以上的日期。
#/开盘>0.03返回值为boolean值,将boolean作为行索引来使用
#在分析的过程中如果产生了boolean值则下一步马上将布尔值作为源数据的行索引#如果布尔值作为df的行索引,则可以取出true对应的行数据,忽略false对应的行数据#print/df["open"]>0.0#获取了True对应的行数据
print/df["open"]>0.03].index)
需求输出该股票所有开盘比前日收盘跌幅超过2%的日期。
#/昨日收盘价<-0.02#print)#使df["close"]列整体下移一位
print)/df["close"].shift<-0.02].index)
#需求假如我从2010年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?
-分析:
-时间节点:2010-2020
-一手股票:100支股票
-买:
-一个完整的年需要买入1200支股票
-卖:
-一个完整的年需要卖出1200支股票
-买卖股票的单价:
-开盘价
#买股票:找每个月的第一个交易日对应的行数据==》每月的第一行数据#根据月份从原始数据中提取指定的数据#每月第一个交易日对应的行数据
new_df=df["2010-01-01":]
mairu=new_df.resample.first["open"].sum*100#数据的重新取样,取出每月的第一支股票
maichu=new_df.resample.last["open"][:-1].sum*1200#取出每年最后一个交易日的收盘价
yu=new_df["close"][-1]*600#剩余股票价值
#print.first["open"]*100)#print.last["close"][:-1]*100)
双均线策略
需求计算该股票历史数据的5日均线和60日均线
-什么是均线?
-对于每一个交易日,都可以计算出前N天的移动平均值,然后把这些移动平均值连起来,成为一条线,就叫做N日移动平均线。移动平均线常用线有5天、10天、30天、60天、120天和240天的指标。
-5天和10天的是短线操作的参照指标,称做日均线指标;
-30天和60天的是中期均线指标,称做季均线指标;
-120天和240天的是长期均线指标,称做年均线指标。
-均线计算方法:MA=
df=ts.get_k_data
df["date"]=pd.to_datetime
ma5=df["close"].rolling.mean#5日均线
ma30=df["close"].rolling.mean#30日均线
plt.rcParams["font.sans-serif"]=["SimHei"]#用来正常显示中文标签
plt.rcParams["axes.unicode_minus"]=False#用来正常显示负号
plt.plot
plt.plot
plt.show
需求
-分析输出所有金叉日期和死叉日期
-股票分析技术中的金叉和死叉,可以简单解释为:
-分析指标中的两根线,一根为短时间内的指标线,另一根为较长时间的指标线。
-如果短时间的指标线方向拐头向上,并且穿过了较长时间的指标线,这种状态叫“金叉”
-如果短时间的指标线方向拐头向下,并且穿过了较长时间的指标线,这种状态叫“死叉”
-一般情况下,出现金叉后,操作趋向买入;死叉则趋向卖出。金叉和死叉只是分析指标之要和其他很多指标配合使用,才能增加操作的准确性。
#分析输出所有金叉日期和死叉日期
df["date"]=pd.to_datetime
ma5=df["close"].rolling.mean
ma30=df["close"].rolling.mean
s5=ma5[30:] s30=ma5[30:]>ma30[30:] df=df[30:] down=s5&s30.shiftprint#死叉 up=~)print#金叉 需求如果我从假如我从2010年1月1日开始,初始资金为100000元,金叉尽量买入,死叉全部卖出,则到今天为止,我的炒股收益率如何? df["date"]=pd.to_datetime ma5=df["close"].rolling.mean ma30=df["close"].rolling.mean s5=ma5[30:] s30=ma5[30:]>ma30[30:] df=df[30:] up=~)#金叉 down=s5&s30.shift#死叉 up_code=Series) s=s.sort_index["2010-01-01"] first_monry=100000#本金,不变 money=first_monry#可变的,买股票话的钱和卖股票收入的钱都从该变量中进行操作 hold=0#持有股票的数量 foriinrange):#i表示的s这个Series中的隐式索引 #i=0=1 ifs[i]==1:#金叉的时间 #基于100000的本金尽可能多的去买入股票 #获取股票的单价 time=s.index[i]#金叉的时间 p=df.loc[time]["open"]#股票的单价 hand_count=money//#使用100000最多买入多少手股票 hold=hand_count*100money-=#将买股票话的钱从money中减去 else:#将买入的股票卖出去 #找出卖出股票的单价 p_death=df.loc[death_time]["open"]#卖股票的单价 money+=#卖出的股票收入加入到money hold=0#如何判定最后一天为金叉还是死叉 last_monry=hold*df["close"][-1]#剩余股票的价值 #总收益 人口分析项目 -需求:-导入文件,查看原始数据-将人口数据和各州简称数据进行合并-将合并的数据中重复的abbreviation列进行删除-查看存在缺失数据的列-找到有哪些state/region使得state的值为NaN,进行去重操作-为找到的这些state/region的state项补上正确的值,从而去除掉state这一列的所有NaN-合并各州面积数据areas-我们会发现area这一列有缺失数据,找出是哪些行-去除含有缺失数据的行-找出2010年的全民人口数据-计算各州的人口密度-排序,并找出人口密度最高的州 #导入文件,查看原始数据 abb=pd.read_csv#stateabbreviation area=pd.read_csv#state州的全称,area州的面积 pop=pd.read_csv#state/region简称,ages年龄,year时间,population人口数量 #将人口数据和各州简称数据进行合并 abb_pop#将合并的数据中重复的abbreviation列进行删除 abb_pop2#查看存在缺失数据的列 abb_popisnull.any#找到有哪些state/region使得state的值为NaN,进行去重操作 abb_pop.loc[abb_pop["state"].isnull]["state/region"].unique#为找到的这些state/region的state项补上正确的值,从而去除掉state这一列的所有NaN#合并各州面积数据areas#我们会发现area这一列有缺失数据,找出是哪些行#去除含有缺失数据的行#找出2010年的全民人口数据#计算各州的人口密度#排序,并找出人口密度最高的州 消费记录分析 第一部分 第一部分:数据类型处理-数据加载-字段含义:-user_id:用户ID-order_dt:购买日期-order_product:购买产品的数量-order_amount:购买金额-观察数据-查看数据的数据类型-数据中是否存储在缺失值-将order_dt转换成时间类型-查看数据的统计描述-计算所有用户购买商品的平均数量-计算所有用户购买商品的平均花费-在源数据中添加一列表示月份:astype 代码实现 df=pd.read_csv df#查看数据的数据类型 df.info#数据中是否存储在缺失值#df.isnull.any df.notnull.all#将order_amount转换成时间类型 df["order_dt"]=pd.to_datetime df#查看数据的统计描述 df.describe#在源数据中添加一列表示月份:astype df["month"]=df["order_dt"].astype 第二部分 第二部分:按月数据分析-用户每月花费的总金额-绘制曲线展示-所有用户每月的产品购买量-所有用户每月的消费总次数-统计每月的消费人数 代码实现 #用户每月花费的总金额 df.groupby["order_amount"].sum#绘制曲线展示 plt.plot["order_amount"].sum) df.groupby["order_amount"].sum.plot#所有用户每月的产品购买量 df.groupby["order_product"].sum#所有用户每月的消费总次数 df.groupby["user_id"].count#统计每月的消费人数 df.groupby["user_id"].nunique 第三部分 第三部分:用户个体消费数据分析-用户消费总金额和消费总次数的统计描述-用户消费金额和消费产品数量的散点-各个用户消费总金额的直方分布-各个用户消费的总数量的直方分布 代码实现 #用户消费总金额和消费总次数的统计描述 df.groupby["order_amount"].sum df.groupby["order_product"].count#用户消费金额和消费产品数量的散点 plt.scatter#各个用户消费总金额的直方分布 df.groupby.sum.query["order_amount"].hist#各个用户消费的总数量的直方分布 df.groupby.sum.query["order_product"].hist 第四部分 第四部分:用户消费行为分析-用户第一次消费的月份分布,和人数统计-绘制线形-用户最后一次消费的时间分布,和人数统计-绘制线形-新老客户的占比-消费一次为新用户-消费多次为老用户-分析出每一个用户的第一个消费和最后一次消费的时间-agg:对分组后的结果进行指定聚合-分析出新老客户的消费比例-用户分层-分析得出每个用户的总购买量和总消费金额and最近一次消费的时间的表格rfm-RFM模型设计-R表示客户最近一次交易时间的间隔。-/np.timedelta6去除days-F表示客户购买商品的总数量,F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃。-M表示客户交易的金额。M值越大,表示客户价值越高,反之则表示客户价值越低。-将R,F,M作用到rfm表中-根据价值分层,将用户分为:-重要价值客户-重要保持客户-重要挽留客户-重要发展客户-一般价值客户-一般保持客户-一般挽留客户-一般发展客户-使用已有的分层模型即可rfm_func 代码实现 df=pd.read_csv#转为时间格式 df["order_dt"]=pd.to_datetime#增加月份一列 df["month"]=df["order_dt"].astype#用户第一次消费的月份分布,和人数统计,绘制线形 df.groupby["month"].min.value_counts.plot#用户最后一次消费的时间分布,和人数统计,绘制线形 df.groupby["month"].max.value_counts.plot#新老客户的占比 new_old_user=df.groupby["order_dt"].agg val=.value_counts#新用户占比 val[True]/#老用户占比 val[False]/ 构建RFM数据表 #分析得出每个用户的总购买量和总消费金额and最近一次消费的时间的表格rfm rfm=df.pivot_table rfm#R表示客户最近一次交易时间的间隔 new_date=df["order_dt"].max#数据中最大时间,假设为当前时间 rfm["R"]=-["order_dt"].max-new_date)/np.timedelta64 rfm#F表示客户购买商品的总数量,F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃#M表示客户交易的金额。M值越大,表示客户价值越高,反之则表示客户价值越低#将R,F,M作用到rfm表中 #删除order_dt列 rfm.drop#对列进行重命名 rfm.columns=["M","F","R"] 用户分层 level=x.map dit={"111":"重要价值客户","011":"重要保持客户","101":"重要挽留客户","001":"重要发展客户","110":"一般价值客户","010":"一般保持客户","100":"一般挽留客户","000":"一般发展客户", rfm["level"]=rfm.apply).apply 第五部分:用户生命周期 第五部分:用户的生命周期-将用户划分为活跃用户和其他用户-统计每个用户每个月的消费次数-统计每个用户每个月是否消费,消费为1否则为0-知识点:DataFrame的apply和applymap的区别-applymap:返回df-将函数做用于DataFrame中的所有元素-apply:返回Series-apply将一个函数作用于DataFrame中的每个行或者列-将用户按照每一个月份分成:-unreg:观望用户-unactive:首月购买后,后序月份没有购买则在没有购买的月份中该用户的为非活跃用户-new:当前月就进行首次购买的用户在当前月为新用户-active:连续月份购买的用户在这些月中为活跃用户-return:购买之后间隔n月再次购买的第一个月份为该月份的回头客 代码实现 df=pd.read_csv df["order_dt"]=pd.to_datetime df["month"]=df["order_dt"].astype df#统计每个用户每个月的消费次数 month_sum=df.pivot_table.fillna#统计每个用户每个月是否消费,消费为1否则为0 month_sum=df.pivot_table.fillna 区分用户类别 #将df_purchase中的原始数据0和1修改为new,unactive......,返回新的df叫做df_purchase_new#固定算法 month_sum=df.pivot_table.fillna status=[]#某个用户每一个月的活跃度 foriinrange:#若本月没有消费 ifdata[i]==0:iflen>0:ifstatus[i-1]=="unreg": status.append#若本月消费 status.appendelse:ifstatus[i-1]=="unactive": status.appendelifstatus[i-1]=="unreg": pivoted_status_list=pivoted_status.values.tolist#生成新的数据表#new_start_info=DataFrame#生成新的数据表并更换回原index new_start_info=DataFrame -每月【不同活跃】用户的计数 -转置进行最终结果的查看 文章为作者独立观点,不代表股票交易接口观点