← All ivy-nodes
IVYXSTUDIO · IVY NODE
T

Table Clean

ivy.node.table-clean · v0.1.0

ivyx✓

Cleans rows for analysis: trims text, drops empty rows, turns number columns into numbers and date columns into ISO dates. Returns the cleaned rows, how many were dropped and every value it could not convert.

#data#clean#table

Inputs

FieldTypeDescription
rowsrequiredarrayThe rows to clean.
number_columnsarrayColumns to turn into numbers; currency signs and thousands separators are removed.
date_columnsarrayColumns to turn into YYYY-MM-DD dates.

Outputs

FieldTypeDescription
rowsrequiredarrayThe cleaned rows.
droppedrequiredintegerRows removed because every cell was empty.
problemsrequiredarrayValues that could not be converted, as {row, column, value}; the cell is set to null.

Source

python

inp = __ivy_ctx__["nodes"][__ivy_node_id__]["input"]

import re
from datetime import datetime

rows = inp["rows"]
number_columns = inp.get("number_columns", [])
date_columns = inp.get("date_columns", [])
date_formats = ["%Y-%m-%d", "%d.%m.%Y", "%d/%m/%Y", "%m/%d/%Y", "%Y/%m/%d", "%Y-%m-%dT%H:%M:%S"]


def to_number(value):
    if isinstance(value, (int, float)) and not isinstance(value, bool):
        return value
    text = re.sub(r"[^0-9,.\-]", "", str(value))
    # 1.234,56 and 1,234.56 both mean one thousand two hundred.
    if "," in text and "." in text:
        text = text.replace(".", "").replace(",", ".") if text.rfind(",") > text.rfind(".") else text.replace(",", "")
    elif "," in text:
        text = text.replace(",", ".")
    return float(text)


def to_date(value):
    text = str(value).strip()[:19]
    for fmt in date_formats:
        try:
            return datetime.strptime(text[:len(datetime(2000, 1, 1).strftime(fmt))], fmt).strftime("%Y-%m-%d")
        except ValueError:
            continue
    raise ValueError(text)


cleaned, problems, dropped = [], [], 0
for index, row in enumerate(rows):
    row = {k: (v.strip() if isinstance(v, str) else v) for k, v in row.items()}
    if all(v is None or v == "" for v in row.values()):
        dropped += 1
        continue
    for column in number_columns:
        if row.get(column) not in (None, ""):
            try:
                row[column] = to_number(row[column])
            except ValueError:
                problems.append({"row": index, "column": column, "value": row[column]})
                row[column] = None
    for column in date_columns:
        if row.get(column) not in (None, ""):
            try:
                row[column] = to_date(row[column])
            except ValueError:
                problems.append({"row": index, "column": column, "value": row[column]})
                row[column] = None
    cleaned.append(row)

out = __ivy_ctx__["nodes"][__ivy_node_id__]["output"]
out["rows"] = cleaned
out["dropped"] = dropped
out["problems"] = problems

Tests

Requires: python:3.9

  • cleans

    Text is trimmed, money and dates converted, an empty row dropped.

  • reports-bad-values

    A value that is not a date is reported and set to null.