2020-07-12题目五

面试题


根据所需结果的格式,观察一下数据。



可以发现,日期格式和所需的日期格式不一致、appname里不止相机一种app。所以估计之后需要用date_format修改日期格式,需要用where筛选出相机有关的数据。同时,因为所需结果都和每日有关,应该是要用group by按日期分组。要计算不同时间的留存用户数,说明一行数据里要有用户当天日期,还有同一用户之后使用相机的日期。所以考虑要根据uid相等进行自连接,这样一行能得到两个日期,这样才能做比较。

先筛选有关相机的条目。因为同一用户一天多次行为只算一次,所以group by进行去重处理减少数据量。同时对日期格式做处理。

select uid, date_format(dayno, '%Y%m%d') as day1
from act_user_info_mysql
where app_name = '相机'
group by uid, dayno;

因为一行要有同个用户(相同uid)的当日打开相机的日期和之后打开相机的日期,所以考虑自连接,并且表a的日期要小于表b的日期(表a为当日打开日期,表b为之后日期打开)。

select * 
from
    (select uid, date_format(dayno, '%Y%m%d') as day1
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) a
left join
    (select uid, date_format(dayno, '%Y%m%d') as day2
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) b
on a.uid = b.uid and a.day1 < b.day2;

现在,按照day1的日期分组,计算去重后的uid数量可作为每日活跃用户数。

select day1 as 日期, count(distinct a.uid) as 活跃用户数
from
    (select uid, date_format(dayno, '%Y%m%d') as day1
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) a
left join
    (select uid, date_format(dayno, '%Y%m%d') as day2
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) b
on a.uid = b.uid and a.day1 < b.day2
group by a.day1;

用sum(if(条件,1,0))统计不同的活跃用户数。

select day1 as 日期,
count(distinct a.uid) as 活跃用户数,
sum(if(b.day2-a.day1=1, 1, 0)) as 次日留存用户数,
sum(if(b.day2-a.day1=2, 1, 0)) as 三日留存用户数,
sum(if(b.day2-a.day1=6, 1, 0)) as 七日留存用户数
from
    (select uid, date_format(dayno, '%Y%m%d') as day1
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) a
left join
    (select uid, date_format(dayno, '%Y%m%d') as day2
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) b
on a.uid = b.uid and a.day1 < b.day2
group by a.day1;

计算留存率

select day1 as 日期,
count(distinct a.uid) as 活跃用户数,
sum(if(b.day2-a.day1=1, 1, 0)) as 次日留存用户数,
sum(if(b.day2-a.day1=2, 1, 0)) as 三日留存用户数,
sum(if(b.day2-a.day1=6, 1, 0)) as 七日留存用户数,
concat(round((sum(if(b.day2-a.day1=1, 1, 0))/count(distinct a.uid))*100,2),'%') as 次日留存率,
concat(round((sum(if(b.day2-a.day1=2, 1, 0))/count(distinct a.uid))*100,2),'%') as 三日留存率,
concat(round((sum(if(b.day2-a.day1=6, 1, 0))/count(distinct a.uid))*100,2),'%') as 三日留存率
from
    (select uid, date_format(dayno, '%Y%m%d') as day1
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) a
left join
    (select uid, date_format(dayno, '%Y%m%d') as day2
    from act_user_info_mysql
    where app_name = '相机'
    group by uid, dayno) b
on a.uid = b.uid and a.day1 < b.day2
group by a.day1;

深入面试题延伸问题

1、分别用Python、sql导入附件(相机.xlsx)的数据进入自己的Mysql当中,要求字段符合、表名符合(可加后缀区分)。

sql步骤

尝试多次用sql语句导入,都是报错。所以直接用workbench自带的功能导入,这样可能会更正规一些,不容易报错。

首先将源数据excel文件用excel打开,并另存为csv格式。
通过notepad++打开csv文件,并且选择编码为UTF-8。

打开workbench,右键点击需要存放表格的数据库,按照下图步骤进行操作。



选择需要载入的csv文件后,输入要创建的表的名称。剩下的一路next就可以了。



载入完成
python步骤

导入常用模块

import pandas as pd
import numpy as np
import pymysql
pymysql.install_as_MySQLdb()
from sqlalchemy import create_engine

首先,把文件读取到Python中。因为excel文件里有两个表,所以通过sheet_name='Sheet1'指定读取我们要的表。

df=pd.read_excel(r'C:\Users\admin\Desktop\xiangji.xlsx',sheet_name='Sheet1')
df

修改某些列的数据类型,使其符合要求

df.uid=df.uid.astype(str)
df.dayno=df.dayno.astype(str)

输出到本地数据库

engine = create_engine('mysql://root:mysql1234@localhost:3306/datafrog_test')
df.to_sql('act_user_info',con = engine,if_exists='replace', index=False)

2、分析pd.to_sql中的if_exist参数可用哪些,分别是什么,有什么细节区别?认为用哪种更好。

分别是fail, append, replace。

fail当数据库里存在同名表时,什么也不做。append当数据库里存在同名表时,把数据追加在表后面。replace会删除原来的同名表,再新建表后插入数据。
具体哪种比较好要看情况,如果你的数据每天追加就用append。每天都更新,并且不需要保留之前数据就用replace。fail的话,可以避免覆盖原有表格的失误发生。

3、总结MYSQL的date_format、concat的用法,并以此数据举例说明。

date_format可以将把一个日期转换为各种样式的字符串。格式为date_format(原本日期,需要的日期的格式)

select uid, date_format(dayno, '%Y%m%d') as day1
from act_user_info_mysql
where app_name = '相机'
group by uid, dayno;

concat()用于拼接,将需要拼接在一起的内容放在括号内,用逗号隔开。
例如把uid和日期通过'-'符号连接在一起。

select concat(uid,'-',dayno) from act_user_info_mysql;

4、利用MYSQL语句解题,分步骤达到目的再一次性运行,达到题目要求。

参照上面的解题过程

5、用Python解题,要求函数封装,利用调用函数计算。

import datetime
import pandas as pd
import numpy as np

def jisuan():
    # 读取数据
    df=pd.read_excel(r'C:\Users\admin\Desktop\xiangji.xlsx',sheet_name='Sheet1')
    # 通过处理,得到后续所有计算所需的df2表
    df=df[df.app_name=='相机'].drop_duplicates(subset=['dayno','uid'])
    df=df[['uid','dayno']]
    df2=pd.merge(df,df,on='uid',how='left')
    df2=df2[df2.dayno_x<=df2.dayno_y]
    df2['difference']=df2.apply(lambda x: x[2]-x[1], axis=1)
    # 得到基础活跃人数表d1,方便以后做连接
    d1=df2.groupby('dayno_x').uid.nunique().reset_index().rename(columns={'dayno_x':'日期','uid':'活跃用户数'})
    d2=df2[df2['difference']==datetime.timedelta(1)].groupby('dayno_x').uid.count().reset_index().rename(columns={'dayno_x':'日期','uid':'次日留存用户数'})
    d1=pd.merge(d1,d2,how='left')
    d2=df2[df2['difference']==datetime.timedelta(2)].groupby('dayno_x').uid.count().reset_index().rename(columns={'dayno_x':'日期','uid':'三日留存用户数'})
    d1=pd.merge(d1,d2,how='left')
    d2=df2[df2['difference']==datetime.timedelta(6)].groupby('dayno_x').uid.count().reset_index().rename(columns={'dayno_x':'日期','uid':'七日留存用户数'})
    # 填充缺失值
    d1=pd.merge(d1,d2,how='left').fillna(0)
    # 浮点数转为整数
    d1[['次日留存用户数','三日留存用户数','七日留存用户数']]=d1[['次日留存用户数','三日留存用户数','七日留存用户数']].astype(int)
    d1['次日留存率']=d1['次日留存用户数']/d1['活跃用户数']
    d1['三日留存率']=d1['三日留存用户数']/d1['活跃用户数']
    d1['七日留存率']=d1['七日留存用户数']/d1['活跃用户数']
    # 浮点数转为百分比
    d1['次日留存率']=d1['次日留存率'].apply(lambda x: format(x,'.2%'))
    d1['三日留存率']=d1['三日留存率'].apply(lambda x: format(x,'.2%'))
    d1['七日留存率']=d1['七日留存率'].apply(lambda x: format(x,'.2%'))
    return d1
运行结果

你可能感兴趣的:(2020-07-12题目五)