This tutorial have a lot of examples (and some notes/implementation details in
the end). All examples you'll read here are available to you - see examples
folder. You can also learn more details about implementation reading the tests
at tests/test_*.py
.
If you find any kind of bug, error, have a suggestion, doubt or want to contribute with code or pay me a beer, please contact me. You can follow the development of this code in outputty on GitHub.
Enjoy! :-)
A Table
is simply a list of rows. These rows can be represented as
dict
-like, list
-like or tuple
-like objects. Let's create one
Table
with some rows and print it to stdout.
If you have this code, like in examples/01_table.py:
from outputty import Table my_table = Table(headers=['First Name', 'Last Name', 'Main Language']) my_table.append({'First Name': 'Álvaro', 'Last Name': 'Justen', 'Main Language': 'Python'}) #appending row as dict my_table.append(('Flávio', 'Amieiro', 'Python')) #appending row as tuple my_table.append(['Flávio', 'Coelho', 'Python']) #appending row as list print my_table
After executing it, you'll get this output:
+------------+-----------+---------------+ | First Name | Last Name | Main Language | +------------+-----------+---------------+ | Álvaro | Justen | Python | | Flávio | Amieiro | Python | | Flávio | Coelho | Python | +------------+-----------+---------------+
Using plugins we can import and export Table
data to CSV (really, to and
from a lot of formats). Let's create a simple table and export it to a CSV
file.
You can also create any kind of DSV (delimiter-separeted value) files, just
passing delimiter
, quote_char
and line_terminator
to write
(the
same parameters apply to read
).
If you have this code, like in examples/02_table_to_csv.py:
from outputty import Table my_table = Table(headers=['First name', 'Last name']) my_table.append({'First name': 'Álvaro', 'Last name': 'Justen'}) my_table.append(('Flávio', 'Amieiro')) my_table.append(['Flávio', 'Coelho']) my_table.write('csv', 'my-data.csv') #Let's create a other kind of DSV: my_table.write('csv', 'my-data.dsv', delimiter=';', quote_char="'", line_terminator='\r\n')
The file my-data.csv will be created with this content:
"First name","Last name" "Álvaro","Justen" "Flávio","Amieiro" "Flávio","Coelho"
The file my-data.dsv will be created with this content:
'First name';'Last name' 'Álvaro';'Justen' 'Flávio';'Amieiro' 'Flávio';'Coelho'
We can also import data from a CSV file and export it to a text file (using
plugins, again). The data written to the text file will be the same we saw
when executed print my_table
in Example 1.
If you have the file nice-software.csv
with these contents:
id,name,website 1,Python,http://www.python.org/ 2,OpenSSH,http://www.openssh.com/ 3,fabric,http://fabfile.org/
and do you have the code below, like in examples/03_table_to_text_file.py
:
from outputty import Table my_table = Table() my_table.read('csv', 'nice-software.csv') my_table.write('text', 'nice-software.txt')
The file nice-software.txt will be created with this content:
+----+---------+-------------------------+ | id | name | website | +----+---------+-------------------------+ | 1 | Python | http://www.python.org/ | | 2 | OpenSSH | http://www.openssh.com/ | | 3 | fabric | http://fabfile.org/ | +----+---------+-------------------------+
You can order your table's data with the method Table.order_by
.
You need to specify a column in which the ordering will be based on and
optionally specify if the ordering will be ascending (default) or descending.
If you have this code, like in examples/04_order_by.py:
from outputty import Table my_table = Table(headers=['First name', 'Last name']) my_table.append({'First name': 'Álvaro', 'Last name': 'Justen'}) my_table.append({'First name': 'Renne'}) my_table.append(('Flávio', 'Amieiro')) my_table.order_by('Last name') print my_table
After executing it, you'll get this output:
+------------+-----------+ | First name | Last name | +------------+-----------+ | Renne | None | | Flávio | Amieiro | | Álvaro | Justen | +------------+-----------+
You can export your data to HTML using the plugin HTML (that is shipped by
default with outputty
). If you don't specify a filename, the HTML plugin
will return a string (encoded with output_encoding
, specified in
Table.__init__
). If it receives the filename, the contents will be saved
into it and it'll return nothing.
If you have this code, like in examples/05_table_to_html_file.py:
from outputty import Table my_table = Table() my_table.read('csv', 'nice-software.csv') my_table.write('html', 'nice-software.html')
The file nice-software.html will be created with this content:
<table> <thead> <tr class="header"> <th>id</th> <th>name</th> <th>website</th> </tr> </thead> <tbody> <tr class="odd"> <td>1</td> <td>Python</td> <td>http://www.python.org/</td> </tr> <tr class="even"> <td>2</td> <td>OpenSSH</td> <td>http://www.openssh.com/</td> </tr> <tr class="odd"> <td>3</td> <td>fabric</td> <td>http://fabfile.org/</td> </tr> </tbody> </table>
There is a plugin called histogram
that is shipped by default with
outputty
- it can create histograms of your table's columns (using
numpy
). The output will be the histogram represented as text.
If you have this code, like in examples/06_histogram.py:
from numpy.random import normal from numpy.random import seed from outputty import Table seed(1234) distribution = normal(size=1000) my_table = Table(headers=['numbers']) my_table.extend([[value] for value in distribution]) print 'Vertical:' print my_table.write('histogram', 'numbers', 'vertical', bins=10, height=7) print print 'Horizontal:' print my_table.write('histogram', 'numbers', 'horizontal', bins=10, height=7, character='#')
After executing it, you'll get this output:
Vertical: 265 | | ||| ||| |||| ||||| ||||||| -3.56 2.76 Horizontal: 265 -3.56: -2.93: -2.30: # -1.67: ## -1.03: ##### -0.40: ####### 0.23 : ##### 0.87 : ### 1.50 : # 2.13 :
You can get an entire table column just getting the item column-name
in
your table object. You can also change and delete an entire column.
If the item you get is a string, a column is returned. If it is an integer, a
row is returned (starting from 0). Table
objects are iterable, so you can
navigate through the rows with a simple for
loop.
If you have this code, like in examples/07_table_columns.py:
from outputty import Table table = Table(headers=['spam', 'eggs', 'ham']) table.append(['python', 3.14, 1 + 5j]) table.append(['rules', 42, 3 + 4j]) del table['eggs'] print 'Table after deleting "eggs" column:' print table print '\nNow only column "spam":' print table['spam'] print 'First row:' print table[0] print 'All rows:' for index, row in enumerate(table): print ' Row #%d: %s' % (index, row) table['ham'] = [1, 2] # Setting new values for this column print 'Table after chaning an entire column:' print table
After executing it, you'll get this output:
Table after deleting "eggs" column: +--------+--------+ | spam | ham | +--------+--------+ | python | (1+5j) | | rules | (3+4j) | +--------+--------+ Now only column "spam": [u'python', u'rules'] First row: [u'python', (1+5j)] All rows: Row #0: [u'python', (1+5j)] Row #1: [u'rules', (3+4j)] Table after chaning an entire column: +--------+-----+ | spam | ham | +--------+-----+ | python | 1 | | rules | 2 | +--------+-----+
A Table
is implemented as a list of rows with some methods to use plugins,
ordering and do other things. Table
have all operations/methods other
Python mutable sequence objects have so you can use slicing,
Table.extend
, Table.index
, Table.count
and so on. The exception is
sort
(Table
have order_by
instead).
Read more:
mutable sequence operations.
If you have this code, like in examples/08_table_methods.py:
from outputty import Table table = Table(headers=['City', 'State', 'Country']) table.append(['Três Rios', 'Rio de Janeiro', 'Brazil']) table.append(['Niterói', 'Rio de Janeiro', 'Brazil']) table.append(['Rio de Janeiro', 'Rio de Janeiro', 'Brazil']) table.append(['Porto Alegre', 'Rio Grande do Sul', 'Brazil']) table.append(['São Paulo', 'São Paulo', 'Brazil']) print 'First 3 rows:' for row in table[:3]: # Slicing print row #Change the two last rows: table[-2:] = [['Junín', 'Buenos Aires', 'Argentina'], ['Ciudad del Este', 'Alto Paraná', 'Paraguay']] #Insert a row in the first position, using dict notation: table.insert(0, {'City': 'La Paz', 'State': 'La Paz', 'Country': 'Bolivia'}) print 'New table:' print table print table.reverse() print 'And the table in the reversed order:' print table print popped_row = table.pop() rio = ['Rio de Janeiro', 'Rio de Janeiro', 'Brazil'] table.append(rio) #repeated row number_of_rios = table.count(rio) index_of_first_rio = table.index(rio) table.remove(rio) #remove the first occurrence of this row number_of_rows = len(table) print 'Popped row:', popped_row print 'Number of rows:', number_of_rows print 'Count of Rios rows (before remove):', number_of_rios print 'Table after pop and remove:' print table print #Removing non-brazilian cities: del table[:2] #Let's change an entire column: table['Country'] = ['Brasil', 'Brasil', 'Brasil'] print 'Column "Country" changed:' print table
After executing it, you'll get this output:
First 3 rows: [u'Tr\xeas Rios', u'Rio de Janeiro', u'Brazil'] [u'Niter\xf3i', u'Rio de Janeiro', u'Brazil'] [u'Rio de Janeiro', u'Rio de Janeiro', u'Brazil'] New table: +-----------------+----------------+-----------+ | City | State | Country | +-----------------+----------------+-----------+ | La Paz | La Paz | Bolivia | | Três Rios | Rio de Janeiro | Brazil | | Niterói | Rio de Janeiro | Brazil | | Rio de Janeiro | Rio de Janeiro | Brazil | | Junín | Buenos Aires | Argentina | | Ciudad del Este | Alto Paraná | Paraguay | +-----------------+----------------+-----------+ And the table in the reversed order: +-----------------+----------------+-----------+ | City | State | Country | +-----------------+----------------+-----------+ | Ciudad del Este | Alto Paraná | Paraguay | | Junín | Buenos Aires | Argentina | | Rio de Janeiro | Rio de Janeiro | Brazil | | Niterói | Rio de Janeiro | Brazil | | Três Rios | Rio de Janeiro | Brazil | | La Paz | La Paz | Bolivia | +-----------------+----------------+-----------+ Popped row: [u'La Paz', u'La Paz', u'Bolivia'] Number of rows: 5 Count of Rios rows (before remove): 2 Table after pop and remove: +-----------------+----------------+-----------+ | City | State | Country | +-----------------+----------------+-----------+ | Ciudad del Este | Alto Paraná | Paraguay | | Junín | Buenos Aires | Argentina | | Niterói | Rio de Janeiro | Brazil | | Três Rios | Rio de Janeiro | Brazil | | Rio de Janeiro | Rio de Janeiro | Brazil | +-----------------+----------------+-----------+ Column "Country" changed: +----------------+----------------+---------+ | City | State | Country | +----------------+----------------+---------+ | Niterói | Rio de Janeiro | Brasil | | Três Rios | Rio de Janeiro | Brasil | | Rio de Janeiro | Rio de Janeiro | Brasil | +----------------+----------------+---------+
You can append a column in your Table
object using the append_column
method or just setting an item (my_table['new-column'] = ...
). You can
pass a list of values or a function to generate the values based on row data.
Let's see how it works - it's quite simple.
If you have this code, like in examples/09_append_column.py:
from outputty import Table table = Table(headers=['Name', 'Creation Year']) table.append(['Python', 1991]) table.append(['Unix', 1969]) #We have the values, so we'll append it: table.append_column('Category', ['Programming Language', 'Operating System']) #Same effect for this line: #table['Category'] = ['Programming Language', 'Operating System'] #We can also generate the values: table.append_column('Age', lambda row: 2012 - row[1]) #row is a list #Our function can receive row as dict (with `row_as_dict` parameter) and we #can insert the column where we want (with `position` parameter): table.append_column('First Letter', lambda row: row['Name'][0], row_as_dict=True, position=0) #row is dict #...and the result: print table
After executing it, you'll get this output:
+--------------+--------+---------------+----------------------+-----+ | First Letter | Name | Creation Year | Category | Age | +--------------+--------+---------------+----------------------+-----+ | P | Python | 1991 | Programming Language | 21 | | U | Unix | 1969 | Operating System | 43 | +--------------+--------+---------------+----------------------+-----+
It's easy to import data from and export data to a MySQL table.
outputty
automatically identify type of data and creates a table in MySQL
for you with correct data types, so don't worry about converting everyting.
Let's create a simple table, export it to MySQL and then import it again.
Note: you need to change connection_string
before run it.
If you have this code, like in examples/10_plugin_mysql.py:
from outputty import Table from random import randint # The connection string should be in the format: # 'username:password@server[:port]/database/table_name' connection_string = 'root:r00t@localhost/testing/test_table_' + \ str(randint(0, 99999)) my_table = Table(headers=['ID', 'First name', 'Last name']) my_table.append({'First name': 'Álvaro', 'Last name': 'Justen', 'ID': '123'}) my_table.append((456, 'Flávio', 'Amieiro')) my_table.append(['789', 'Flávio', 'Coelho']) my_table.write('mysql', connection_string) print 'Table saved:' print my_table print 'The types identified are:', my_table.types other_table = Table() other_table.read('mysql', connection_string) print print 'Table retrieved:' print other_table
After executing it, you'll get this output:
Table saved: +-----+------------+-----------+ | ID | First name | Last name | +-----+------------+-----------+ | 123 | Álvaro | Justen | | 456 | Flávio | Amieiro | | 789 | Flávio | Coelho | +-----+------------+-----------+ The types identified are: {u'Last name': <type 'str'>, u'First name': <type 'str'>, u'ID': <type 'int'>} Table retrieved: +-----+------------+-----------+ | ID | First_name | Last_name | +-----+------------+-----------+ | 123 | Álvaro | Justen | | 456 | Flávio | Amieiro | | 789 | Flávio | Coelho | +-----+------------+-----------+
Received strings are decoded using UTF-8 and output is encoded also using
UTF-8 by default. You can change this behaviour with the parameters
input_encoding
and output_encoding
to Table
, for example:
my_table = Table(headers=['Column 1', 'Column 2'], input_encoding='iso-8859-1', output_encoding='utf16')
You can also get the table string decoded, in unicode:
table_in_unicode = unicode(my_table)
We have two kinds of normalization in Table
:
.normalize_types()
: used by default when importing from CSV, this method convert table rows to the types it identify. All data that in first moment are strings will be converted tounicode
,int
,float
,datetime.date
ordatetime.datetime
when identified.
unicode
normalization: all operations inTable
(import from some format, output table in some format, normalization and ordering) will convert data internally tounicode
usinginput_encoding
as codec (passed inTable.__init__
). Whenconvert_types=False
, all row's values will beunicode
, otherwise only types identified as string will be converted tounicode
.
If you want to access all table rows as dicts, just convert it using the
method to_list_of_dicts
. Using the same table from Example 1, if we
execute:
rows = my_table.to_list_of_dicts() print rows[1]['First Name']
...it'll print:
Flávio
You'll receive this data encoded with output_encoding
. If you need it as
unicode just pass unicode=True
to this method.
You can also convert your table to a dict
, with header names as keys and
columns as values and filter which columns will go to the dictionary:
table_dict = my_table.to_dict() print table_dict table_dict_filtered = my_table.to_dict(only=['First Name', 'Last Name']) print table_dict_filtered
...will print:
{'Last Name': (u'Justen', u'Amieiro'), 'First Name': (u'\xc1lvaro', u'Fl\xe1vio'), 'Main Language': (u'Python', u'Python')} {'Last Name': (u'Justen', u'Amieiro'), 'First Name': (u'\xc1lvaro', u'Fl\xe1vio')}
And if you want to create a dict
with some column value as key and other
column value as value you can specify key
and value
parameters, as in:
other_table = Table(headers=['date', 'measure']) other_table.append(('2011-12-01', 21)) other_table.append(('2011-12-02', 42)) other_table.append(('2011-12-03', 3.14)) other_table.append(('2011-12-04', 2.71)) values_as_dict = other_table.to_dict(key='date', value='measure') print values_as_dict
...that produces:
{'2011-12-04': 2.71, '2011-12-03': 3.14, '2011-12-02': 42, '2011-12-01': 21}