{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Writing pandas dataframes as fortran-formatted csv files\n", "\n", "Say you have a pandas dataframe, `df`, and you need to write that dataframe as a fortran-readable csv file with a very specific formatting. Here's a simple approach that makes use of the [fortranformat](https://pypi.org/project/fortranformat/) package. \n", "\n", "The following notebook builds up a working example, but if you just want the minimal code snippet to understand, here it is: \n", "\n", "```\n", "import fortranformat as ff\n", "\n", "format_string='(a20, f15.2, f15.2, f15.5 ,i15)'\n", "header_line = ff.FortranRecordWriter(format_string)\n", "Formatted_df=df.apply(lambda x : header_line.write(x.values),axis=1)\n", "```\n", "\n", "The final object, `Formatted_df` will be a `Series` the length of the original dataframe and a given element in the `Series` will be the formatted string for the corresponding row in the dataframe. Once you have that, you can write it to a file with \n", "\n", "```\n", "Formatted_df.to_csv('formatted_df.csv',index=False,header=False)\n", "```\n", "\n", "The rest of this notebook builds a test dataframe, applies the above formatting and outputs it. Throughout, I'm assuming you're already familiar with fortran read/write formatting. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## a working example\n", "\n", "Let's start by importing all the things we'll use and creating a function to build a dataframe with a mix of data types: " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "import fortranformat as ff, pandas as pd, numpy as np\n", "import os, random,string,time\n", "import matplotlib.pyplot as plt \n", "\n", "def buildTestData(Nrows):\n", " ''' build a test dataframe with a mix of strings, integers and floats with number of rows as input''' \n", " samp_names = [] \n", " for sampNum in range(0,Nrows):\n", " Nchars=int(np.random.rand(1)[0]*10)+3 \n", " samp_names.append(''.join(random.choices(string.ascii_uppercase + string.digits, k = Nchars)))\n", " \n", " df = pd.DataFrame({\n", " 'sample_name': samp_names, \n", " 'lat' : np.linspace(30,14,Nrows),\n", " 'lon' : np.linspace(200,230,Nrows),\n", " 'obs' : np.random.random(Nrows),\n", " 'Nobs': (np.random.random(Nrows)*100).astype(int)\n", " })\n", " return df " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
sample_namelatlonobsNobs
06USTN45ZNOR30.000000200.0000000.5184700
1V2A9E29.838384200.3030300.94010639
2A5O1Q94029.676768200.6060610.99302865
3IG98UF029.515152200.9090910.66570358
40WXBCQU67O29.353535201.2121210.84410527
\n", "
" ], "text/plain": [ " sample_name lat lon obs Nobs\n", "0 6USTN45ZNOR 30.000000 200.000000 0.518470 0\n", "1 V2A9E 29.838384 200.303030 0.940106 39\n", "2 A5O1Q940 29.676768 200.606061 0.993028 65\n", "3 IG98UF0 29.515152 200.909091 0.665703 58\n", "4 0WXBCQU67O 29.353535 201.212121 0.844105 27" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = buildTestData(100)\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we initialize the `fortranformat` record writer with a fortan format string: " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "format_string='(a20, f15.2, f15.2, f15.5 ,i15)'\n", "header_line = ff.FortranRecordWriter(format_string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To apply the record writer to a single row of the dataframe, you can do: " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "' 6USTN45ZNOR 30.00 200.00 0.51847 0'" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "header_line.write(df.loc[0].values)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "but we wnat to use it on all the rows. As I show at the end of this notebook, using `df.apply` is faster than manually looping: " ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "Formatted_df=df.apply(lambda x : header_line.write(x.values),axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What comes out is a `pandas.Series` object, where each record is a single row of the original dataframe formatted as a string: " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 6USTN45ZNOR 30.00 20...\n", "1 V2A9E 29.84 20...\n", "2 A5O1Q940 29.68 20...\n", "3 IG98UF0 29.52 20...\n", "4 0WXBCQU67O 29.35 20...\n", "dtype: object" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Formatted_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So to write it out, you can just use `Formatted_df.to_csv`. The following initializes a file with a few header lines containing the colum names and the format string that we used to generate the file: " ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "output_fi='formatted_df.csv'\n", "col_names=df.columns.tolist()\n", "with open(output_fi,'w') as outfi: \n", " outfi.write('# '+' '.join(col_names)+\"\\n\")\n", " outfi.write('# '+format_string+\"\\n\")\n", " \n", "Formatted_df.to_csv(output_fi,mode='a',index=False,header=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So let's take a look at the first 10 lines of the file we just wrote: " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "# sample_name lat lon obs Nobs\n", "\n", "# (a20, f15.2, f15.2, f15.5 ,i15)\n", "\n", " 6USTN45ZNOR 30.00 200.00 0.51847 0\n", "\n", " V2A9E 29.84 200.30 0.94011 39\n", "\n", " A5O1Q940 29.68 200.61 0.99303 65\n", "\n", " IG98UF0 29.52 200.91 0.66570 58\n", "\n", " 0WXBCQU67O 29.35 201.21 0.84411 27\n", "\n", " WVT50 29.19 201.52 0.98179 34\n", "\n", " UQRASDW 29.03 201.82 0.41387 95\n", "\n", " ZN2T 28.87 202.12 0.83129 77\n", "\n" ] } ], "source": [ "outfi = open(output_fi,'r')\n", "for i in range(10): \n", " print(outfi.readline())\n", "outfi.close() " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**To read the file back in**, we can use `pd.read_csv` with `delim_whitespace=True` to signify variable whitespace as the delimiter. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
sample_namelatlonobsNobs
06USTN45ZNOR30.00200.000.518470
1V2A9E29.84200.300.9401139
2A5O1Q94029.68200.610.9930365
3IG98UF029.52200.910.6657058
40WXBCQU67O29.35201.210.8441127
\n", "
" ], "text/plain": [ " sample_name lat lon obs Nobs\n", "0 6USTN45ZNOR 30.00 200.00 0.51847 0\n", "1 V2A9E 29.84 200.30 0.94011 39\n", "2 A5O1Q940 29.68 200.61 0.99303 65\n", "3 IG98UF0 29.52 200.91 0.66570 58\n", "4 0WXBCQU67O 29.35 201.21 0.84411 27" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.read_csv(output_fi,skiprows=2,names=col_names,delim_whitespace=True)\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we didn't already know the column names, we could have parsed the header rows to figure it out.\n", "\n", "**So in summary, the following code does it all:**" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "format_string='(a20, f15.2, f15.2, f15.5 ,i15)'\n", "header_line = ff.FortranRecordWriter(format_string)\n", "\n", "Formatted_df=df.apply(lambda x : header_line.write(x.values),axis=1)\n", "\n", "output_fi='formatted_df.csv'\n", "col_names=df.columns.tolist()\n", "with open(output_fi,'w') as outfi: \n", " outfi.write('# '+' '.join(col_names)+\"\\n\")\n", " outfi.write('# '+format_string+\"\\n\")\n", " \n", "Formatted_df.to_csv(output_fi,mode='a',index=False,header=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## controlling column ordering \n", "One important caveat is that we've assumed that we know the dataframe column ordering. If we didn't, or if we wanted to change the order for writing, we could do the following:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(f15.2 , a20 , f15.2 , f15.5 , i15)\n" ] }, { "data": { "text/plain": [ "0 30.00 6USTN45ZNOR 20...\n", "1 29.84 V2A9E 20...\n", "2 29.68 A5O1Q940 20...\n", "3 29.52 IG98UF0 20...\n", "4 29.35 0WXBCQU67O 20...\n", " ... \n", "95 14.65 078A54H2 22...\n", "96 14.48 22Z4DFF4V 22...\n", "97 14.32 EQ7DYKZB 22...\n", "98 14.16 66B0B24AN 22...\n", "99 14.00 EUZ 23...\n", "Length: 100, dtype: object" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# specifiy format of each as an ordered dict, moving the sample_name to second column\n", "import collections \n", "format_dict=collections.OrderedDict(lat='f15.2',sample_name='a20',lon='f15.2',obs='f15.5',Nobs='i15')\n", "\n", "# build the format string \n", "format_string='('+\" , \".join(format_dict.values()) + ')'\n", "print(format_string)\n", "header_line = ff.FortranRecordWriter(format_string)\n", "\n", "# now use apply, but pull the columns using the ordered dict keys to ensure ordering: \n", "Formatted_df=df.apply(lambda x : header_line.write(x[format_dict.keys()]),axis=1)\n", "Formatted_df\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Is it faster than looping? (yes)\n", "\n", "While using `apply` with a fortranformat `FortranRecordWriter` is nice and tidy, it's not clear that it will be any faster than a manual iteration over the records, as `apply` will end up looping under the hood. So here's a test! The following function captures everything above with an additional argument, `method`. If `method` is `apply`, then we'll build a dataframe of `Nrows` and format it exactly as above. If the method is `iter`, then we'll manually loop over the dataframe and format each row ourselves (using the same fortranformat writer). " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def testCase(Nrows,method='apply'):\n", " df = buildTestData(Nrows)\n", " format_string='(a10, f15.2, f15.2, f15.5 ,i15)'\n", " header_line = ff.FortranRecordWriter(format_string)\n", " \n", " # set up the output file \n", " output_fi='formatted_df.csv'\n", " col_names=df.columns.tolist()\n", " with open(output_fi,'w') as outfi: \n", " outfi.write('# '+' '.join(col_names)+\"\\n\")\n", " outfi.write('# '+format_string+\"\\n\")\n", " \n", " if method=='apply': \n", " Formatted_df=df.apply(lambda x : header_line.write(x.values),axis=1) \n", " Formatted_df.to_csv(output_fi,mode='a',index=False,header=False)\n", " elif method=='iter':\n", " outfi=open(output_fi,'a')\n", " for rowid,vals in df.iterrows(): \n", " outfi.write(header_line.write(vals.values)+\"\\n\" )\n", " outfi.close()\n", " return None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The time savings will probably be most noticeable for large dataframes, so we'll run both methods for a range of dataframe sizes: " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "runTimes={'apply':[],'iter':[],'nrows':[]}\n", "\n", "for nRows in np.logspace(2,6,10).astype('int'):\n", " runTimes['nrows'].append(nRows)\n", " for meth in ['iter','apply']:\n", " time1= time.time()\n", " run=testCase(nRows,method=meth)\n", " runTimes[meth].append(time.time()-time1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now let's plot the computation time of each: " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "f=plt.figure()\n", "for meth in ['iter','apply']:\n", " runTimes[meth]=np.array(runTimes[meth])\n", " plt.semilogx(runTimes['nrows'],runTimes[meth],label=meth,marker='.')\n", "plt.xlabel('N rows') \n", "plt.ylabel('Computation time [s]')\n", "plt.legend() \n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and a speedup factor: " ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "f=plt.figure()\n", "plt.semilogx(runTimes['nrows'],(runTimes['apply'])/runTimes['iter']*100,marker='.')\n", "plt.xlabel('N rows') \n", "plt.ylabel('Apply / Iter Speed ')\n", "plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So above about 1000 rows, `apply` + fortran formatting is about 42% faster than a manual loop, so definitely the way to go. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "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.7" } }, "nbformat": 4, "nbformat_minor": 4 }