pandas数据处理与分析补充习题(二)

发布于:2022-10-22 ⋅ 阅读:(956) ⋅ 点赞:(0)

书接上回~

Ex3:统计商品的审核情况

在data/supplement/ex3中存放了两个有关商品审核的信息表,“商品信息.csv”中记录了每个商品的ID号,唯一的识别码以及商品所属的类别,“申请与审核记录.csv”中记录了每个商品的审核信息。已知商品的审核流程如下:由申请人发起商品审核的申请,然后由审核人审核,审核的结果包括通过与不通过两种情况,若商品不通过审核则可以由另一位申请人再次发起申请,直到商品的审核通过。

df_info = pd.read_csv('data/supplement/ex3/商品信息.csv')

df_info.head()
Out[9]: 
         ID号      识别码  类别
0  ID 000001  CRtXJUK  T1
1  ID 000002  RGSxifC  Q1
2  ID 000003  AboduTp  S1
3  ID 000004  zlpUeMl  S2
4  ID 000005  IVQqhIK  S3

df_record = pd.read_csv('data/supplement/ex3/申请与审核记录.csv')

df_record.head()
Out[11]: 
         ID号         申请人        申请时间         审核人        审核时间   结果
0  ID 000001  \#+3((52\{  2020-04-19  ~1=6\*183|  2020-05-03  未通过
1  ID 000001  8@75[1|2\*  2020-05-10  15![3\({59  2020-07-17  未通过
2  ID 000001  }!7)(#^0*7  2020-07-28  3`}04}%@75  2020-08-23   通过
3  ID 000002  |*{20#9|}5  2020-01-05  ={`8]03*4+  2020-03-09  未通过
4  ID 000002  4~6%)455`[  2020-03-14  =$-36[)|8]  2020-04-21  未通过
  • 有多少商品最终通过审核?

df_info = pd.read_csv('d:\pdd\data_set\data\supplement\ex3/商品信息.csv')
df_record = pd.read_csv('d:\pdd\data_set\data\supplement\ex3\申请与审核记录.csv')
df_record.loc[df_record.结果=='通过'].shape[0]
#query或者loc都可以

10848
  • 各类别商品的通过率分别为多少?

思路类似求数据集中各列的非空值占比,求得一个布尔序列后计算均值就是通过率

df_record_pass = df_record.loc[df_record.结果=='通过',['ID号','结果']]
df_info_pass = df_info.merge(df_record_pass,on = 'ID号',how = 'left')
#商品表以ID号主键左连接申请审核记录表
df_info_pass.groupby('类别')['结果'].agg(lambda x:x.notna().mean())
#结果列非空,即通过为1,不通过为空,即0,求均值即通过率

类别
Q1    0.395866
Q2    0.406604
S1    0.402618
S2    0.408443
S3    0.414896
T1    0.398679
T2    0.402781
Name: 结果, dtype: float64
  • 对于类别为“T1”且最终状态为通过的商品,平均审核次数为多少?

df_record_pass = df_record.loc[df_record['ID号'].isin(df_record_pass['ID号'].unique())]
#用loc取出df_record中通过的商品行记录
df_record_pass_cat = df_record_pass.loc[df_record['ID号'].isin(df_info.query('类别 == "T1"')['ID号'].unique())]
#loc取出T1类别商品
df_record_pass_cat.groupby('ID号')['结果'].count().mean()

5.547382372432074
  • 是否存在商品在上一次审核未完成时就提交了下一次审核申请?

解题思路:按商品分组后,对其申请时间的序列做shift操作,使其对齐上一次的审核时间的序列,比较有没有大于的;使用了apply来操作按以上思路构造的自定义函数,效率很低,运行的很慢,各位有更好的方法,十分乐意讨教。

df_record['申请时间'] = pd.to_datetime(df_record['申请时间'])
df_record['审核时间'] = pd.to_datetime(df_record['审核时间'])
df_record = df_record.sort_values(['ID号','申请时间'])
df_record_shift = df_record.copy()
#df_record_shift['审核时间'] = df_record_shift['审核时间'].shift(1)
#all(df_record_shift['申请时间'] >= df_record_shift['审核时间'])
def gb_compare(x):
    s1 = x['申请时间']
    s2 = x['审核时间']
    s1 = s1.shift(-1)
    #
    s1 = s1.iloc[:-1]
    s2 = s2.iloc[:-1]
    return all(s1>s2)
all(df_record.groupby(['ID号'])['申请时间','审核时间'].apply(gb_compare))

False
  • 请对所有审核通过的商品统计第一位申请人和最后一位审核人的信息,返回格式如下:

pd.DataFrame(
    {
        "ID号": ["ID 000001"]+["..."]*3,
        "类别":["T1"]+["..."]*3,
        "申请人":["\#+3((52\{"]+["..."]*3,
        "审核人":["3`}04}%@75"]+["..."]*3
    },
    index=[1,2,3,"..."]
)

Out[12]: 
           ID号   类别         申请人         审核人
1    ID 000001   T1  \#+3((52\{  3`}04}%@75
2          ...  ...         ...         ...
3          ...  ...         ...         ...
...        ...  ...         ...         ...

解题思路在代码里,直接看吧:

df_record = df_record.sort_values(['ID号','申请时间'])
#按申请时间排序以便取出首个申请人和末位审核人
df_record_pass = df_record.loc[df_record['ID号'].isin(df_record_pass['ID号'].unique())]
#用loc取出df_record中通过的商品行记录
df_record_applicant = df_record_pass.groupby('ID号')['申请人'].head(1)
#gb对象head\tail函数过滤首行或末行
df_record_applicant = pd.concat([df_record_pass['ID号'],df_record_applicant],axis = 1,join = 'inner').set_index('ID号')
#使用concat函数连接两张表,按索引列连接
df_record_reviewer = df_record_pass.groupby('ID号')['审核人'].tail(1)
df_record_reviewer = pd.concat([df_record_pass['ID号'],df_record_reviewer],axis = 1,join = 'inner').set_index('ID号')
df_e3_res = pd.concat([df_record_applicant,df_record_reviewer,df_info[['ID号','类别']].set_index('ID号')],axis = 1,join = 'inner')
#三表连接
df_e3_res[['类别','申请人','审核人']].reset_index()
#调整为格式所要求的列顺序

看下结果:

Ex4:删除同样的行

现有两张表,请在df1中剔除在df2中出现过的行。

df1 = pd.DataFrame({
    "A": [3,2,2,3,1,3],
    "B": [2,1,1,3,6,2],
    "C": [1,2,2,7,7,1],
    "D": [5,6,6,1,2,5],
})


df1
Out[14]: 
   A  B  C  D
0  3  2  1  5
1  2  1  2  6
2  2  1  2  6
3  3  3  7  1
4  1  6  7  2
5  3  2  1  5

df2 = pd.DataFrame({
    "A": [2,3,1],
    "B": [1,9,6],
    "C": [2,7,7],
    "D": [6,1,2],
})


df2
Out[16]: 
   A  B  C  D
0  2  1  2  6
1  3  9  7  1
2  1  6  7  2

结果应当如下:

pd.DataFrame({
    "A": [3,3,3],
    "B": [2,3,2],
    "C": [1,7,1],
    "D": [5,1,5],
})

Out[17]: 
   A  B  C  D
0  3  2  1  5
1  3  3  7  1
2  3  2  1  5

面向测试用例编程:

#别人都是面向对象编程,咱没有对象,咱们直接面向测试用例编程
df2['flag'] = 'flag'
#df2添加flag标记列,方便后续两张表连接时对于标记不相同行
flg_idx = df1.merge(df2,on = df1.columns.to_list(),how = 'left')['flag'].isna()
#将所有列作为连接条件,取左连接结果中来源于df2的flag列为空的行
df1.loc[flg_idx].reset_index(drop = True)

	A	B	C	D
0	3	2	1	5
1	3	3	7	1
2	3	2	1	5

Ex5:统计每个学区的开课数量

某个城市共有4个学区,每个学区有若干学校,学校之间名字互不相同。每一条记录为该学校开设的课程,一个学校可能有多条记录,每一条记录内部的课程不会重复,但同一学校不同记录之间的课程可能重复。

df = pd.read_csv('data/supplement/ex5/school_course.csv')

df.head()
Out[19]: 
     Area      School                         Course
0  area_1   school_99                      course_90
1  area_2   school_32                      course_20
2  area_3   school_64                      course_38
3  area_1  school_231  course_9 course_40 course_100
4  area_3  school_147  course_57 course_77 course_28

课程的种类共有100门,编号为”school_1”到”school_100”。现要统计每个学区各项课程的开设学校数量,结果如下格式:

res = pd.DataFrame(
    0, index=["course_%d"%(i+1) for i in range(100)],
    columns=["area_%d"%(i+1) for i in range(4)]
)


res.head() # 若area_1共有20所学校开设了course_1,则第一个单元格为20
Out[21]: 
          area_1  area_2  area_3  area_4
course_1       0       0       0       0
course_2       0       0       0       0
course_3       0       0       0       0
course_4       0       0       0       0
course_5       0       0       0       0

真的不想搞这些格式,看代码吧:

df_school = pd.read_csv('d:/pdd/data_set/data/supplement/ex5/school_course.csv')
df_course = df_school.Course.str.split(' ',expand = True).rename(columns = lambda x:'cou'+str(x))
#拆分下用_连接不同课程的course列
df_school = pd.concat([df_school,df_course],join ='inner',axis = 1)
#拆分的课程与原表连接
df_cou1 = df_school[['Area','School','cou1']].dropna().rename(columns = {'cou1':'cou0'})
df_cou2 = df_school[['Area','School','cou2']].dropna().rename(columns = {'cou2':'cou0'})
#第二,第三课程列去空值
df_school = pd.concat([df_school[['Area','School','cou0']],df_cou1,df_cou2],axis = 0,join = 'inner')
#将拆分出来的所有课程列纵向合并
df_school = df_school.drop_duplicates(['Area','School','cou0'])
#去除重复值
df_school = pd.DataFrame(df_school.rename(columns = {'cou0':'Course'}).groupby(['Area','Course'])['School'].count().astype('Int32'))
#分组计算不同区域内不同课程的开课学校计数
df_res = df_school.reset_index().pivot(columns = 'Area',index = 'Course',values = 'School')
#pivot函数将关于area的长表变宽表
import re
df_res = df_res.reset_index()
df_res['CourseNo'] = df_res['Course'].str.extract('(\d+)').astype('Int32')
#正则表达式取课程编号方便对课程排序
df_res.set_index('Course').sort_values('CourseNo').iloc[:,:-1]

看结果:

 还剩两题,咱们明天见~

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到