疫情期間,我用python輔助統計班級打卡信息

前言:由於全國受到新型冠狀病毒的影響,我們學校在已有的學工系統開發了 “每日一報”和“i簽到” 兩個功能來記錄學生的身體狀況和位置信息,確保並監督學生無誤填寫,每天輔導員都要多次從系統中導出今日打卡記錄,並讓班長提醒未打卡同學打卡或者有信息異常的同學重新確認信息。

操作平臺: win10,python37,jupyter
圖片名字等信息均打碼處理



1、初步打卡情況簡介

  1. 剛開始的時候,輔導員每天導出打卡的名單,然後發到通知羣裏,每個班幹再用Excel篩選出自己班的信息,進行相關的信息通知同學,全部打卡完成後,輔導員還需要查看每一個同學的信息是否填寫有誤。輔導員一共管理6班班級,共340名學生,每次都會在上面花費大量的時間,不小心還會篩選統計錯誤。

● 通知情況: 導出表格給班長查看打卡情況!
在這裏插入圖片描述


● 篩選出未信息通知學生: 需要一直統計催打卡,過程艱辛複雜!
在這裏插入圖片描述


● 領導反饋: 學生出現亂填亂寫的情況很嚴重,需要老師來做好把關工作!
在這裏插入圖片描述
在這裏插入圖片描述


  1. 我每次篩選我們班的信息也感覺一點麻煩,先是在Excel中篩選出我們班的信息,然後在對“打卡情況進行”排序,再截屏發到班羣裏通知同學打卡,有時候還要一個一個的檢查同學們是否打卡有誤,這個估計也是每一個輔導員和班長都要共同面臨的問題

《每日一報》打卡表格信息:
在這裏插入圖片描述


《i簽到》打卡表格信息:
在這裏插入圖片描述

  • 接下來到python出場了,用它完成全部過程的自動化統計,然後複製粘貼到消息通知羣就可以了!

2、pandas導入數據

2.1、導入數據並查看

import pandas as pd
data1 = pd.read_excel("./data/測試/每日一報.xlsx")#導入每日一報數據
data1.columns#查看錶頭
Index(['學號', '姓名', '性別代碼', '性別', '聯繫方式', '學院', '專業代碼', '專業', '班級', '年級',
       '是否完成填報', '輔導員填報', '本人體溫', '本人是否是疑似病例或確診病例', '本人是否接觸過疑似或確診病例',
       '本人是否是湖北、武漢經歷的人', '本人是否是確診病例的密切接觸者共同居住人員',
       '本人居住地是否是湖北省以外疫點人員(指生活的小區、單元樓發生確診病例)', '本人昨天是否外出', '外出地點',
       '本人是否被社區列爲重點排查對象', '被確定爲重點排查對象時間', '是否解除隔離', '解除隔離時間', '具體解除時間',
       '家庭成員感染新冠狀病毒情況', '居住地是否發生變化', '是否有發熱、咳嗽等症狀'],
      dtype='object')

2.2、查看數據形狀

data.shape
(340, 28)

結果分析: 從學工系統中導出的《每日一報》表格一共有340行,也就是340人;一共有28列數據信息,其中大部分是需要填寫的,其他信息可以更改。



3、每日一報未打卡人數

3.1、查看打卡情況

data['是否完成填報'].value_counts()
已完成    282
未完成     58
Name: 是否完成填報, dtype: int64

3.2、提取出未打卡的同學

  • data['是否完成填報'] == '未完成' 返回的結果爲 TrueFalse ,當它相等時返回True,提取返回 True 的所有數據,就是沒有打卡的數據。
noDo = data[data['是否完成填報'] == '未完成'] #提取出未完成的學生
noDo.head()#顯示前五行

在這裏插入圖片描述

3.3、提取出對應的學生

noDo = data[data['是否完成填報'] == '未完成'] #提取出未完成的學生
noDo_num = noDo.shape[0] #獲取未打卡人數,如果全部完成,就不需要查找對應的同學
if noDo_num == 0:
    print ("▼ 每日一報已全部打卡完畢!")
else:
    print ("▼ 每日一報未打卡人數: %s(人)"%noDo_num)#記錄未打卡人數
    for bj in range(len(noDo['班級'].unique())): #noDo['班級'].unique()取出所有未打卡的班級,並去重,計算班級數
        class_xinxi = noDo['班級'].unique()[bj] #依次取出每個
        index = noDo[noDo['班級'] == class_xinxi] #在該班級中取出對應的同學
        name_list = [] #每次循環到這裏都會把它置空
        for name in index['姓名']:
            name_list.append(name)
        names = "、".join(name_list)#將數組變爲字符串
        print (class_xinxi + ": "+ names +'\n')

結果如下:
在這裏插入圖片描述
備註: 找出《i簽到》未打卡的同學,方式也是一樣的,這裏就不重述了。


4、查找打卡信息異常同學

  • 由於在填寫信息時,不小心很容易把自己的信息填寫錯,本來沒有生病的也填寫成生病,沒有被隔離也填寫爲被隔離,所以必須要把這類信息找出來,讓填寫的同學確認一下,是否填寫有誤。
  • 因爲大部分同學的信息在選擇填寫時是一致的,所以我們可以選擇衆數比對的方式來找出不符合衆數的值

4.1、求衆數

(1)查看衆數

# 取衆數
mode = data['是否完成填報'].mode()[0] #它輸出的值爲數組,加上[0]提取第一個值爲字符串,在這裏幾乎不會出現兩個衆數
mode
'已完成'
  • 這樣就找出了大部分同學填寫的值,如果誰沒有填這個值,那麼就判定可能是異常值,並提取出該同學的信息。

(2)提取出異常的數據

do_data = data[data['是否完成填報'] == '已完成'] #只統計完成的同學,爲打卡的爲空值,以免被空值干擾衆數
mode = do_data['本人是否是疑似病例或確診病例'].mode()[0] #衆數
dif_do = do_data[do_data['本人是否是疑似病例或確診病例'] != mode] #提取出完成打卡中的異常值,不等於衆數的就是異常值
dif_do

在這裏插入圖片描述

4.2、標明我要審覈的表頭

(1)提取出表頭

columns =['本人是否是疑似病例或確診病例', '本人是否接觸過疑似或確診病例', '本人是否是湖北、武漢經歷的人', '本人是否是確診病例的密切接觸者共同居住人員', '本人居住地是否是湖北省以外疫點人員(指生活的小區、單元樓發生確診病例)', '家庭成員感染新冠狀病毒情況', '是否有發熱、咳嗽等症狀']
for col in columns:
    print (col)
本人是否是疑似病例或確診病例
本人是否接觸過疑似或確診病例
本人是否是湖北、武漢經歷的人
本人是否是確診病例的密切接觸者共同居住人員
本人居住地是否是湖北省以外疫點人員(指生活的小區、單元樓發生確診病例)
家庭成員感染新冠狀病毒情況
是否有發熱、咳嗽等症狀

(2)用法

data['本人是否是疑似病例或確診病例']
0      否,身體健康
1      否,身體健康
2      否,身體健康
3      否,身體健康
4      否,身體健康
        ...  
335    否,身體健康
336    否,身體健康
337       NaN
338    否,身體健康
339    否,身體健康
Name: 本人是否是疑似病例或確診病例, Length: 340, dtype: object
  • NaN 表示空值,沒有數據,也就是沒有打卡

(3)總結
我提取出我需要額外審覈的列,把它放進 data[ ] 中,就可以獲取到同學們打卡的所有信息了


4.3、提取出該同學

# 移除不必要的列
columns =['本人是否是疑似病例或確診病例', '本人是否接觸過疑似或確診病例', '本人是否是湖北、武漢經歷的人', '本人是否是確診病例的密切接觸者共同居住人員', '本人居住地是否是湖北省以外疫點人員(指生活的小區、單元樓發生確診病例)', '家庭成員感染新冠狀病毒情況', '是否有發熱、咳嗽等症狀']
do_data = data[data['是否完成填報'] == '已完成'] #只統計完成的同學,爲打卡的爲空值,以免被空值干擾衆數

for col in columns:
    mode = do_data[col].mode()[0] #衆數
    dif_do = do_data[do_data[col] != mode] #提取出完成打卡中的異常值,不等於衆數的就是異常值
    dif_do_num = dif_do.shape[0] #統計異常值數量,如果爲0,就結束這個循環
    if dif_do_num == 0:
        pass
    else:
        print ("●",col + ":",dif_do_num, "人")
        for bj in range(len(dif_do['班級'].unique())):
            class_xinxi = dif_do['班級'].unique()[bj]
            index = dif_do[dif_do['班級'] == class_xinxi]
            name_list = []
            for name in index['姓名']:
                name_list.append(name)
            names = "、".join(name_list)
            print (class_xinxi + ": "+ names)
        print ("")

結果:
在這裏插入圖片描述

5、查找體溫異常的同學

  • 醫學上把人的正常體溫定爲:35.5~37.2℃之間,我就以它作爲判斷的標準。
  • 由於打卡系統的溫度信息是全手動填寫的,所以容易出現各種各樣的格式,如:
    • 36, 36.3, 體溫:36.4, 36.5度, 體溫正常, 36.6℃, 36度7 等等
  • 學校要求填寫具體體溫,所以必須要找出填寫“體溫正常”之類的學生,要求填寫具體溫度。

5.1、體溫預處理

  • 這個主要是把學生的體溫標準化處理,讓它可以正常進行大小判斷。
  • 原因:有些同學的體溫是標準的數值,有些帶了漢字,有些忘記小數點,有些帶了特殊符號
  • 列如:
import re
#學生可能出現的體溫填寫情況
text_list = ['36','36.3', '體溫:36.4', '36.5度', '體溫正常', '36.6℃','36度7', '38度8','3690', '368', '370度', '體溫:38度1']
for txt in text_list:
    text = re.sub("[^0-9\u4e00.]", "", txt) #只保留數字“0~9”和“.”
    if text == '':
        print("text沒有數值:", txt)
    else:
        if float(text) <  35.5 or float(text) > 37.2:
            #如果體溫中有“度”字,如:36度8,用“度”字進行分隔,分別去掉干擾因子,下一步拼接完整,末尾接“0”防止小數點在末尾
            if "度" in txt:
                temperature = re.sub("[^0-9\u4e00.]","", txt.split('度')[0]) + "." + re.sub("[^0-9\u4e00.]","", txt.split('度')[1]) + "0"
                if float(temperature) < 35.5 or float(temperature) > 37.2:
                    print ("超過範圍:",txt)
            else:
                print ("體溫異常:", text)
text沒有數值: 體溫正常
超過範圍: 388
體溫異常: 3690
體溫異常: 368
超過範圍: 370度
超過範圍: 體溫:381

結果分析: 這樣就可以找出沒有填寫具體體溫的同學了。

5.2、沒有填寫具體體溫

temperature_tab = do_data['本人體溫'] #體溫列
for i in do_data.index:       
    temperature = re.sub("[^0-9\u4e00.]","", str(temperature_tab[i])) #體溫清洗,只保留"數值"和“.”
    if temperature == '':            
        print ("沒有填寫具體體溫: ", do_data['班級'][i], do_data['姓名'][i], do_data['本人體溫'][i])

運行結果:
在這裏插入圖片描述

5.3、獲取所有的異常體溫

  • 所用上面的方法,先對一些帶有漢字的體溫進行預處理,再進行大小判斷!
print ("\n◙以下同學的體溫不在35.5~37.2度之間")
for i in do_data.index:#從數據索引中循環出索引
    temperature = re.sub("[^0-9\u4e00.]","", str(temperature_tab[i]))
    if temperature == '':
        continue #運行到這裏後就結束程序當前運行,過濾掉沒有數值的數據
    single_tem = do_data['本人體溫'][i] #遍歷個人體溫
    # 爲了預防學生填寫的類型超過我的判斷,設置一個異常捕捉
    try:
        if float(temperature) < 35.5 or float(temperature) > 37.2: #體溫不在[3.5, 37.2]之間,進行下一步,初步判斷異常
            #如果體溫中有“度”字,如:36度8,用“度”字進行分隔,分別去掉干擾因子,下一步拼接完整
            if "度" in single_tem:
                temperature = re.sub("[^0-9\u4e00.]","", str(single_tem.split('度')[0])) + "." + re.sub("[^0-9\u4e00.]","", str(single_tem.split('度')[1])) + "0"
                print ("b"*50)
                if float(temperature) < 35.5 or float(temperature) > 37.2:
                    print (do_data['班級'][i], do_data['姓名'][i], single_tem)
            else:
                print (do_data['班級'][i], do_data['姓名'][i], single_tem)
    except:
        print (do_data['班級'][i], do_data['姓名'][i], single_tem)   

運行結果:
在這裏插入圖片描述




6、查詢所有信息代碼彙總

  • 在發通知時,最好的方法就是把填寫有誤的同學也提出了,方便讓他改正。直接發輸出的結果發到羣裏是很直觀的方法,所有需要把我們需要的功能彙總在一起,一起輸出結果。
import pandas as pd
import numpy as np
from pandas import DataFrame,Series
import re

#導入數據
data1 = pd.read_excel("./data/每日一報.xlsx")
data2 = pd.read_excel("./data/i簽到.xlsx")
time = input("數據導出時間:") #輸入時間,目的是方便直接複製到羣裏

"""查找出沒有完成每日一報簽到的同學"""
noDo = data1[data1['是否完成填報'] == '未完成'] #提取出未打卡的同學
noDo_num = noDo.shape[0]
if noDo_num == 0:
    print ("▼ 每日一報已全部打卡完畢!")
else:
    print ("▼ 每日一報未打卡人數: %s(人)"%noDo_num) #打印出人數
    for bj in range(len(noDo['班級'].unique())):
        class_xinxi = noDo['班級'].unique()[bj]
        index = noDo[noDo['班級'] == class_xinxi]
        name_list = []
        for name in index['姓名']:
            name_list.append(name)
        names = "、".join(name_list)
        print (class_xinxi + ": "+ names +'\n')
print ("")

"""查找出沒有完成i簽到打卡的同學"""
noDo = data2[data2['簽到狀態'] == '未簽到'] #提取出未簽到的同學
noDo_num = noDo.shape[0]
if noDo_num == 0:
    print ("◆ i簽到已全部打卡完畢!")
else:
    print ("◆ i簽到未打卡人數: %s(人)"%noDo_num)
    for bj in range(len(noDo['班級'].unique())):
        class_xinxi = noDo['班級'].unique()[bj]
        index = noDo[noDo['班級'] == class_xinxi]
        name_list = []
        for name in index['姓名']:
            name_list.append(name)
        names = "、".join(name_list)
        print (class_xinxi + ": "+ names +'\n')
        
print ("\n☢以下同學“每日一報”打卡的信息可能有誤☟☟☟")
"""查找出表格中的異常值"""
# 移除不必要的列
columns =['本人是否是疑似病例或確診病例', '本人是否接觸過疑似或確診病例', '本人是否是湖北、武漢經歷的人', '本人是否是確診病例的密切接觸者共同居住人員', '本人居住地是否是湖北省以外疫點人員(指生活的小區、單元樓發生確診病例)', '家庭成員感染新冠狀病毒情況', '是否有發熱、咳嗽等症狀']
do_data = data1[data1['是否完成填報'] == '已完成'] 
for col in columns:
    mode = do_data[col].mode()[0] #衆數
    dif_do = do_data[do_data[col] != mode] #提取與衆數不一樣的值,也就是異常值
    dif_do_num = dif_do.shape[0]    
    if dif_do_num == 0:
        pass
    else:
        print ("●",col + ":",dif_do_num, "人")
        for bj in range(len(dif_do['班級'].unique())): #班級去重dif_do['班級'].unique()
            class_xinxi = dif_do['班級'].unique()[bj] #提取出班級
            index = dif_do[dif_do['班級'] == class_xinxi]
            name_list = []
            for name in index['姓名']:
                name_list.append(name)
            names = "、".join(name_list)
            print (class_xinxi + ": "+ names)
        print ("")

"""找出沒有填寫具體體溫的同學"""
temperature_tab = do_data['本人體溫'] #體溫列
for i in do_data.index:       
    temperature = re.sub("[^0-9\u4e00.]","", str(temperature_tab[i])) #體溫清洗,只保留"數值"和“.”
    if temperature == '':            
        print ("沒有填寫具體體溫: ", do_data['班級'][i], do_data['姓名'][i], do_data['本人體溫'][i])

"""找出體溫不在35.5~37.2度之間的同學"""       
print ("\n◙以下同學的體溫不在35.5~37.2度之間")
for i in do_data.index:
    temperature = re.sub("[^0-9\u4e00.]","", str(temperature_tab[i]))
    if temperature == '':
        continue #運行到這裏後就結束程序當前運行,過濾掉沒有數值的數據
    single_tem = do_data['本人體溫'][i] #遍歷個人體溫
    # 爲了預防學生填寫的類型超過我的判斷,設置一個異常捕捉
    try:
        if float(temperature) < 35.5 or float(temperature) > 37.2: #體溫不在[3.5, 37.2]之間,進行下一步,初步判斷異常
            #如果體溫中有“度”字,如:36度8,用“度”字進行分隔,分別去掉干擾因子,下一步拼接完整
            if "度" in single_tem:
                temperature = re.sub("[^0-9\u4e00.]","", str(single_tem.split('度')[0])) + "." + re.sub("[^0-9\u4e00.]","", str(single_tem.split('度')[1])) + "0"
                print ("b"*50)
                if float(temperature) < 35.5 or float(temperature) > 37.2:
                    print (do_data['班級'][i], do_data['姓名'][i], single_tem)#輸出班級,姓名,體溫
            else:
                print (do_data['班級'][i], do_data['姓名'][i], single_tem)
    except:
        print (do_data['班級'][i], do_data['姓名'][i], single_tem)      

運行結果: 直接把它複製粘貼到通知羣裏就完事了☟☟☟

在這裏插入圖片描述



7、繪製打卡分佈圖

7.1、認識cpca

  • cpca官網: https://pypi.org/project/cpca/

  • cpca : chinese_province_city_area_mapper:一個用於識別簡體中文字符串中省,市和區並能夠進行映射,檢驗和簡單繪圖的python模塊。

  • 安裝:目前只支持python3 pip install cpca

7.1.1、全文模式

  • 默認情況下transform方法的cut參數爲True,即採用分詞匹配的方式,這種方式速度比較快,但是準確率可能會比較低,如果追求準確率而不追求速度的話,建議將cut設爲False(全文模式)
  • jieba分詞並不能百分之百保證分詞的正確性,所以我們引入了全文模式,不進行分詞,直接全文匹配,使用方法如下:
location_str = ["貴州省黔西南布依族苗族自治州貞豐縣210省道", "湖南省岳陽市岳陽樓區對門山路", "貴州省遵義市餘慶縣方竹街", "貴州省黔南布依族苗族自治州都勻市75國道"]
import cpca
df = cpca.transform(location_str, cut=False)
df
地址
0 貴州省 黔西南布依族苗族自治州 貞豐縣 黔西南布依族苗族自治州貞豐縣210省道
1 湖南省 岳陽市 岳陽樓區 對門山路
2 貴州省 遵義市 餘慶縣 方竹街
3 貴州省 黔南布依族苗族自治州 都勻市 黔南布依族苗族自治州都勻市75國道

7.1.2、查看同省重名的地點

location_str = ["江蘇省鼓樓區軟件大道89號"]
import cpca
df = cpca.transform(location_str)
df
WARNING:root:鼓樓區 無法映射, 建議添加進umap中
地址
0 江蘇省 鼓樓區 軟件大道89號

在結果中,它沒有把市映射出來,因爲還有其他的地名和鼓樓區同名,江蘇省徐州市也有一個鼓樓區:

import cpca
cpca.province_area_map.get_relational_addrs(('江蘇省', '鼓樓區'))
[('江蘇省', '南京市', '鼓樓區'), ('江蘇省', '徐州市', '鼓樓區')]

7.1.3、加入自定義地點

  • 當程序發現重名區並且不知道將其映射到哪一個市時,會將其加入警告信息。
  • 如果你想要讓“鼓樓區”只映射到南京市的話,在transform方法中加入umap參數指定
    映射即可:
location_str = ["江蘇省鼓樓區軟件大道89號"]
import cpca
df = cpca.transform(location_str, umap={"鼓樓區":"南京市"})
df
地址
0 江蘇省 南京市 鼓樓區 軟件大道89號

7.2、cpca繪圖

  • 模塊中還自帶一些簡單繪圖工具,可以在地圖上將上面輸出的數據以熱力圖的形式畫出來。
  • 這個工具依賴folium,爲了減小本模塊的體積,所以並不會預裝這個依賴,在使用之前請使用 pip install folium
  • 代碼運行結束後會在運行代碼的當前目錄下生成一個df.html文件,用瀏覽器打開即可看到
    繪製好的地圖。

如我繪製《i簽到》中 17級信息管理與信息系統班 班同學定位打卡的分佈圖:
(1)查看信息

xinguan = data2[data2['班級'] == '17信息管理與信息系統班'] #提取出17信息管理與信息系統班信息
print (xinguan.shape)
print (xinguan.columns)
(57, 9)
Index(['序號', '姓名', '學號', '學院', '班級', '簽到狀態', '簽到時間', '地址', '備註'], dtype='object')

(2)繪圖

import cpca #用於劃分中國的省份
from cpca import drawer #用於畫圖
import folium #導入地圖
from folium.plugins import HeatMap

loc = cpca.transform(xinguan['地址'], cut=False)#轉化地點
drawer.draw_locations(loc, "./std_loc.html")#畫出具體地點

圖中顯示:有兩名同學打卡位置沒有在貴州
在這裏插入圖片描述
貴州板塊放大後:
在這裏插入圖片描述

7.4、繪製分佈密度

  • 這裏需要安裝幾個畫圖的庫來輔助
pip install pyecharts
pip install echarts-countries-pypkg
pip install pyecharts-snapshot
  • 通過額外傳入一個樣本的分類信息,能夠在地圖上以不同的顏色畫出屬於不同分類的樣本散點圖。
  • 當鼠標移到點上時,它可以顯示具體的位置。

繪製6個班的打卡位置:

import cpca #用於劃分中國的省份庫
from cpca import drawer#畫中國地圖庫
processed = cpca.transform(data2['地址'], cut=False)#轉化信管班地點
drawer.echarts_cate_draw(processed, processed["區"], "echarts_cates.html")#顯示地理位置,區,並畫圖爆粗

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章