चूँकि मेरा पुराना काम अक्सर 1 मिलियन लाइनों के फ़िक्स्फ़ॉर्म डेटा को संभालता है, मैंने इस मुद्दे पर शोध किया जब मैंने पायथन का उपयोग करना शुरू किया।
फिक्स्डविद 2 प्रकार के होते हैं
- ASCII फिक्स्डविद (ascii वर्ण लंबाई = 1, डबल-बाइट एन्कोडेड वर्ण लंबाई = 2)
- यूनिकोड फिक्स्डविद (एससीआई चरित्र और डबल-बाइट एन्कोडेड वर्ण लंबाई = 1)
यदि संसाधन स्ट्रिंग सभी एससीआई वर्णों से बना है, तो ASCII फिक्स्डविद = यूनिकोड फिक्स्डविद
सौभाग्य से, स्ट्रिंग और बाइट py3 में अलग-अलग होते हैं, जो डबल-बाइट एन्कोडेड वर्णों (अंडाबक, बिग 5, ईक-जेपी, शिफ्ट-जीस, आदि) के साथ काम करते समय बहुत भ्रम को कम करता है।
"ASCII फिक्स्डविथ" के प्रसंस्करण के लिए, स्ट्रिंग को आमतौर पर बाइट्स में परिवर्तित किया जाता है और फिर विभाजित किया जाता है।
तृतीय-पक्ष मॉड्यूल को आयात किए बिना
TotalLineCount = 1 मिलियन, LineLength = 800 बाइट, FixedWidthArgs = (10,25,4, ....), मैंने रेखा को लगभग 5 तरीकों से विभाजित किया और निम्नलिखित निष्कर्ष प्राप्त किया:
- संरचना सबसे तेज (1x) है
- केवल लूप, पूर्व-प्रसंस्करण नहीं फिक्स्डविथ एग्स सबसे धीमा (5x +) है
slice(bytes)
से तेज है slice(string)
- स्रोत स्ट्रिंग बाइट्स परीक्षा परिणाम है: संरचना (1x), संचालक। विजेट (1.7x), पूर्व-स्लाइस किए गए ऑबजेक्ट और सूची बोध (2.8x), re.patten ऑब्जेक्ट (2.9x)
बड़ी फ़ाइलों के साथ काम करते समय, हम अक्सर उपयोग करते हैं with open ( file, "rb") as f:
।
विधि उपरोक्त फ़ाइलों में से एक का पता लगाती है, लगभग 2.4 सेकंड।
मुझे लगता है कि उपयुक्त हैंडलर, जो डेटा की 1 मिलियन पंक्तियों को संसाधित करता है, प्रत्येक पंक्ति को 20 क्षेत्रों में विभाजित करता है और 2.4 सेकंड से कम समय लेता है।
मैं केवल उसे ढूंढता हूं stuct
और itemgetter
आवश्यकताओं को पूरा करता हूं
ps: सामान्य प्रदर्शन के लिए, मैंने यूनिकोड को बाइट्स में परिवर्तित किया। यदि आप एक डबल-बाइट वातावरण में हैं, तो आपको ऐसा करने की आवश्यकता नहीं है।
from itertools import accumulate
from operator import itemgetter
def oprt_parser(sArgs):
sum_arg = tuple(accumulate(abs(i) for i in sArgs))
cuts = tuple(i for i,num in enumerate(sArgs) if num < 0)
ig_Args = tuple(item for i, item in enumerate(zip((0,)+sum_arg,sum_arg)) if i not in cuts)
oprtObj =itemgetter(*[slice(s,e) for s,e in ig_Args])
return oprtObj
lineb = b'abcdefghijklmnopqrstuvwxyz\xb0\xa1\xb2\xbb\xb4\xd3\xb5\xc4\xb6\xee\xb7\xa2\xb8\xf6\xba\xcd0123456789'
line = lineb.decode("GBK")
fieldwidthsU = (13, -13, 4, -4, 5,-5)
fieldwidths = (13, -13, 8, -8, 5,-5)
parse = oprt_parser(fieldwidthsU)
fields = parse(line)
print('Unicode FixedWidth','fields: {}'.format(tuple(map(lambda s: s.encode("GBK"), fields))))
parse = oprt_parser(fieldwidths)
fields = parse(lineb)
print('ASCII FixedWidth','fields: {}'.format(fields))
line = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n'
fieldwidths = (2, -10, 24)
parse = oprt_parser(fieldwidths)
fields = parse(line)
print(f"fields: {fields}")
आउटपुट:
Unicode FixedWidth fields: (b'abcdefghijklm', b'\xb0\xa1\xb2\xbb\xb4\xd3\xb5\xc4', b'01234')
ASCII FixedWidth fields: (b'abcdefghijklm', b'\xb0\xa1\xb2\xbb\xb4\xd3\xb5\xc4', b'01234')
fields: ('AB', 'MNOPQRSTUVWXYZ0123456789')
oprt_parser
4x है make_parser
(सूची समझ + स्लाइस)
शोध के दौरान, यह पाया गया कि जब सीपीयू की गति तेज होती है, तो ऐसा लगता है कि re
विधि की दक्षता तेजी से बढ़ती है।
चूँकि मेरे पास परीक्षण करने के लिए अधिक और बेहतर कंप्यूटर नहीं हैं, मैं अपना परीक्षण कोड प्रदान करता हूं, अगर किसी को भी रुचि है, तो आप इसे तेज कंप्यूटर के साथ परीक्षण कर सकते हैं।
पर्यावरण चलाएं:
- ओएस: win10
- अजगर: 3.7.2
- CPU: amd Athlon x3 450
- एचडी: सीगेट 1 टी
import timeit
import time
import re
from itertools import accumulate
from operator import itemgetter
def eff2(stmt,onlyNum= False,showResult=False):
'''test function'''
if onlyNum:
rl = timeit.repeat(stmt=stmt,repeat=roundI,number=timesI,globals=globals())
avg = sum(rl) / len(rl)
return f"{avg * (10 ** 6)/timesI:0.4f}"
else:
rl = timeit.repeat(stmt=stmt,repeat=10,number=1000,globals=globals())
avg = sum(rl) / len(rl)
print(f"【{stmt}】")
print(f"\tquick avg = {avg * (10 ** 6)/1000:0.4f} s/million")
if showResult:
print(f"\t Result = {eval(stmt)}\n\t timelist = {rl}\n")
else:
print("")
def upDouble(argList,argRate):
return [c*argRate for c in argList]
tbStr = "000000001111000002222真2233333333000000004444444QAZ55555555000000006666666ABC这些事中文字abcdefghijk"
tbBytes = tbStr.encode("GBK")
a20 = (4,4,2,2,2,3,2,2, 2 ,2,8,8,7,3,8,8,7,3, 12 ,11)
a20U = (4,4,2,2,2,3,2,2, 1 ,2,8,8,7,3,8,8,7,3, 6 ,11)
Slng = 800
rateS = Slng // 100
tStr = "".join(upDouble(tbStr , rateS))
tBytes = tStr.encode("GBK")
spltArgs = upDouble( a20 , rateS)
spltArgsU = upDouble( a20U , rateS)
testList = []
timesI = 100000
roundI = 5
print(f"test round = {roundI} timesI = {timesI} sourceLng = {len(tStr)} argFieldCount = {len(spltArgs)}")
print(f"pure str \n{''.ljust(60,'-')}")
def str_parser(sArgs):
def prsr(oStr):
r = []
r_ap = r.append
stt=0
for lng in sArgs:
end = stt + lng
r_ap(oStr[stt:end])
stt = end
return tuple(r)
return prsr
Str_P = str_parser(spltArgsU)
testList.append("Str_P(tStr)")
print(f"pure bytes \n{''.ljust(60,'-')}")
def byte_parser(sArgs):
def prsr(oBytes):
r, stt = [], 0
r_ap = r.append
for lng in sArgs:
end = stt + lng
r_ap(oBytes[stt:end])
stt = end
return r
return prsr
Byte_P = byte_parser(spltArgs)
testList.append("Byte_P(tBytes)")
print(f"re compile object \n{''.ljust(60,'-')}")
def rebc_parser(sArgs,otype="b"):
re_Args = "".join([f"(.{{{n}}})" for n in sArgs])
if otype == "b":
rebc_Args = re.compile(re_Args.encode("GBK"))
else:
rebc_Args = re.compile(re_Args)
def prsr(oBS):
return rebc_Args.match(oBS).groups()
return prsr
Rebc_P = rebc_parser(spltArgs)
testList.append("Rebc_P(tBytes)")
Rebc_Ps = rebc_parser(spltArgsU,"s")
testList.append("Rebc_Ps(tStr)")
print(f"struct \n{''.ljust(60,'-')}")
import struct
def struct_parser(sArgs):
struct_Args = " ".join(map(lambda x: str(x) + "s", sArgs))
def prsr(oBytes):
return struct.unpack(struct_Args, oBytes)
return prsr
Struct_P = struct_parser(spltArgs)
testList.append("Struct_P(tBytes)")
print(f"List Comprehensions + slice \n{''.ljust(60,'-')}")
import itertools
def slice_parser(sArgs):
tl = tuple(itertools.accumulate(sArgs))
slice_Args = tuple(zip((0,)+tl,tl))
def prsr(oBytes):
return [oBytes[s:e] for s, e in slice_Args]
return prsr
Slice_P = slice_parser(spltArgs)
testList.append("Slice_P(tBytes)")
def sliceObj_parser(sArgs):
tl = tuple(itertools.accumulate(sArgs))
tl2 = tuple(zip((0,)+tl,tl))
sliceObj_Args = tuple(slice(s,e) for s,e in tl2)
def prsr(oBytes):
return [oBytes[so] for so in sliceObj_Args]
return prsr
SliceObj_P = sliceObj_parser(spltArgs)
testList.append("SliceObj_P(tBytes)")
SliceObj_Ps = sliceObj_parser(spltArgsU)
testList.append("SliceObj_Ps(tStr)")
print(f"operator.itemgetter + slice object \n{''.ljust(60,'-')}")
def oprt_parser(sArgs):
sum_arg = tuple(accumulate(abs(i) for i in sArgs))
cuts = tuple(i for i,num in enumerate(sArgs) if num < 0)
ig_Args = tuple(item for i,item in enumerate(zip((0,)+sum_arg,sum_arg)) if i not in cuts)
oprtObj =itemgetter(*[slice(s,e) for s,e in ig_Args])
return oprtObj
Oprt_P = oprt_parser(spltArgs)
testList.append("Oprt_P(tBytes)")
Oprt_Ps = oprt_parser(spltArgsU)
testList.append("Oprt_Ps(tStr)")
print("|".join([s.split("(")[0].center(11," ") for s in testList]))
print("|".join(["".center(11,"-") for s in testList]))
print("|".join([eff2(s,True).rjust(11," ") for s in testList]))
आउटपुट:
Test round = 5 timesI = 100000 sourceLng = 744 argFieldCount = 20
...
...
Str_P | Byte_P | Rebc_P | Rebc_Ps | Struct_P | Slice_P | SliceObj_P|SliceObj_Ps| Oprt_P | Oprt_Ps
-----------|-----------|-----------|-----------|-- ---------|-----------|-----------|-----------|---- -------|-----------
9.6315| 7.5952| 4.4187| 5.6867| 1.5123| 5.2915| 4.2673| 5.7121| 2.4713| 3.9051