Python, Org Mode, and writing Org tables to CSVs so that I can read them back
| emacs, org, writing, babel, pythonI’ve been getting deeper into Python so that I can model our personal finances. I really like using the pandas
library to manipulate data. All those years I spent trying to juggle increasing complex spreadsheets… Working with Python code in Org Babel blocks is just so much more fun. I like being able to keep my assumptions in tables without having to fuss around with naming cells for easy-to-read formulas, slice and summarize parts of my data frames, organize my notes in outlines and add commentary, and define more complicated functions that I don’t have to squeeze into a single line.
I haven’t quite been able to tempt W- into the world of Org Babel Python blocks. Still, I don’t want to give up the awesomeness of having pretty tables that I can easily edit and use. So I have a bunch of named tables (using #+NAME:
), and some code that exports my tables to CSVs:
#+NAME: tables
| Table | Key |
|---------------+---------------------|
| assets_w | Description |
| assets_s | Description |
| tax_rates | |
| disposition | Asset |
| probate_rates | Asset |
| basic | Client information |
| base_expenses | Category |
| general | General assumptions |
#+begin_src emacs-lisp :results silent :var tables=tables :tangle no
(defun my-tbl-export (row)
"Search for table named `NAME` and export."
(interactive "s")
(save-excursion
(goto-char (point-min))
(let ((case-fold-search t))
(when (search-forward-regexp (concat "#\\+NAME: +" (car row)) nil t)
(next-line)
(org-table-export (format "%s.csv" (car row)) "orgtbl-to-csv")))))
(mapc 'my-tbl-export tables)
#+end_src
and some code that imports them back in, and formats tables nicely if I’m displaying them in Org. The in_org
block doesn’t get tangled into index.py, so I don’t clutter command-line use with Org table markup.
#+begin_src python :results silent :tangle no
in_org=1
#+end_src
#+begin_src python :results silent :exports code
import pandas as pd
import numpy as np
import orgbabelhelper as ob
def out(df, **kwargs):
if 'in_org' in globals():
print(ob.dataframe_to_orgtable(df, **kwargs))
else:
print(df)
return df
#+end_src
#+begin_src python :results silent :var tables=tables :colnames yes
for row in tables:
table = row[0]
index = row[1]
if row[1] == '':
index = None
globals()[table] = pd.read_csv(table + '.csv', index_col=index).apply(pd.to_numeric, errors='ignore')
# print(globals()[table])
#+end_src
Then I can use C-c C-v C-b
(org-babel-execute-buffer
) to update everything if I change the table in my Org file, and I can use C-c C-v C-t
(org-babel-tangle
) to create an index.py that W- can read through or run without needing Org.