I first made a topic on SO to ask *why* my script is so slow. I then tried to figure out the suggestions to improve the performance of the script. Unfortunately, I am incredibly new to Python but I have some background in PHP and JS. In this post I'll first explain *what the script does* and then I'll ask you to improve the existing code. **Example data and script can be downloaded below.** I am working on a project which crunches plain text files (.lst). The name of the file names (`fileName`) are important because I'll extract `node` (e.g. *abessijn*) and `component` (e.g. WR-P-E-A) from them into a dataframe. Examples: abessijn.WR-P-E-A.lst A-bom.WR-P-E-A.lst acroniem.WR-P-E-C.lst acroniem.WR-P-E-G.lst adapter.WR-P-E-A.lst adapter.WR-P-E-C.lst adapter.WR-P-E-G.lst Each file consists of one or more line. Each line consists of a sentence (inside `<sentence>` tags). Example (abessijn.WR-P-E-A.lst) /home/nobackup/SONAR/COMPACT/WR-P-E-A/WR-P-E-A0000364.data.ids.xml: <sentence>Vooral mijn abessijn ruikt heerlijk kruidig .. : ) )</sentence> /home/nobackup/SONAR/COMPACT/WR-P-E-A/WR-P-E-A0000364.data.ids.xml: <sentence>Mijn abessijn denkt daar heel anders over .. : ) ) Maar mijn kinderen richt ik ook niet af , zit niet in mijn bloed .</sentence> From each line I extract the sentence, do some small modifications to it, and call it `sentence`. Up next is an element called `leftContext`, which takes the first part of the split between `node` (e.g. *abessijn*) and the sentence it came from. Finally, from `leftContext` I get precedingWord, which is the word preceding `node` in `sentence`, or the right most word in `leftContext` (with some limitations such as the option of a compound formed with a hyphen). Example: ID | filename | node | component | precedingWord | leftContext | sentence --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1 adapter.WR-P-P-F.lst adapter WR-P-P-F aanpassingseenheid Een aanpassingseenheid ( Een aanpassingseenheid ( adapter ) , 2 adapter.WR-P-P-F.lst adapter WR-P-P-F toestel Het toestel ( Het toestel ( adapter ) draagt zorg voor de overbrenging van gegevens 3 adapter.WR-P-P-F.lst adapter WR-P-P-F de de aansluiting tussen de sensor en de de aansluiting tussen de sensor en de adapter , 4 airbag.WS-U-E-A.lst airbag WS-U-E-A den ja voor den ja voor den airbag op te pompen eh :p 5 airbag.WS-U-E-A.lst airbag WS-U-E-A ne Dobby , als ze valt heeft ze dan wel al ne Dobby , als ze valt heeft ze dan wel al ne airbag hee That dataframe is exported as dataset.csv. After that, the intention of my project comes at hand: I create a frequency table that takes `node` and `precedingWord` into account. From a variable I define `neuter` and `non_neuter`, e.g (in Python) neuter = ["het", "Het"] non_neuter = ["de","De"] and a rest category `unspecified`. When `precedingWord` is an item from the list, assign it to the variable. Example of a frequency table output: node | neuter | nonNeuter | unspecified ------------------------------------------------- A-bom 0 4 2 acroniem 3 0 2 act 3 2 1 The frequency list is exported as frequencies.csv. Finally I came up with the following script ([paste][2]) to run. import os, pandas as pd, numpy as np, regex as re from glob import glob from datetime import datetime from html import unescape start_time = datetime.now() # Create empty dataframe with correct column names columnNames = ["fileName", "component", "precedingWord", "node", "leftContext", "sentence" ] df = pd.DataFrame(data=np.zeros((0,len(columnNames))), columns=columnNames) # Create correct path where to fetch files subdir = "rawdata" path = os.path.abspath(os.path.join(os.getcwd(), os.pardir, subdir)) # "Cache" regex # See http://stackoverflow.com/q/452104/1150683 p_filename = re.compile(r"[./\\]") p_sentence = re.compile(r"<sentence>(.*?)</sentence>") p_typography = re.compile(r" (?:(?=[.,:;?!) ])|(?<=\( ))") p_non_graph = re.compile(r"[^\x21-\x7E\s]") p_quote = re.compile(r"\"") p_ellipsis = re.compile(r"\.{3}(?=[^ ])") p_last_word = re.compile(r"^.*\b(?<!-)(\w+(?:-\w+)*)[^\w]*$", re.U) # Loop files in folder for file in glob(path+"\\*.lst"): with open(file, encoding="utf-8") as f: [n, c] = p_filename.split(file.lower())[-3:-1] fn = ".".join([n, c]) for line in f: s = p_sentence.search(unescape(line)).group(1) s = s.lower() s = p_typography.sub("", s) s = p_non_graph.sub("", s) s = p_quote.sub("'", s) s = p_ellipsis.sub("... ", s) if n in re.split(r"[ :?.,]", s): lc = re.split(r"(^| )" + n + "( |[!\",.:;?})\]])", s)[0] pw = p_last_word.sub("\\1", lc) df = df.append([dict(fileName=fn, component=c, precedingWord=pw, node=n, leftContext=lc, sentence=s)]) continue # Reset indices df.reset_index(drop=True, inplace=True) # Export dataset df.to_csv("dataset/py-dataset.csv", sep="\t", encoding="utf-8") # Let's make a frequency list # Create new dataframe # Define neuter and non_neuter neuter = ["het"] non_neuter = ["de"] # Create crosstab df.loc[df.precedingWord.isin(neuter), "gender"] = "neuter" df.loc[df.precedingWord.isin(non_neuter), "gender"] = "non_neuter" df.loc[df.precedingWord.isin(neuter + non_neuter)==0, "gender"] = "rest" freqDf = pd.crosstab(df.node, df.gender) freqDf.to_csv("dataset/py-frequencies.csv", sep="\t", encoding="utf-8") # How long has the script been running? time_difference = datetime.now() - start_time print("Time difference of", time_difference) I am running on Windows 10 64 bit with a quad-core processor and 8 GB Ram. Python runs on version *3.4.3* (Anaconda) and is executed in Spyder. Note that I'm running Python in 32 bit because I'd like to use the [nltk module][3] in the future and they discourage users to use 64 bit. Seeing that the goal of Python was to make everything go smoothly, I was confused. **The goal is to beat a similar R script in execution speed.** I read Python was fast, so where did I go wrong? What is the problem? Is Python slower in reading files and lines, or in doing regexes? Or is R simply better equipped to dealing with dataframes and can't it be beaten by pandas? *Or* is my code simply badly optimised and should Python indeed be the victor? Everyone who is willing to give the script a try can download the test data I used [**here**][4]. Please give me a heads-up when you downloaded the files. Please, if you take the time to re-write my efforts, do make comments in the script explaining what code does what. I am very new to this language and I like to learn more. [1]: http://pastebin.com/gtCrAdMq [2]: http://pastebin.com/rPFJKuBV [3]: http://www.nltk.org/ [4]: http://bramvanroy.be/files/testdata.zip