A simple package intended to help write iterables of objects to CSV files
Project description
List2CSV
List2CSV is a simple package that helps with writing lists of objects to CSV files.
Installation
list2csv can be downloaded from pypi or installed using pip:
pip install list2csv
Overview
The main class Writer
takes a writable file as a parameter and can have various columns added with format specifiers.
Columns can either be defined by instance attribute names or functions that map an object to some value. For example:
from dataclasses import dataclass
from statistics import mean
import list2csv
@dataclass
class Student:
student_id: str
test_mark: float
lab_marks: list[float]
students = [
Student('abcd123', 78.5, [92.3, 98, 100, 70]),
Student('efgh456', 62, [98, 68.2, 0, 93.5]),
Student('ijkl789', 100, [100, 100, 98.7, 100]),
]
with open('student_overview.csv', 'w') as f:
writer = list2csv.Writer(f)
writer.add_column('ID', 'student_id')
writer.add_column('Test Mark', 'test_mark', '{:.2f}')
writer.add_column('Average Lab Mark', lambda s: mean(s.lab_marks), '{:.2f}')
writer.write_header()
writer.write_all(students)
Would produce the following table:
ID | Test Mark | Average Lab Mark |
---|---|---|
abcd123 | 78.50 | 90.08 |
efgh456 | 62.00 | 64.92 |
ijkl789 | 100.00 | 99.67 |
Fields
Columns, added by .add_column
or .add_multi_column
, take a function that maps an object to a value or a string
that matches the name of an attribute of Type T
.
In the above example, the add_column
method is used to add the student_id
and test_mark
fields; both of which are
attribute names of Student
. But the add field method is also used to add the Average Lab Mark
field, which is _
computed_ using a lambda that maps a Student
instance to a float
representing the average lab mark for that student.
In the case of multi-columns, either a string representing an attribute name or a function that maps an object to an iterable of values can be used.
Writing Iterables
It may be desirable to quickly add iterables as a set of columns to the table. For this, the .add_multi_column
method
can be used. This column expects iterables of a pre-defined length as specified by a range
and will write them as
individual columns. For example, if we wanted to put the lab marks of each student in separate columns, we could do:
with open('student_overview.csv', 'w') as f:
writer = list2csv.Writer(f)
writer.add_column('ID', 'student_id')
writer.add_column('Test Mark', 'test_mark', '{:.2f}')
writer.add_multi_column('Lab {}', 'lab_marks', range(1, 5), '{:.2f}')
writer.write_header()
writer.write_all(students)
Which would yield:
ID | Test Mark | Lab 1 | Lab 2 | Lab 3 | Lab 4 |
---|---|---|---|---|---|
abcd123 | 78.50 | 92.30 | 98.00 | 100.00 | 70.00 |
efgh456 | 62.00 | 98.00 | 68.20 | 0.00 | 93.50 |
ijkl789 | 100.00 | 100.00 | 100.00 | 98.70 | 100.00 |
Here the name
parameter is a generic name that contains exactly one placeholder value. Upon writing the header, the
column names are generated by replacing the placeholder with the index of the column starting at value 1.
The number of values must be predefined. If an item in the resulting iterable does not have the same number of items,
a ValueError
will be raised.
Aggregator Columns
Columns have an aggregate
parameter that indicates the field will be used in an aggregate column. These aggregate
columns can be added using the .add_aggregator
method. For example, if we wanted to add the average lab mark to the
table, we could do:
with open('student_overview.csv', 'w') as f:
writer = list2csv.Writer(f)
writer.add_column('ID', 'student_id')
writer.add_column('Test Mark', 'test_mark', '{:.2f}')
writer.add_multi_column('Lab {}', 'lab_marks',
range(1, 5), '{:.2f}', aggregate=True)
writer.add_aggregator('Average Lab Mark', mean, '{:.2f}')
writer.write_header()
writer.write_all(students)
Which would yield:
ID | Test Mark | Lab 1 | Lab 2 | Lab 3 | Lab 4 | Average Lab Mark |
---|---|---|---|---|---|---|
abcd123 | 78.50 | 92.30 | 98.00 | 100.00 | 70.00 | 90.08 |
efgh456 | 62.00 | 98.00 | 68.20 | 0.00 | 93.50 | 64.92 |
ijkl789 | 100.00 | 100.00 | 100.00 | 98.70 | 100.00 | 99.67 |
In this case, the use of the .add_aggregator
method is trivial; however, it can be applied to several columns making
it useful in some instances. For example, if the 'TestMark' field was also flagged as an aggregate field, this would be
included in the average calculation for the 'Average Lab Mark' field.
The range parameter indicates the indices that will be used in the column headers, as well as how many columns will be expected.
Counters
To add auto incrementing values to the table, the .add_counter
method can be used. This will increment values with a
given start and step value for each row written. For example:
with open('student_overview.csv', 'w') as f:
writer = list2csv.Writer(f)
writer.add_counter('Student', start=1)
writer.add_column('ID', 'student_id')
writer.add_column('Test Mark', 'test_mark', '{:.2f}')
writer.add_column('Average Lab Mark', lambda s: mean(s.lab_marks), '{:.2f}')
writer.write_header()
writer.write_all(students)
Would produce:
Student | ID | Test Mark | Average Lab Mark |
---|---|---|---|
1 | abcd123 | 78.50 | 90.08 |
2 | efgh456 | 62.00 | 64.92 |
3 | ijkl789 | 100.00 | 99.67 |
Here the 'Student' column starts at one and increments by one for each row. Multiple counter columns can be added with different start or step values if desired.
Putting it all together
The following example adds a more complex student representation to demonstrate how these features may be used together.
from dataclasses import dataclass
from statistics import mean
import list2csv
@dataclass
class Student:
student_id: str
test_1_mark: float
test_2_mark: float
assignment_marks: list[float]
lab_marks: list[float]
comments: list[str]
students = [
Student('abcd123',
78.5, 88,
[84.5, 96, 87],
[92.3, 98, 100, 70],
['Good', 'Needs work on classes']),
Student('efgh456',
62, 74,
[70.5, 76, 80],
[98, 68.2, 0, 93.5],
['Good', 'Needs work on formatting', 'Needs work on recursion']),
Student('ijkl789',
100, 99.5,
[98.5, 100, 100],
[100, 100, 98.7, 100],
['Excellent']),
]
with open('student_overview.csv', 'w') as f:
writer = list2csv.Writer(f)
writer.add_counter('Student', start=1)
writer.add_column('ID', 'student_id')
writer.add_column('Test 1', 'test_1_mark', '{:.2f}', aggregate=True)
writer.add_column('Test 2', 'test_2_mark', '{:.2f}', aggregate=True)
writer.add_aggregator('Av. Test Mark', mean, '{:.2f}')
writer.add_multi_column('A{}', 'assignment_marks',
range(1, 4), '{:.2f}', aggregate=True)
writer.add_aggregator('Av. Assignment Mark', mean, '{:.2f}')
writer.add_multi_column('Lab {}', 'lab_marks',
range(1, 5), '{:.2f}', aggregate=True)
writer.add_aggregator('Av. Lab Mark', mean, '{:.2f}')
writer.add_column('Comments', lambda s: '\n'.join(s.comments))
writer.write_header()
writer.write_all(students)
Would produce:
Student | ID | Test 1 | Test 2 | Av. Test Mark | A1 | A2 | A3 | Av. Assignment Mark | Lab 1 | Lab 2 | Lab 3 | Lab 4 | Av. Lab Mark | Comments |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | abcd123 | 78.50 | 88.00 | 83.25 | 84.50 | 96.00 | 87.00 | 89.17 | 92.30 | 98.00 | 100.00 | 70.00 | 90.08 | Good Needs work on classes |
2 | efgh456 | 62.00 | 74.00 | 68.00 | 70.50 | 76.00 | 80.00 | 75.50 | 98.00 | 68.20 | 0.00 | 93.50 | 64.92 | Good Needs work on formatting Needs work on recursion |
3 | ijkl789 | 100.00 | 99.50 | 99.75 | 98.50 | 100.00 | 100.00 | 99.50 | 100.00 | 100.00 | 98.70 | 100.00 | 99.67 | Excellent |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.