Add script to split CSV file

This commit is contained in:
2026-10-03 13:51:47 +02:00
parent 50fe67f33c
commit d5d068324e
3 changed files with 221 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
adherents*
jetpack-map.txt
+49
View File
@@ -78,3 +78,52 @@ A few practical details:
- If you do not want Mapbox to return French place names, use `--language en`.
The `permanent=True` setting in the script is intentional because the coordinates and Mapbox IDs are being saved into WordPress. If your Mapbox account does not support permanent geocoding, remove that setting only after confirming that your intended storage and use comply with Mapbox’s current terms.
I also needed a helper to split addresses as my initial file sometimes contrained two addresses on the same line.
The script will convent
```csv
name,address
"Title 1 / Title 2","Address 1 / Address 2"
"Title 3","Address 3"
```
into:
```csv
name,address
Title 1,Address 1
Title 2,Address 2
Title 3,Address 3
```
Run it like this:
```bash
python split_addresses.py addresses.csv addresses-normalized.csv
```
The script pairs values by position:
```text
Title 1 / Title 2
Address 1 / Address 2
```
becomes:
```text
Title 1 → Address 1
Title 2 → Address 2
```
It also checks that each row contains the same number of titles and addresses. For example, this input will produce an error rather than silently pairing incorrectly:
```csv
name,address
"Title 1 / Title 2","Address 1"
```
This assumes `/` is always your separator. If an address can legitimately contain a slash, such as `12 A/B Main Street`, that row would need a different delimiter or special handling.
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env python3
import argparse
import csv
import re
import sys
from pathlib import Path
def split_cell(value):
"""
Split a cell on slash characters.
Empty parts are removed, so:
"A / B" becomes ["A", "B"]
"""
if value is None:
return [""]
parts = re.split(r"\s*/\s*", value.strip())
return [
part.strip()
for part in parts
if part.strip()
]
def expand_row(row, line_number):
"""
Expand one CSV row into one or more rows.
Cells without a slash are repeated.
Cells with slashes must contain the same number
of values as the other split cells.
"""
split_values = {
column: split_cell(value)
for column, value in row.items()
}
split_lengths = [
len(values)
for values in split_values.values()
if len(values) > 1
]
# A row with no slash produces one row.
if not split_lengths:
return [row]
expected_count = split_lengths[0]
# Every cell containing a slash must split into the same
# number of values.
invalid_columns = [
column
for column, values in split_values.items()
if len(values) not in (1, expected_count)
]
if invalid_columns:
raise ValueError(
f"line {line_number}: inconsistent number of values. "
f"Expected {expected_count} values in slash-separated "
f"cells, but these columns differ: "
f"{', '.join(invalid_columns)}"
)
expanded_rows = []
for index in range(expected_count):
expanded_row = {}
for column, values in split_values.items():
if len(values) == 1:
# Repeat values that did not contain a slash.
expanded_row[column] = values[0]
else:
# Take the corresponding split value.
expanded_row[column] = values[index]
expanded_rows.append(expanded_row)
return expanded_rows
def preprocess_csv(input_path, output_path):
output_rows = []
with open(
input_path,
"r",
encoding="utf-8-sig",
newline="",
) as input_file:
reader = csv.DictReader(input_file)
if not reader.fieldnames:
raise ValueError("The input CSV has no header row.")
fieldnames = reader.fieldnames
for line_number, row in enumerate(reader, start=2):
# Ignore completely empty rows.
if not any(
value is not None and value.strip()
for value in row.values()
):
continue
expanded_rows = expand_row(row, line_number)
output_rows.extend(expanded_rows)
with open(
output_path,
"w",
encoding="utf-8",
newline="",
) as output_file:
writer = csv.DictWriter(
output_file,
fieldnames=fieldnames,
extrasaction="ignore",
)
writer.writeheader()
writer.writerows(output_rows)
print(
f"Created {output_path} with "
f"{len(output_rows)} row(s).",
file=sys.stderr,
)
def main():
parser = argparse.ArgumentParser(
description=(
"Expand slash-separated values in any CSV column "
"into separate rows."
)
)
parser.add_argument(
"input_csv",
help="Input CSV file",
)
parser.add_argument(
"output_csv",
help="Output CSV file",
)
args = parser.parse_args()
try:
preprocess_csv(
Path(args.input_csv),
Path(args.output_csv),
)
except (OSError, ValueError) as error:
print(f"Error: {error}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()