Skip to content

Instantly share code, notes, and snippets.

@min2bro
Created January 8, 2019 10:28
Show Gist options
  • Save min2bro/94146ad1dcc73867e865e6081a9eaddb to your computer and use it in GitHub Desktop.
Save min2bro/94146ad1dcc73867e865e6081a9eaddb to your computer and use it in GitHub Desktop.
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import numba\n",
"%load_ext Cython\n",
"# df = pd.DataFrame({'a': np.random.randn(1000),\n",
"# 'b': np.random.randn(1000),\n",
"# 'N': np.random.randint(100, 100000, (1000)),\n",
"# 'x': 'x'})"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### How to do faster processing with Pandas\n",
"\n",
"Working with large dataset has always been a challenging and daunting task. The data is growing everyday but the resources and computational power required to process this big data is limited. In last few year we have seen Pandas growing popularity within the data science fraternity and it is one of the widely used python library for data cleaning and processing. However it still works on a single core and every data lover wants to process their data as fast as he or she can and this cannot be achieved unless you know how to write clean, fast and better code in Pandas.\n",
"\n",
" Off late, I learnt this lesson when working with a large dataset with million of rows and more than 100 columns that the basic Pandas code I was using for smaller dataset doesn't perform well on a larger dataset and after much research and learning I thought to summarize my learning in this blog. I have processed around 1.4 million data using all the below methods and recorded the performance for each of these methods. \n",
"\n",
"This is a basic func I used to strip the special character \"&\" from the Text column values and I am going to apply this function on the \"category_name\" column of the dataset\n",
"\n",
"\n",
"# Basic Looping\n",
"\n",
"We all have at one point of time use this basic looping to iterate through each row of the dataframe and pass through a function to get the desired result. This is an easy way to get the things done but it's the most slowest approach when working with large dataset. This should be the last resort when you are left with no other option than basic looping. \n",
"\n",
"I am using the %%timeit function to capture the time it takes to execute a python statement or expression\n",
"\n",
"it took around 2.19 sec with a Standard Deviation of 119 ms. The basic looping doesn't uses any of the pandas or numpy optimization techniques and hence one of the inefficient way to process the data. "
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"df=pd.read_csv('./train.tsv',sep='\\t')\n",
"df['Profit']=np.random.randn(1482535).tolist()\n",
"df['category_name']=df['category_name'].fillna('Other')\n",
"def remove_special_char(x):\n",
" return x.replace('\\\\','')\n",
"\n",
"def remove_special_char_without_numba(x):\n",
" return x.replace('\\\\','')\n",
"\n",
"@numba.jit\n",
"def remove_special_char_with_numba(x):\n",
" return x.replace('\\\\','')"
]
},
{
"cell_type": "code",
"execution_count": 262,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2.19 s ± 119 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
]
}
],
"source": [
"# Crude Looping in Pandas\n",
"%timeit df['category_name'] = remove_special_char(df)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>train_id</th>\n",
" <th>name</th>\n",
" <th>item_condition_id</th>\n",
" <th>category_name</th>\n",
" <th>brand_name</th>\n",
" <th>price</th>\n",
" <th>shipping</th>\n",
" <th>item_description</th>\n",
" <th>Profit</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0</td>\n",
" <td>MLB Cincinnati Reds T Shirt Size XL</td>\n",
" <td>3</td>\n",
" <td>Men/Tops/T-shirts</td>\n",
" <td>NaN</td>\n",
" <td>10.0</td>\n",
" <td>1</td>\n",
" <td>No description yet</td>\n",
" <td>0.796852</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>Razer BlackWidow Chroma Keyboard</td>\n",
" <td>3</td>\n",
" <td>Electronics/Computers &amp; Tablets/Components &amp; P...</td>\n",
" <td>Razer</td>\n",
" <td>52.0</td>\n",
" <td>0</td>\n",
" <td>This keyboard is in great condition and works ...</td>\n",
" <td>-1.326353</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" train_id name item_condition_id \\\n",
"0 0 MLB Cincinnati Reds T Shirt Size XL 3 \n",
"1 1 Razer BlackWidow Chroma Keyboard 3 \n",
"\n",
" category_name brand_name price \\\n",
"0 Men/Tops/T-shirts NaN 10.0 \n",
"1 Electronics/Computers & Tablets/Components & P... Razer 52.0 \n",
"\n",
" shipping item_description Profit \n",
"0 1 No description yet 0.796852 \n",
"1 0 This keyboard is in great condition and works ... -1.326353 "
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Apply Method\n",
"\n",
"This is little better than the basic looping because the object passed to the function is Pandas series object with index as rows (axis=0) or Dataframe column (axis=1) and it returns a new Series or DataFrame object. Even observed the memory consumption was high when using apply over 1.4 million rows. However under the hood this also iterates through the rows of the dataframe but with internal implementation optimization. The result was far better with apply method than the basic looping and an improvement of almost almost 80% is seen, which is pretty fast.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 260,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"437 ms ± 34.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
]
}
],
"source": [
"# Using Apply\n",
"%timeit df['category_name']=df['category_name'].apply(remove_special_char_without_numba) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Using Numba\n",
"\n",
"A recent alternative to statically compiling Cython code, is to use a dynamic jit-compiler, Numba.\n",
"\n",
"As per it documentation, Numba works by generating optimized machine code using the LLVM compiler infrastructure at import time, runtime, or statically (using the included pycc tool). Numba supports compilation of Python to run on either CPU or GPU hardware, and is designed to integrate with the Python scientific software stack.\n",
"\n",
"In order to compile the code with Numba just take the regular python code and annotate with the numba jit - just in time @jit decorator.\n",
"\n",
"We have seen a drastic improvement using numba and the overall time to execute the same code is reduced to 87.2 ms"
]
},
{
"cell_type": "code",
"execution_count": 264,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"87.2 ms ± 1.83 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
]
}
],
"source": [
"# Using numba\n",
"%timeit df['category_name'] = remove_special_char_with_numba(df['category_name'])"
]
},
{
"cell_type": "code",
"execution_count": 236,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"17.5 ms ± 1.16 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%timeit df['half_profit']=df['Profit']/2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Pandas eval and query\n",
"\n",
"Pandas eval is used for expression evaluation of Series and DataFrame objects. it evaluates the boolean and arithmetic operations with the speed of C without costly allocation of intermediate arrays. it relies on the Numexpr package, which is fast numerical expression evaluator for NumPy."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import pandas as pd\n",
"nrows, ncols = 100000, 2\n",
"rng = np.random.RandomState(42)\n",
"df1, df2, df3 = (pd.DataFrame(rng.rand(nrows, ncols))\n",
" for i in range(3))"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>start</th>\n",
" <th>end</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.374540</td>\n",
" <td>0.950714</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0.731994</td>\n",
" <td>0.598658</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0.156019</td>\n",
" <td>0.155995</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>0.058084</td>\n",
" <td>0.866176</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>0.601115</td>\n",
" <td>0.708073</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" start end\n",
"0 0.374540 0.950714\n",
"1 0.731994 0.598658\n",
"2 0.156019 0.155995\n",
"3 0.058084 0.866176\n",
"4 0.601115 0.708073"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df1.columns = ['start','end']\n",
"df1.head()"
]
},
{
"cell_type": "code",
"execution_count": 266,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"102 ms ± 10.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
]
}
],
"source": [
"%timeit df1 + df2 + df3"
]
},
{
"cell_type": "code",
"execution_count": 267,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"60.3 ms ± 5.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
]
}
],
"source": [
"%timeit pd.eval('df1 + df2 + df3')"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"### Query\n",
"\n",
"This uses the top-level pandas.eval() function to evaluate the passed query. \n",
"\n",
"df.query() is basically df[df.eval()]\n",
"\n",
"It also uses numexpr library. You can read more about this function and how it works under the hood in the official pandas documentation here(provide link)"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4.74 ms ± 800 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%timeit df1.query('start < 0.5 and end < 0.5')"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2.33 ms ± 353 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%timeit df1.loc[(df1['start'] < 0.5) & (df1['end'] < 0.5)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Using Numexpr\n",
"\n",
"Fast numerical expression evaluator for NumPy. it uses less memory compared to any other methods discussed above and way faster than any of these methods. it performs well on large array because it doesn't access memory for intermediate results and uses cache utilization and hence reduces the memory access. This is the main reason Numexpr gets the best of your machine computing capability for array operations on very large datasets.\n",
"\n",
"Here is a comparison of eval, Query, Numpy Vectorization, Numexpr"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"===Numexpr===\n",
"1.7 ms ± 312 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n",
"===Query===\n",
"3.38 ms ± 667 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n",
"===Eval===\n",
"2.64 ms ± 47.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"import numexpr as nex\n",
"import pandas as pd\n",
"\n",
"\n",
"np.random.seed(125)\n",
"N = 100000\n",
"df = pd.DataFrame({'Arc':np.random.randint(10, size=N)})\n",
"\n",
"def ne(df):\n",
" x = df.Arc.values\n",
" return df[nex.evaluate('(x > 5)')]\n",
"\n",
"print(\"===Numexpr===\")\n",
"%timeit (ne(df))\n",
"\n",
"# print('===Vectorization with NumPy arrays===')\n",
"# %timeit df[df.A.values > 5]\n",
"\n",
"print(\"===Query===\")\n",
"%timeit df.query('Arc > 10')\n",
"\n",
"\n",
"print(\"===Eval===\")\n",
"%timeit df[df.eval('Arc > 10')]\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Although you can see Vectorization with NumPy arrays also performed well but the Numexpr seems to be a winner here. The least time taken is 634ms with a standard deviation of 22.4 ms"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n",
" 0%| | 0/23 [00:00<?, ?it/s]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.71it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.95it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 2.65it/s]\n",
"\n",
"\n",
" 4%|███▌ | 1/23 [00:01<00:32, 1.49s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 3.40it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.10it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.99it/s]\n",
"\n",
"\n",
" 9%|███████▏ | 2/23 [00:03<00:35, 1.67s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.45it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.00it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.72it/s]\n",
"\n",
"\n",
" 13%|██████████▊ | 3/23 [00:05<00:37, 1.87s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.24it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.23it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.90it/s]\n",
"\n",
"\n",
" 17%|██████████████▍ | 4/23 [00:07<00:36, 1.93s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.65it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.86it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 2.39it/s]\n",
"\n",
"\n",
" 22%|██████████████████ | 5/23 [00:09<00:33, 1.84s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.30it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.98it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 2.02it/s]\n",
"\n",
"\n",
" 26%|█████████████████████▋ | 6/23 [00:11<00:32, 1.90s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.44it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.71it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.63it/s]\n",
"\n",
"\n",
" 30%|█████████████████████████▎ | 7/23 [00:14<00:33, 2.08s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.19it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.90it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.73it/s]\n",
"\n",
"\n",
" 35%|████████████████████████████▊ | 8/23 [00:16<00:32, 2.15s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.28it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.09it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 2.03it/s]\n",
"\n",
"\n",
" 39%|████████████████████████████████▍ | 9/23 [00:18<00:29, 2.10s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:01, 2.46it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:00, 2.01it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.81it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.68it/s]\n",
"\n",
"\n",
" 43%|███████████████████████████████████▋ | 10/23 [00:20<00:28, 2.23s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:00, 8.83it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 5.80it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 3.20it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 3.14it/s]\n",
"\n",
"\n",
" 48%|███████████████████████████████████████▏ | 11/23 [00:22<00:23, 1.98s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:01, 2.39it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.82it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 2.17it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 2.38it/s]\n",
"\n",
"\n",
" 52%|██████████████████████████████████████████▊ | 12/23 [00:24<00:20, 1.89s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.34it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.49it/s]\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.74it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:01<00:00, 1.94it/s]\n",
"\n",
"\n",
" 57%|██████████████████████████████████████████████▎ | 13/23 [00:26<00:19, 1.92s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.46it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:01, 1.88it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.60it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.50it/s]\n",
"\n",
"\n",
" 61%|█████████████████████████████████████████████████▉ | 14/23 [00:28<00:18, 2.09s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:01, 2.93it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:00<00:00, 2.45it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:01<00:00, 1.66it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.47it/s]\n",
"\n",
"\n",
" 65%|█████████████████████████████████████████████████████▍ | 15/23 [00:31<00:18, 2.32s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.09it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.20it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.29it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00, 1.37it/s]\n",
"\n",
"\n",
" 70%|█████████████████████████████████████████████████████████ | 16/23 [00:34<00:17, 2.48s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:01, 2.39it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.97it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.61it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.31it/s]\n",
"\n",
"\n",
" 74%|████████████████████████████████████████████████████████████▌ | 17/23 [00:37<00:16, 2.68s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.25it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.32it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.16it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.12it/s]\n",
"\n",
"\n",
" 78%|████████████████████████████████████████████████████████████████▏ | 18/23 [00:40<00:14, 2.94s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.31it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.34it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.19it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.11it/s]\n",
"\n",
"\n",
" 83%|███████████████████████████████████████████████████████████████████▋ | 19/23 [00:44<00:12, 3.14s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.05it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.07it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.14it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.07it/s]\n",
"\n",
"\n",
" 87%|███████████████████████████████████████████████████████████████████████▎ | 20/23 [00:48<00:09, 3.31s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.12it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.18it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.14it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.12it/s]\n",
"\n",
"\n",
" 91%|██████████████████████████████████████████████████████████████████████████▊ | 21/23 [00:51<00:06, 3.39s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.12it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.18it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.17it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.17it/s]\n",
"\n",
"\n",
" 96%|██████████████████████████████████████████████████████████████████████████████▍ | 22/23 [00:55<00:03, 3.43s/it]\n",
"\n",
" 0%| | 0/4 [00:00<?, ?it/s]\n",
"\n",
" 25%|█████████████████████ | 1/4 [00:00<00:02, 1.14it/s]\n",
"\n",
" 50%|██████████████████████████████████████████ | 2/4 [00:01<00:01, 1.14it/s]\n",
"\n",
" 75%|███████████████████████████████████████████████████████████████ | 3/4 [00:02<00:00, 1.28it/s]\n",
"\n",
"100%|████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:03<00:00, 1.20it/s]\n",
"\n",
"\n",
"100%|██████████████████████████████████████████████████████████████████████████████████| 23/23 [00:58<00:00, 3.47s/it]\n"
]
},
{
"data": {
"image/png": "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\nrkJggg==\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x22ece1b9588>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numexpr\n",
"import numpy as np\n",
"import perfplot\n",
"\n",
"np.random.seed(125)\n",
"\n",
"def ne(x):\n",
" x = x.A.values\n",
" return x[numexpr.evaluate('(x > 5)')]\n",
"\n",
"def be(x):\n",
" return x[x.A > 5]\n",
"\n",
"def q(x):\n",
" return x.query('A > 5')\n",
"\n",
"def ev(x):\n",
" return x[x.eval('A > 5')]\n",
"\n",
"\n",
"def make_df(n):\n",
" df = pd.DataFrame(np.random.randint(10, size=n), columns=['A'])\n",
" return df\n",
"\n",
"\n",
"perfplot.show(\n",
" setup=make_df,\n",
" kernels=[ne, be, q, ev],\n",
" n_range=[2**k for k in range(2, 25)],\n",
" logx=True,\n",
" logy=True,\n",
" equality_check=False, \n",
" xlabel='len(df)')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment