Skip to content

Commit c710e6d

Browse files
committed
feat: add --csv-txyz-idxs and rename --txyz to --csv-txyz
**src/stepcount/stepcount.py** - Rename --txyz to --csv-txyz for consistency with other CSV options - Add --csv-txyz-idxs for specifying columns by index (e.g., '0,1,2,3') **src/stepcount/utils.py** - Add csv_txyz_idxs param to read() - Parse and validate indices (must be 4 non-negative integers) - Read CSV header to map indices to column names - Raise ValueError for out-of-range or invalid indices - Simplify warning message for non-CSV files
1 parent 12e7649 commit c710e6d

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

src/stepcount/stepcount.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,9 @@ def main():
4242
type=str, default='cpu')
4343
parser.add_argument("--sample-rate", "-r", help="Sample rate for measurement, otherwise inferred.",
4444
type=int, default=None)
45-
parser.add_argument("--txyz",
46-
help=("Use this option to specify the column names for time, x, y, z "
47-
"in the input file, in that order. Use a comma-separated string. "
48-
"Default: 'time,x,y,z'"),
45+
parser.add_argument("--csv-txyz",
46+
help=("Column names for time, x, y, z in CSV files. "
47+
"Comma-separated string. Default: 'time,x,y,z'"),
4948
type=str, default="time,x,y,z")
5049
parser.add_argument("--exclude-wear-below", "-w",
5150
help="Exclude days with wear time below threshold. Pass values as strings, e.g.: '12H', '30min'. "
@@ -88,6 +87,9 @@ def main():
8887
parser.add_argument("--csv-time-format",
8988
help="Format string for parsing the time column (e.g., '%%Y-%%m-%%d %%H:%%M:%%S.%%f').",
9089
type=str, default=None)
90+
parser.add_argument("--csv-txyz-idxs",
91+
help="Column indices for time,x,y,z (0-indexed, e.g., '0,1,2,3'). Overrides --csv-txyz.",
92+
type=str, default=None)
9193
parser.add_argument('--quiet', '-q', action='store_true', help='Suppress output')
9294
args = parser.parse_args()
9395

@@ -108,7 +110,7 @@ def main():
108110
# Load file
109111
data, info_read = utils.read(
110112
args.filepath,
111-
usecols=args.txyz,
113+
usecols=args.csv_txyz,
112114
start_time=args.start,
113115
end_time=args.end,
114116
calibration_stdtol_min=args.calibration_stdtol_min,
@@ -118,6 +120,7 @@ def main():
118120
csv_start_row=args.csv_start_row,
119121
csv_end_row=args.csv_end_row,
120122
csv_time_format=args.csv_time_format,
123+
csv_txyz_idxs=args.csv_txyz_idxs,
121124
verbose=verbose
122125
)
123126
info.update(info_read)

src/stepcount/utils.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def read(
2121
csv_start_row: int = None,
2222
csv_end_row: int = None,
2323
csv_time_format: str = None,
24+
csv_txyz_idxs: str = None,
2425
verbose: bool = True
2526
):
2627
"""
@@ -46,6 +47,8 @@ def read(
4647
Only applies to CSV files. Default is None (read to the end).
4748
- csv_time_format (str, optional): Format string for parsing the time column (e.g., '%Y-%m-%d %H:%M:%S.%f').
4849
Only applies to CSV files. Default is None (auto-detect).
50+
- csv_txyz_idxs (str, optional): Column indices for time,x,y,z as comma-separated string (0-indexed, e.g., '0,1,2,3').
51+
Overrides usecols for CSV files. Default is None (use usecols/csv_txyz).
4952
- verbose (bool, optional): If True, enables verbose output during processing. Default is True.
5053
5154
Returns:
@@ -69,7 +72,23 @@ def read(
6972
if ftype in (".csv", ".pkl"):
7073

7174
if ftype == ".csv":
72-
tcol, xcol, ycol, zcol = usecols.split(',')
75+
# Determine column names: either from indices or from usecols
76+
if csv_txyz_idxs is not None:
77+
# Parse and validate indices
78+
try:
79+
tidx, xidx, yidx, zidx = map(int, csv_txyz_idxs.split(','))
80+
except ValueError:
81+
raise ValueError(f"csv_txyz_idxs must be 4 comma-separated integers, got: '{csv_txyz_idxs}'")
82+
if any(i < 0 for i in [tidx, xidx, yidx, zidx]):
83+
raise ValueError(f"csv_txyz_idxs must be non-negative integers, got: '{csv_txyz_idxs}'")
84+
# Read header to get column names at those indices
85+
header = pd.read_csv(filepath, nrows=0).columns.tolist()
86+
max_idx = max(tidx, xidx, yidx, zidx)
87+
if max_idx >= len(header):
88+
raise ValueError(f"Column index {max_idx} out of range. CSV has {len(header)} columns.")
89+
tcol, xcol, ycol, zcol = header[tidx], header[xidx], header[yidx], header[zidx]
90+
else:
91+
tcol, xcol, ycol, zcol = usecols.split(',')
7392

7493
# Validate csv_start_row and csv_end_row
7594
if csv_start_row is not None and csv_end_row is not None:
@@ -145,8 +164,8 @@ def read(
145164

146165
elif ftype in (".cwa", ".gt3x", ".bin"):
147166

148-
if csv_start_row is not None or csv_end_row is not None or csv_time_format is not None:
149-
warnings.warn("--csv-start-row, --csv-end-row, and --csv-time-format are only supported for CSV files. Ignoring.")
167+
if csv_start_row is not None or csv_end_row is not None or csv_time_format is not None or csv_txyz_idxs is not None:
168+
warnings.warn("--csv-* options are only supported for CSV files. Ignoring.")
150169

151170
data, info = actipy.read_device(
152171
filepath,

0 commit comments

Comments
 (0)