這次專題基於「民眾對於酒駕者的判刑度不理解,社會輿論缺乏針對酒駕修法的背景脈絡」出發,搜集了大量的酒駕判決書,但判決書以文字寫成,刑度、罰金或其他背景分析,都需要經過資料清理與轉化,才能進一步統計。
這次使用的判決書總數量達49萬份,若以人力編碼曠日廢時,且恐面臨編碼員信度考驗。這次我們以正規表達式(regular expression)鎖定文字特徵萃取,以程式掃描文字、輔以人工抽查,以確保分析數據能維持一定的正確性。
本專題以python進行資料整理與分析,這篇notebook會按步驟說明「不能安全駕駛」文字清理使用的regex模式說明,並附上酒駕致人於死、酒駕致人受傷的程式碼。
import re
import pandas as pd
df1 = pd.read_csv('/Volumes/Untitled/opendata/一般酒駕/drunkDrive_2013.csv',sep='\t')
df2 = pd.read_csv('/Volumes/Untitled/opendata/fatal_export.csv',sep='\t')
df3 = pd.read_csv('/Volumes/Untitled/opendata/hurt_export.csv',sep='\t')
df3.head(5)
當大部分的案例都有色塊時,代表模式可以抓取我們指定的特徵 |
要能歸納出模式,必須要花費時間找出判決書寫法的「樣態」,再針對這些樣態進行模式描寫(regular expression pattern)。
例如,酒後駕車在刑法中歸類於不能安全駕駛動力交通工具,查看判決書會發現,有的判決書會提出酒精濃度超過標準、有的只寫不能安全駕駛。經過大量比對後,我們以a1、a2作為法官描述「不能安全駕駛罪」的特徵。
a1是法官提出酒精濃度超過標準,其中包含了幾個寫作用詞的變形,如酒精濃度有吐氣或血液兩種檢測、酒駕標準因年代不同而有零點二五、零點五五等不同標準、標點符號的使用習慣不同、行文的輔助詞(而、之)不同,針對這些詞出現與否進行正規表達式的描寫。
a2則是可以承續酒精濃度的句子,也可以針對比較簡寫的狀況進行抓取。其中融合三種可能描述:
利用a1+a2即可標記出大部分的「不能安全駕駛罪」用詞。
不能安全駕駛罪這20年來有罰金、拘役、有期徒刑等處刑方式,因此以b1鎖定自由刑的用詞、b2鎖定罰金的用詞。以自由刑的b1為例,可以萃取這些語句:
接著我們再分流,以c1鎖定處刑數量、c2鎖定罰金數量。這部分主要是在處理數字組合與單位(年月日、元)。以罰金的c2為例,可以萃取這些語句:
綜上所述,我們以a1+a2+b1+c1來萃取不能安全駕駛遭處有期徒刑的語句、以a1+a2+b2+c2來萃取不能安全駕駛遭科罰金的語句。
#鎖定「不能安全駕駛罪」用詞
a1 = "(交通工具(而)*[,,,]*有*(吐氣|血液中)(所含)*酒精濃度達(每公升|百分之)*零點(貳伍|二五|伍伍|五五)(毫克)*以上(之情形)*|"
a2 = "(不能安全駕駛)*(而駕駛)*動力交通工具罪*(而駕駛)*者*)"
#鎖定累犯註記、判刑(拘役、有期徒刑)用詞
b1 = "[,,,](累犯)*(共*罪)*[,,,]*[各均有]*處*[,,]*(有期徒*刑徒*|拘役)"
b2 = "[,,,](累犯)*(共*罪)*[,,,]*[各均有]*[處科]*[,,]*罰金(新[台臺]幣)*"
#鎖定處刑用詞
c1 = "[一二三四五六七八九十壹貳參肆伍陸柒捌玖拾廿卅1234567890]*(年|個*月|日)"
c2 = "[一二三四五六七八九十壹貳參肆伍陸柒捌玖拾廿卅百佰千仟萬1234567890]*元*"
#找出剝奪自由刑之模式組合
findimprison = a1+a2+b1+c1
#找出罰金之模式組合
findpenalty = a1+a2+b2+c2
透過findimprison與findpenalty兩種模式可以找出大部分的處刑,但會發現判決書中也有無罪的狀況,因此再增加一種判斷無罪的模式。
當我們有能力從文字中定位單一罪行的片段時,再從中取出我們需要的數字,可以提升精準度。這個過程如下:
當判斷出處有期徒刑、罰金、無罪,分別切出結果,再把非刑期與罰金的文字都刪除(如罰金、有期徒刑、累犯、標點符號等),就可以得到量刑。以這種方式逐步建立可操作的判決書結構化資料。
#無罪的模式組合
d = "[,,,、,]*部*[分份]*[,,,、,]*均*無罪"
findun = a2+d
#把非刑期、罰金的描述都刪掉(很暴力)
rep = "情形|罰金|科|處|新[台臺]幣|銀|元|不能安全|駕駛|動力|交通工具|而|吐氣|所含|酒精濃度|達|每公升.*毫克|以上|而|者|,|,|、.*|;.*|。.*|累犯|共.罪|有期徒*刑徒*|罪|拘役|過失|如易科.*|被訴.*|應執行.*|緩刑.*|各|均|部[份分].*|共|有|之"
result_imprison_text = []
result_penalty_text = []
for i in df1['主文']:
if bool(re.search(findimprison, i)):
r = re.sub(rep,"",re.search(findimprison, i).group())
result_imprison_text.append(r)
result_penalty_text.append('')
elif bool(re.search(findf,i)):
r = re.sub(rep,"",re.search(findf,i).group())
result_imprison_text.append("")
result_penalty_text.append(r)
else:
if bool(re.search(findn,i)):
result_imprison_text.append("無罪")
result_penalty_text.append("")
else:
result_imprison_text.append("")
result_penalty_text.append("")
抓出刑期與罰金的資訊之後,需將國字換為數字。另外寫成了兩個函式:todays(x) 將刑期國字換為數字、tomoney(x) 將罰金國字換為數字。
步驟大致是:
def repl(g):
u = ['壹','貳','參','肆','伍','陸','柒','捌','玖','拾','廿','卅','一','二','三','四','五','六','七','八','九','十',0,'1','2','3','4','5','6','7','8','9','0','佰','仟','萬','百','千']
p = u.index(g)
n = [1,2,3,4,5,6,7,8,9,10,20,30,1,2,3,4,5,6,7,8,9,10,0,1,2,3,4,5,6,7,8,9,0,100,1000,10000,100,1000]
return n[p]
def nds(y):
if bool(re.search('[壹貳參肆伍陸柒捌玖拾廿卅佰百仟千萬一二三四五六七八九十1234567890]', y)):
if len(y) == 1:
return repl(y)
elif len(y) == 2:
if y[1]=='拾':
return repl(y[0])*repl(y[1])
elif y[0] =='拾' or y[0] =='廿' or y[0]=='卅':
return repl(y[0])+repl(y[1])
else:
return repl(y[0])*10 + repl(y[1])
elif len(y) == 3:
return repl(y[0])*repl(y[1])+repl(y[2])
else:
return repl(y)
else:
return 0
def todays(x):
if '年' in x or '月' in x or '日' in x:
try:
y = x.index("年")
except:
y = 0
try:
m = x.index("月")
except:
m = 0
try:
d = x.index("日")
except:
d = 0
if y>0:
year = x[0:y]
if m>0:
month = x[0+y+1:m]
if d>0:
day = x[0+m+1:d]
else:
day = ""
else:
month = ""
if d>0:
day = x[0+y+1:d]
else:
day = ""
else:
year = ""
if m>0:
month = x[0:m]
if d>0:
day = x[0+m+1:d]
else:
day =""
else:
month = ""
day = x[0:d]
res = nds(year)*365+nds(month)*30+nds(day)
return res
else:
return '*'+x
def tomoney(x):
x = x.replace('千','仟').replace('百','佰')
if '萬' in x or '仟' in x or '佰' in x:
try:
w = x.index("萬")
except:
w = 0
try:
t = x.index("仟")
except:
t = 0
try:
h = x.index("佰")
except:
h = 0
if w>0:
wan = x[0:w]
if t>0:
tho = x[0+w+1:t]
if h>0:
hun = x[0+t+1:h]
else:
hun = ""
else:
tho = ""
if h>0:
hun = x[0+w+1:t]
else:
hun = ""
else:
wan = ""
if t>0:
tho = x[0:t]
if h>0:
hun = x[0+t+1:h]
else:
hun =""
else:
tho = ""
hun = x[0:h]
res = nds(wan)*10000+nds(tho)*1000+nds(hun)*100
return res
else:
return '*'+x
result_imprison_num = []
for j in result_imprison_text:
try:
result_imprison_num.append(todays(j))
except:
result_imprison_num.append('*'+j)
result_penalty_num = []
for j in result_penalty_text:
try:
result_penalty_num.append(tomoney(j))
except:
result_penalty_num.append('*'+j)
dfnew = pd.DataFrame()
dfnew['主文'] = df1['主文']
dfnew['不-刑國字'] = result_imprison_text
dfnew['不-刑'] = result_imprison_num
dfnew['不-罰國字'] = result_penalty_text
dfnew['不-罰'] = result_penalty_num
dfnew
findimprison = "致人於死[者罪]*[,,](累犯)*(共*罪)*[,,]*[各均]*處*[,,]*(有期徒*刑*徒*|拘役)[一二三四五六七八九十壹貳參肆伍陸柒捌玖拾廿卅1234567890]*(年|個*月|日)*"
findun = "(過失致人於死|過失傷害)[、,]*部*[份分]*[、,]*(均)*無罪"
rep = "致人於死|者|,|,|、.*|;.*|。.*|累犯|處|有期徒*刑徒*|罪|拘役|過失|如易科.*|被訴.*|應執行.*|緩刑.*|各|均|部份.*|共"
result_imprison_text = []
for i in df2['主文']:
try:
r = re.sub(rep, "", re.search(findimprison, i).group())
result_imprison_text.append(r)
except:
if bool(re.search(findn, i)):
result_imprison_text.append("無罪")
else:
result_imprison_text.append("")
result_imprison_num = [todays(j) for j in result_imprison_text]
dfnew = pd.DataFrame()
dfnew['主文'] = df2['主文']
dfnew['死-刑國字'] = result_imprison_text
dfnew['死-刑'] = result_imprison_num
dfnew
findimprison = "(過失)*傷害而*(人(之身體)*|罪)*者*[,,]*(致人*[重受]傷罪*)*[,,](累犯)*(共貳罪)*[,,]*處*[,,]*(拘役|有期徒*刑徒*)([一二三四五六七八九十壹貳參肆伍陸柒捌玖拾廿卅1234567890]*(年|個*月|日))*"
findun = "(過失)*傷害人*(致人*[重受]傷)*罪*嫌*[、,]*(及.*罪)*(部分|部份)*[、,]*(均)*(無罪|公訴不受理|不受理|不理)"
rep = "(過失)*傷害(人(之身體)*|罪)|者|,|,|、.*|;.*|。.*|累犯|處|有期刑|有期徒刑|罪|拘役|過失|如易科.*|被訴.*|應執行.*|緩刑.*|各|均|部份.*|共|致|人|重|受|傷|而|害"
result_imprison_text = []
for i in df3['主文']:
try:
r = re.sub(rep, "", re.search(findimprison, i).group())
result_imprison_text.append(r)
except:
if bool(re.search(findun, i)):
if "無罪" in re.search(findun, i).group():
result_imprison_text.append('無罪')
elif "不受理" in i:
result_imprison_text.append("不受理")
else:
result_imprison_text.append("*不符")
result_imprison_num = [todays(j) for j in result_imprison_text]
dfnew = pd.DataFrame()
dfnew['主文'] = df3['主文']
dfnew['傷-刑國字'] = result_imprison_text
dfnew['傷-刑'] = result_imprison_num
dfnew
我們非資訊專業出身,程式能力是透過坊間課程、自學、不斷嘗試錯誤摸索而來,程式撰寫的風格、效率、與精確度等層面都有許多有待改進之處,歡迎方家指教。
這次使用的regular expression由於是處理中文資料,仍在很直觀的層次,大致說明使用到的語法: