Python: Read CSV to list is not faster then CSV to dict with key check?

Viewed 981

While looking for alternative to SAS sort I decided to try Python 2.6 (both on the same Unix server). Sorting a narrow 500mln rows table takes 20 minutes in SAS. I exported 20% of the table (100mln rows) to CSV file which looks like this:

X|||465097434|912364420|0.00|0.00|0.00|0.00|1.00|01FEB2016|X|0|0
X|||465097434|912364420|0.00|0.00|0.00|0.00|0.00|02FEB2016|X|0|0
X|||465097434|912364420|0.00|0.00|0.00|0.00|2.00|03FEB2016|X|0|0
X|||465097434|912364421|0.00|0.00|0.00|0.00|3.00|04FEB2016|X|0|0
X|||465097434|912364421|0.00|0.00|0.00|0.00|6.00|05FEB2016|X|0|0
X|||965097411|912364455|0.00|0.00|0.00|0.00|4.00|04FEB2016|X|0|0
X|||965097411|912364455|0.00|0.00|0.00|0.00|1.00|05FEB2016|X|0|0

The goal is to sort it by 5th and 11th columns. First I checked how fast python read the file with the code:

from __future__ import print_function
import csv
import time
linesRead=0
with open ('/path/to/file/CSV_FILE.csv','r') as dailyFile:
    allLines=csv.DictReader(dailyFile, delimiter='|')
    startTime=time.time()
    for row in allLines:
        linesRead += 1
        if (linesRead) % 1000000 == 0:
            print(linesRead, ": ", time.time()-startTime, " sec.")
            startTime=time.time()

And the results is that it takes 6 seconds to read each mln of rows.

1000000 :  6.6301009655  sec.
2000000 :  6.33900094032  sec.
3000000 :  6.26246404648  sec.
4000000 :  6.56919789314  sec.
5000000 :  6.17433309555  sec.
...
98000000 :  6.61627292633  sec.
99000000 :  7.14683485031  sec.
100000000 :  7.08069109917  sec.

So I extended the code to load it to dictionary (key=column 5 (account identifier)) and value is a list of lists (lines) for this account. This is where I realized that loading lists to a dictionary slows down while dictionary is growing (quite logic as there is a growing number of keys to check):

import csv
import time
myDictionary = {}
linesRead=0
with open ('/path/to/file/CSV_FILE.csv','r') as dailyFile:
    allLines=csv.DictReader(dailyFile, delimiter='|')
    startTime=time.time()
    for row in allLines:
        accountID=row['account_id'].strip('\'')
        linesRead += 1
        if accountID in myDictionary:
            myDictionary[accountID].append([row['date'].strip('\''), row['balance1'], row['balance2'], row['balance3']])
        else:
            myDictionary[accountID]=[]
        if (linesRead) % 1000000 == 0:
            print(linesRead, ": ", time.time()-startTime, " sec.")
            startTime=time.time()

And the times are:

1000000, ': ', 8.9685721397399902, ' sec.')
(2000000, ': ', 10.344831943511963, ' sec.')
(3000000, ': ', 11.637137889862061, ' sec.')
(4000000, ': ', 13.024128913879395, ' sec.')
(5000000, ': ', 13.508150815963745, ' sec.')
(6000000, ': ', 14.94166088104248, ' sec.')
(7000000, ': ', 16.307464122772217, ' sec.')
(8000000, ': ', 17.130259990692139, ' sec.')
(9000000, ': ', 17.54616379737854, ' sec.')
(10000000, ': ', 20.254321813583374, ' sec.')
...
(39000000, ': ', 55.350741863250732, ' sec.')
(40000000, ': ', 56.762171983718872, ' sec.')
(41000000, ': ', 57.876702070236206, ' sec.')
(42000000, ': ', 54.548398017883301, ' sec.')
(43000000, ': ', 60.040227890014648, ' sec.')

which means that there is no chance to load 500mln rows in reasonable time (last million of 500 millions would be loading 600 seconds). My guess was that the slowest part of each iteration was checking of key existence in dictionary:

if accountID in myDictionary:

So I changed dictionary to list hoping that simple appending will be much faster:

with open ('/path/to/file/CSV_FILE.csv','r') as dailyFile:
    allLines=csv.DictReader(dailyFile, delimiter='|')
    startTime=time.time()
    for row in allLines:
        linesRead += 1
        myList.append([row['account_id'].strip('\''), row['date'].strip('\''), row['balance1'], row['balance2'], row['balance3']])
        if (linesRead) % 1000000 == 0:
            print(linesRead, ": ", time.time()-startTime, " sec.")
            startTime=time.time()

Unfortunately there was no increase in performance at all:

1000000 :  9.15476489067  sec.
2000000 :  10.3512279987  sec.
3000000 :  12.2600080967  sec.
4000000 :  13.5473120213  sec.
5000000 :  14.8431830406  sec.
6000000 :  16.5556428432  sec.
7000000 :  17.6754620075  sec.
8000000 :  19.1299819946  sec.
9000000 :  19.7615978718  sec.
10000000 :  22.5903761387  sec.

Should not loading list be much faster than loading dictionary with key-check at entry?

Am I misusing python to process this kind of data? For comparison I sorted the file in with unix sort command:

$ date ; sort  -t'|' -k5,9 CSV_FILE.csv > delete.txt; date;
Sun Jul 23 18:46:16 CEST 2017
Sun Jul 23 19:06:53 CEST 2017

And it took 20 minutes to do the job. While in python I was unable to load into memory the data.

1 Answers
Related