├── .gitignore ├── LICENSE ├── README.md └── csvtools.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Francisco Saldaña 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ipython-csvmagic 2 | ================ 3 | 4 | Tools for working with CSV files in IPython. 5 | 6 | To install, in iPython shell: 7 | ``` 8 | %install_ext https://raw.githubusercontent.com/FrankSalad/ipython-csvmagic/master/csvtools.py 9 | %load_ext csvtools 10 | ``` 11 | 12 | ###Importing Data: 13 | a.csv 14 | ``` 15 | "n","order_number","name" 16 | "0","389","John Snow" 17 | "1","405","Princess Bubblegum" 18 | ``` 19 | 20 | iPython: 21 | ``` 22 | In [7]: %loadcsv a.csv 23 | Loaded 2 values in n,order_number,name 24 | 25 | In [8]: n 26 | Out[8]: ['0', '1'] 27 | In [9]: order_number 28 | Out[9]: ['389', '405'] 29 | In [10]: name 30 | Out[10]: ['John Snow', 'Princess Bubblegum'] 31 | ``` 32 | 33 | Also supports prefixing headers: 34 | ``` 35 | In [13]: %loadcsv a.csv col_ 36 | Loaded 2 values in col_n,col_order_number,col_name 37 | 38 | In [14]: col_n 39 | Out[14]: ['0', '1'] 40 | 41 | In [15]: col_order_number 42 | Out[15]: ['389', '405'] 43 | 44 | In [16]: col_name 45 | Out[16]: ['John Snow', 'Princess Bubblegum'] 46 | ``` 47 | 48 | ###Exporting Data: 49 | ``` 50 | In [23]: title = ['The Origin of Species', 'Oryx and Crake', 'Fahrenheit 451'] 51 | 52 | In [24]: name = ['Charles Darwin', 'Margaret Atwood', 'Ray Bradbury'] 53 | 54 | In [25]: n = range(len(title)) 55 | 56 | In [26]: %storecsv b.csv n name title 57 | ``` 58 | 59 | ``` 60 | $ cat b.csv 61 | "n","name","title" 62 | "0","Charles Darwin","The Origin of Species" 63 | "1","Margaret Atwood","Oryx and Crake" 64 | "2","Ray Bradbury","Fahrenheit 451" 65 | ``` 66 | -------------------------------------------------------------------------------- /csvtools.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from IPython.core.magic import Magics, magics_class, line_magic 4 | import csv 5 | import string 6 | import keyword 7 | 8 | 9 | def ident_escape(header, fill='_'): 10 | """ Escapes the column header string to make it a valid Python identifier. 11 | """ 12 | # Reference: https://docs.python.org/2/reference/lexical_analysis.html#identifiers 13 | # Starts with a number 14 | if header[0] in string.digits: 15 | header = fill + header 16 | 17 | if keyword.iskeyword(header): 18 | header = fill + header 19 | 20 | mut = bytearray(header) 21 | valid = set(string.ascii_letters+string.digits) 22 | for i, c in enumerate(header): 23 | if c not in valid: 24 | mut[i] = fill 25 | return str(mut) 26 | 27 | 28 | @magics_class 29 | class CSVMagics(Magics): 30 | @line_magic 31 | def loadcsv(self, line): 32 | """ Loads columns from a CSV into python variables. 33 | Column headers are used as the variable names. 34 | 35 | Usage: 36 | * ``%loadcsv a.csv`` - Loads values of a.csv into lists 37 | named each column header. 38 | * ``%loadcsv a.csv col_`` - Loads values of a.csv into lists 39 | named each column header, prefixed by ``col_`` 40 | """ 41 | ip = self.shell 42 | opts, argsl = self.parse_options(line, 'drz', mode='string') 43 | args = argsl.split() 44 | filepath = args.pop(0) 45 | prefix = args[0] if args else '' 46 | with open(filepath, 'rb') as csvfile: 47 | reader = csv.reader(csvfile) 48 | header = reader.next() 49 | lists = [] 50 | names = [] 51 | for column_name in header: 52 | value_list = [] 53 | lists.append(value_list) 54 | identifier = ident_escape(prefix+column_name) 55 | names.append(identifier) 56 | ip.user_ns[identifier] = value_list 57 | 58 | for row in reader: 59 | for i, val in enumerate(row): 60 | value_list = lists[i] 61 | value_list.append(val) 62 | 63 | print('Loaded', len(lists[0]), 'values in', ','.join(names)) 64 | 65 | @line_magic 66 | def storecsv(self, line): 67 | """ Stores lists of values as columns in a csv file. 68 | 69 | Usage: 70 | ``storecsv a.csv col1 col2 col3`` - Creates a.csv, populates the file 71 | with a header line `"col1","col2","col3"` 72 | and each subsequent line the value 73 | corresponding to that index in each list. 74 | Each list must be the same length. 75 | """ 76 | 77 | ip = self.shell 78 | 79 | opts, argsl = self.parse_options(line, 'drz', mode='string') 80 | args = argsl.split() 81 | filepath = args.pop(0) 82 | 83 | # Check if all args are same length 84 | length = len(ip.user_ns[args[0]]) 85 | for arg in args: 86 | iterable = ip.user_ns[arg] 87 | if len(iterable) != length: 88 | print('len('+arg+') != len('+args[0]+')') 89 | return 90 | 91 | iterables = [] 92 | for arg in args: 93 | iterable = iter(ip.user_ns[arg]) 94 | iterables.append(iterable) 95 | 96 | with open(filepath, 'wb') as csvfile: 97 | quoted = [u'"'+arg+u'"' for arg in args] 98 | csvfile.write(u','.join(quoted).encode('utf8')) 99 | csvfile.write('\n') 100 | 101 | for line in range(length): 102 | row = [u'"'+unicode(iterable.next())+u'"' for iterable in iterables] 103 | csvfile.write(u','.join(row).encode('utf8')) 104 | csvfile.write('\n') 105 | 106 | 107 | def load_ipython_extension(ipython): 108 | ipython.register_magics(CSVMagics) 109 | --------------------------------------------------------------------------------