Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

list2csv-1.2.0.tar.gz (6.4 kB view hashes)

Uploaded Source

Built Distribution

list2csv-1.2.0-py3-none-any.whl (6.5 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page