dataframe-textual 0.3.2__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,756 @@
1
+ Metadata-Version: 2.4
2
+ Name: dataframe-textual
3
+ Version: 1.2.0
4
+ Summary: Interactive terminal viewer/editor for tabular data
5
+ Project-URL: Homepage, https://github.com/need47/dataframe-textual
6
+ Project-URL: Repository, https://github.com/need47/dataframe-textual.git
7
+ Project-URL: Documentation, https://github.com/need47/dataframe-textual#readme
8
+ Project-URL: Bug Tracker, https://github.com/need47/dataframe-textual/issues
9
+ Author-email: Tiejun Cheng <need47@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: csv,data-analysis,editor,excel,interactive,polars,terminal,textual,tui,viewer
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Natural Language :: English
19
+ Classifier: Operating System :: MacOS
20
+ Classifier: Operating System :: POSIX
21
+ Classifier: Operating System :: Unix
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
+ Classifier: Topic :: Office/Business
28
+ Classifier: Topic :: Utilities
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.11
31
+ Requires-Dist: polars>=1.34.0
32
+ Requires-Dist: textual>=6.5.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: textual-dev>=1.8.0; extra == 'dev'
35
+ Provides-Extra: excel
36
+ Requires-Dist: fastexcel>=0.16.0; extra == 'excel'
37
+ Requires-Dist: xlsxwriter>=3.2.9; extra == 'excel'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # DataFrame Textual
41
+
42
+ A powerful, interactive terminal-based viewer/editor for CSV/TSV/Excel/Parquet/JSON/NDJSON built with Python, [Polars](https://pola.rs/), and [Textual](https://textual.textualize.io/). Inspired by [VisiData](https://www.visidata.org/), this tool provides smooth keyboard navigation, data manipulation, and a clean interface for exploring tabular data directly in terminal with multi-tab support for multiple files!
43
+
44
+ ![Screenshot](https://raw.githubusercontent.com/need47/dataframe-textual/refs/heads/main/screenshot.png)
45
+
46
+ ## Features
47
+
48
+ ### Data Viewing
49
+ - 🚀 **Fast Loading** - Powered by Polars for efficient data handling
50
+ - 🎨 **Rich Terminal UI** - Beautiful, color-coded columns with various data types (e.g., integer, float, string)
51
+ - ⌨️ **Comprehensive Keyboard Navigation** - Intuitive controls for browsing, editing, and manipulating data
52
+ - 📊 **Flexible Input** - Read from files and/or stdin (pipes/redirects)
53
+ - 🔄 **Smart Pagination** - Lazy load rows on demand for handling large datasets
54
+
55
+ ### Data Manipulation
56
+ - 📝 **Data Editing** - Edit cells, delete rows, and remove columns
57
+ - 🔍 **Search & Filter** - Find values, highlight matches, and filter selected rows
58
+ - ↔️ **Column/Row Reordering** - Move columns and rows with simple keyboard shortcuts
59
+ - 📈 **Sorting & Statistics** - Multi-column sorting and frequency distribution analysis
60
+ - 💾 **Save & Undo** - Save edits back to file with full undo/redo support
61
+
62
+ ### Advanced Features
63
+ - 📂 **Multi-File Support** - Open multiple files in separate tabs
64
+ - 🔄 **Tab Management** - Seamlessly switch between open files with keyboard shortcuts
65
+ - 📌 **Freeze Rows/Columns** - Keep important rows and columns visible while scrolling
66
+ - 🎯 **Cursor Type Cycling** - Switch between cell, row, and column selection modes
67
+
68
+ ## Installation
69
+
70
+ ### Using pip
71
+
72
+ ```bash
73
+ # Install from PyPI
74
+ pip install dataframe-textual
75
+
76
+ # With Excel support (fastexcel, xlsxwriter)
77
+ pip install dataframe-textual[excel]
78
+ ```
79
+
80
+ This installs an executable `dv`.
81
+
82
+ Then run:
83
+ ```bash
84
+ dv <csv_file>
85
+ ```
86
+
87
+ ### Using [uv](https://docs.astral.sh/uv/)
88
+
89
+ ```bash
90
+ # Quick run using uvx without installation
91
+ uvx https://github.com/need47/dataframe-textual.git <csvfile>
92
+
93
+ # Clone or download the project
94
+ cd dataframe-textual
95
+ uv sync --extra excel # with Excel support
96
+
97
+ # Run directly with uv
98
+ uv run dv <csv_file>
99
+ ```
100
+
101
+ ### Development installation
102
+
103
+ ```bash
104
+ # Clone the repository
105
+ git clone https://github.com/need47/dataframe-textual.git
106
+ cd dataframe-textual
107
+
108
+ # Install from local source
109
+ pip install -e .
110
+
111
+ # Or with development dependencies
112
+ pip install -e ".[excel,dev]"
113
+ ```
114
+
115
+ ## Usage
116
+
117
+ ### Basic Usage - Single File
118
+
119
+ ```bash
120
+ # After pip install dataframe-textual
121
+ dv pokemon.csv
122
+
123
+ # Or if running from source
124
+ python main.py pokemon.csv
125
+
126
+ # Or with uv
127
+ uv run python main.py pokemon.csv
128
+
129
+ # Read from stdin (auto-detects format; defaults to TSV if not recognized)
130
+ cat data.tsv | dv
131
+ dv < data.tsv
132
+ ```
133
+
134
+ ### Multi-File Usage - Multiple Tabs
135
+
136
+ ```bash
137
+ # Open multiple files in tabs
138
+ dv file1.csv file2.csv file3.csv
139
+
140
+ # Open multiple sheets in tabs in an Excel file
141
+ dv file.xlsx
142
+
143
+ # Mix files and stdin (read from stdin, then open file)
144
+ dv data1.tsv < data2.tsv
145
+ ```
146
+
147
+ When multiple files are opened:
148
+ - Each file appears as a separate tab at the top
149
+ - Switch between tabs using `>` (next) or `<` (previous)
150
+ - Open additional files with `Ctrl+O`
151
+ - Close the current tab with `Ctrl+W`
152
+ - Each file maintains its own state (edits, sort order, selections, history, etc.)
153
+
154
+ ## Keyboard Shortcuts
155
+
156
+ ### App-Level Controls
157
+
158
+ #### File & Tab Management
159
+
160
+ | Key | Action |
161
+ |-----|--------|
162
+ | `Ctrl+O` | Open file in a new tab |
163
+ | `Ctrl+W` | Close current tab |
164
+ | `Ctrl+Shift+S` | Save all open tabs to Excel file |
165
+ | `>` or `b` | Move to next tab |
166
+ | `<` | Move to previous tab |
167
+ | `B` | Toggle tab bar visibility |
168
+ | `q` | Quit the application |
169
+
170
+ #### View & Settings
171
+
172
+ | Key | Action |
173
+ |-----|--------|
174
+ | `Ctrl+H` | Toggle help panel |
175
+ | `k` | Cycle through themes |
176
+
177
+ ---
178
+
179
+ ### Table-Level Controls
180
+
181
+ #### Navigation
182
+
183
+ | Key | Action |
184
+ |-----|--------|
185
+ | `g` | Jump to first row |
186
+ | `G` | Jump to last row (loads all remaining rows) |
187
+ | `↑` / `↓` | Move up/down one row |
188
+ | `←` / `→` | Move left/right one column |
189
+ | `Home` / `End` | Jump to first/last column in current row |
190
+ | `Ctrl + Home` / `Ctrl + End` | Jump to top/bottom in current page |
191
+ | `PageDown` / `PageUp` | Scroll down/up one page |
192
+
193
+ #### Viewing & Display
194
+
195
+ | Key | Action |
196
+ |-----|--------|
197
+ | `Enter` | View full details of current row in modal |
198
+ | `F` | Show frequency distribution for column |
199
+ | `s` | Show statistics for current column |
200
+ | `S` | Show statistics for entire dataframe |
201
+ | `K` | Cycle cursor type: cell → row → column → cell |
202
+ | `~` | Toggle row labels |
203
+
204
+ #### Data Editing
205
+
206
+ | Key | Action |
207
+ |-----|--------|
208
+ | `Double-click` | Edit cell or rename column header |
209
+ | `delete` | Clear current cell (set to NULL) |
210
+ | `e` | Edit current cell (respects data type) |
211
+ | `E` | Edit entire column with expression |
212
+ | `a` | Add empty column after current |
213
+ | `A` | Add column with name and value/expression |
214
+ | `-` (minus) | Delete current column |
215
+ | `_` (underscore) | Delete current column and all columns after |
216
+ | `Ctrl+-` | Delete current column and all columns before |
217
+ | `x` | Delete current row |
218
+ | `X` | Delete current row and all rows below |
219
+ | `Ctrl+X` | Delete current row and all rows above |
220
+ | `d` | Duplicate current column (appends '_copy' suffix) |
221
+ | `D` | Duplicate current row |
222
+ | `h` | Hide current column |
223
+ | `H` | Show all hidden rows/columns |
224
+
225
+ #### Searching & Filtering
226
+
227
+ | Key | Action |
228
+ |-----|--------|
229
+ | `\` | Search in current column using cursor value and select rows |
230
+ | `\|` (pipe) | Search in current column with expression and select rows |
231
+ | `/` | Find in current column with cursor value and highlight matches |
232
+ | `?` | Find in current column with expression and highlight matches |
233
+ | `n` | Go to next match |
234
+ | `N` | Go to previous match |
235
+ | `{` | Go to previous selected row |
236
+ | `}` | Go to next selected row |
237
+ | `'` | Select/deselect current row |
238
+ | `t` | Toggle selected rows (invert) |
239
+ | `T` | Clear all selected rows and/or matches |
240
+ | `"` (quote) | Filter to selected rows only |
241
+ | `v` | View only rows by selected rows and/or matches or cursor value |
242
+ | `V` | View only rows by expression |
243
+
244
+ #### Find & Replace
245
+
246
+ | Key | Action |
247
+ |-----|--------|
248
+ | `f` | Find across all columns with cursor value |
249
+ | `Ctrl+F` | Find across all columns with expression |
250
+ | `r` | Find and replace in current column (interactive or replace all) |
251
+ | `R` | Find and replace across all columns (interactive or replace all) |
252
+
253
+ #### Sorting
254
+
255
+ | Key | Action |
256
+ |-----|--------|
257
+ | `[` | Sort current column ascending |
258
+ | `]` | Sort current column descending |
259
+
260
+ #### Reordering
261
+
262
+ | Key | Action |
263
+ |-----|--------|
264
+ | `Shift+↑` | Move current row up |
265
+ | `Shift+↓` | Move current row down |
266
+ | `Shift+←` | Move current column left |
267
+ | `Shift+→` | Move current column right |
268
+
269
+ #### Type Conversion
270
+
271
+ | Key | Action |
272
+ |-----|--------|
273
+ | `#` | Cast current column to integer (Int64) |
274
+ | `%` | Cast current column to float (Float64) |
275
+ | `!` | Cast current column to boolean |
276
+ | `$` | Cast current column to string |
277
+ | `@` | Make URLs in current column clickable with Ctrl/Cmd + click|
278
+
279
+ #### Data Management
280
+
281
+ | Key | Action |
282
+ |-----|--------|
283
+ | `z` | Freeze rows and columns |
284
+ | `,` | Toggle thousand separator for numeric display |
285
+ | `c` | Copy current cell to clipboard |
286
+ | `Ctrl+C` | Copy column to clipboard |
287
+ | `Ctrl+R` | Copy row to clipboard (tab-separated) |
288
+ | `Ctrl+S` | Save current tab to file |
289
+ | `u` | Undo last action |
290
+ | `U` | Redo last undone action |
291
+ | `Ctrl+U` | Reset to initial state |
292
+
293
+ ## Features in Detail
294
+
295
+ ### 1. Color-Coded Data Types
296
+
297
+ Columns are automatically styled based on their data type:
298
+ - **integer**: Cyan text, right-aligned
299
+ - **float**: Magenta text, right-aligned
300
+ - **string**: Green text, left-aligned
301
+ - **boolean**: Blue text, centered
302
+ - **temporal**: Yellow text, centered
303
+
304
+ ### 2. Row Detail View
305
+
306
+ Press `Enter` on any row to open a modal showing all column values for that row.
307
+ Useful for examining wide datasets where columns don't fit on screen.
308
+
309
+ **In the Row Detail Modal**:
310
+ - Press `v` to **view** the main table to show only rows with the selected column value
311
+ - Press `"` to **filter** all rows containing the selected column value
312
+ - Press `q` or `Escape` to close the modal
313
+
314
+ ### 3. Search & Filtering
315
+
316
+ The application provides multiple search modes for different use cases:
317
+
318
+ **Search Operations** - Direct value/expression matching in current column:
319
+ - **`|` - Column Expression Search**: Opens dialog to search current column with custom expression
320
+ - **`\` - Column Cursor Search**: Instantly search current column using the cursor value
321
+
322
+ **Find Operations** - Find by value/expression:
323
+ - **`/` - Column Find**: Find cursor value within current column
324
+ - **`?` - Column Expression Find**: Open dialog to search current column with expression
325
+ - **`f` - Global Find**: Find cursor value across all columns
326
+ - **`Ctrl+f` - Global Expression Find**: Open dialog to search all columns with expression
327
+
328
+ **Selection & Filtering**:
329
+ - **`'` - Toggle Row Selection**: Select/deselect current row (marks it for filtering)
330
+ - **`t` - Invert Selections**: Flip selection state of all rows at once
331
+ - **`T` - Clear Selections**: Remove all row selections and matches
332
+ - **`"` - Filter Selected**: Display only the selected rows and remove others
333
+ - **`v` - View by Value**: Filter/view rows by selected rows or cursor value (others hidden but preserved)
334
+ - **`V` - View by Expression**: Filter/view rows using custom Polars expression (others hidden but preserved)
335
+
336
+ **Advanced Matching Options**:
337
+
338
+ When searching or finding, you can use checkboxes in the dialog to enable:
339
+ - **Match Nocase**: Ignore case differences (e.g., "john", "John", "JOHN" all match)
340
+ - **Match Whole**: Match complete value, not partial substrings or words (e.g., "cat" won't match in "catfish")
341
+
342
+ These options work with plain text searches. Use Polars regex patterns in expressions for more control:
343
+ - **Case-insensitive matching in expressions**: Use `(?i)` prefix in regex (e.g., `(?i)john`)
344
+ - **Word boundaries in expressions**: Use `\b` in regex (e.g., `\bjohn\b` matches whole word)
345
+
346
+ **Quick Tips:**
347
+ - Search results highlight matching rows/cells in **red**
348
+ - Multiple searches **accumulate selections** - each new search adds to the selections
349
+ - Type-aware matching automatically converts values. Resort to string comparison if conversion fails
350
+ - Use `u` to undo any search or filter
351
+
352
+ ### 3b. Find & Replace
353
+
354
+ The application provides powerful find and replace functionality for both single-column and global replacements.
355
+
356
+ **Replace Operations**:
357
+ - **`r` - Column Replace**: Replace values in the current column
358
+ - **`R` - Global Replace**: Replace values across all columns
359
+
360
+ **How It Works:**
361
+
362
+ When you press `r` or `R`, a dialog opens where you can enter:
363
+ 1. **Find term**: The value or expression to search for
364
+ 2. **Replace term**: What to replace matches with
365
+ 3. **Matching options**:
366
+ - **Match Nocase**: Ignore case differences when matching (unchecked by default)
367
+ - **Match Whole**: Match complete words only, not partial words (unchecked by default)
368
+ 4. **Replace option**:
369
+ - Choose **"Replace All"** to replace all matches at once (with confirmation)
370
+ - Otherwise, review and confirm each match individually
371
+
372
+ **Replace All** (`r` or `R` → Choose "Replace All"):
373
+ - Shows a confirmation dialog with the number of matches and replacements
374
+ - Replaces all matches with a single operation
375
+ - Full undo support with `u`
376
+ - Useful for bulk replacements when you're confident about the change
377
+
378
+ **Replace Interactive** (`r` or `R` → Choose "Replace Interactive"):
379
+ - Shows each match one at a time with a preview of the replacement
380
+ - For each match, press:
381
+ - `Enter` or press the `Yes` button - **Replace this occurrence** and move to next
382
+ - Press the `Skip` button - **Skip this occurrence** and move to next
383
+ - `Escape` or press the `No` button - **Cancel** remaining replacements (but keep already-made replacements)
384
+ - Displays progress: `Occurrence X of Y` (Y = total matches, X = current)
385
+ - Shows the value that will be replaced and what it will become
386
+ - Useful for careful replacements where you want to review each change
387
+
388
+ **Search Term Types:**
389
+ - **Plain text**: Exact string match (e.g., "John" finds "John")
390
+ - Use **Match Nocase** checkbox to match regardless of case (e.g., find "john", "John", "JOHN")
391
+ - Use **Match Whole** checkbox to match complete words only (e.g., find "cat" but not in "catfish")
392
+ - **NULL**: Replace null/missing values (type `NULL`)
393
+ - **Expression**: Polars expressions for complex matching (e.g., `$_ > 50` for column replace)
394
+ - **Regex patterns**: Use Polars regex syntax for advanced matching
395
+ - Case-insensitive: Use `(?i)` prefix (e.g., `(?i)john`)
396
+ - Whole word: Use `\b` boundary markers (e.g., `\bjohn\b`)
397
+
398
+ **Examples:**
399
+
400
+ ```
401
+ Find: "John"
402
+ Replace: "Jane"
403
+ → All occurrences of "John" become "Jane"
404
+
405
+ Find: "john"
406
+ Replace: "jane"
407
+ Match Nocase: ✓ (checked)
408
+ → "John", "JOHN", "john" all become "jane"
409
+
410
+ Find: "cat"
411
+ Replace: "dog"
412
+ Match Whole: ✓ (checked)
413
+ → "cat" becomes "dog", but "catfish" is not matched
414
+
415
+ Find: "NULL"
416
+ Replace: "Unknown"
417
+ → All null/missing values become "Unknown"
418
+
419
+ Find: "(?i)active" # Case-insensitive
420
+ Replace: "inactive"
421
+ → "Active", "ACTIVE", "active" all become "inactive"
422
+ ```
423
+
424
+ **For Global Replace (`R`)**:
425
+ - Searches and replaces across all columns simultaneously
426
+ - Each column can have different matching behavior (string matching for text, numeric for numbers)
427
+ - Preview shows which columns contain matches before replacement
428
+ - Useful for standardizing values across multiple columns
429
+
430
+ **Features:**
431
+ - **Full history support**: Use `u` (undo) to revert any replacement
432
+ - **Visual feedback**: Matching cells are highlighted before you choose replacement mode
433
+ - **Safe operations**: Requires confirmation before replacing
434
+ - **Progress tracking**: Shows how many replacements have been made during interactive mode
435
+ - **Type-aware**: Respects column data types when matching and replacing
436
+ - **Flexible matching**: Support for case-insensitive and whole-word matching
437
+
438
+ **Tips:**
439
+ - Use interactive mode for one-time replacements to be absolutely sure
440
+ - Use "Replace All" for routine replacements (e.g., fixing typos, standardizing formats)
441
+ - Use **Match Nocase** for matching variations of names or titles
442
+ - Use **Match Whole** to avoid unintended partial replacements
443
+ - Use `u` immediately if you accidentally replace something wrong
444
+ - For complex replacements, use Polars expressions or regex patterns in the find term
445
+ - Test with a small dataset first before large replacements
446
+
447
+ ### 4. [Polars Expressions](https://docs.pola.rs/api/python/stable/reference/expressions/index.html)
448
+
449
+ Complex values or filters can be specified via Polars expressions, with the following adaptions for convenience:
450
+
451
+ **Column References:**
452
+ - `$_` - Current column (based on cursor position)
453
+ - `$1`, `$2`, etc. - Column by 1-based index
454
+ - `$age`, `$salary` - Column by name (use actual column names)
455
+
456
+ **Row References:**
457
+ - `$#` - Current row index (1-based)
458
+
459
+ **Basic Comparisons:**
460
+ - `$_ > 50` - Current column greater than 50
461
+ - `$salary >= 100000` - Salary at least 100,000
462
+ - `$age < 30` - Age less than 30
463
+ - `$status == 'active'` - Status exactly matches 'active'
464
+ - `$name != 'Unknown'` - Name is not 'Unknown'
465
+
466
+ **Logical Operators:**
467
+ - `&` - AND
468
+ - `|` - OR
469
+ - `~` - NOT
470
+
471
+ **Practical Examples:**
472
+ - `($age < 30) & ($status == 'active')` - Age less than 30 AND status is active
473
+ - `($name == 'Alice') | ($name == 'Bob')` - Name is Alice or Bob
474
+ - `$salary / 1000 >= 50` - Salary divided by 1,000 is at least 50
475
+ - `($department == 'Sales') & ($bonus > 5000)` - Sales department with bonus over 5,000
476
+ - `($score >= 80) & ($score <= 90)` - Score between 80 and 90
477
+ - `~($status == 'inactive')` - Status is not inactive
478
+ - `$revenue > $expenses` - Revenue exceeds expenses
479
+
480
+ **String Matching:**
481
+ - `$name.str.contains("John")` - Name contains "John" (case-sensitive)
482
+ - `$name.str.contains("(?i)john")` - Name contains "john" (case-insensitive)
483
+ - `$email.str.ends_with("@company.com")` - Email ends with domain
484
+ - `$code.str.starts_with("ABC")` - Code starts with "ABC"
485
+ - `$age.cast(pl.String).str.starts_with("7")` - Age (cast to string first) starts with "7"
486
+
487
+ **Number Operations:**
488
+ - `$age * 2 > 100` - Double age greater than 100
489
+ - `($salary + $bonus) > 150000` - Total compensation over 150,000
490
+ - `$percentage >= 50` - Percentage at least 50%
491
+
492
+ **Null Handling:**
493
+ - `$column.is_null()` - Find null/missing values
494
+ - `$column.is_not_null()` - Find non-null values
495
+ - `NULL` - a value to represent null for convenience
496
+
497
+ **Tips:**
498
+ - Use column names that match exactly (case-sensitive)
499
+ - Use parentheses to clarify complex expressions: `($a & $b) | ($c & $d)`
500
+
501
+ ### 5. Sorting
502
+
503
+ - Press `[` to sort current column ascending
504
+ - Press `]` to sort current column descending
505
+ - Multi-column sorting supported (press multiple times on different columns)
506
+ - Press same key twice to remove the column from sorting
507
+
508
+ ### 6. Frequency Distribution
509
+
510
+ Press `F` to see how many times each value appears in the current column. The modal shows:
511
+ - Value
512
+ - Count
513
+ - Percentage
514
+ - Histogram
515
+ - **Total row** at the bottom
516
+
517
+ **In the Frequency Table**:
518
+ - Press `[` and `]` to sort by any column (value, count, or percentage)
519
+ - Press `v` to **filter** the main table to show only rows with the selected value
520
+ - Press `"` to **exclude** all rows except those containing the selected value
521
+ - Press `q` or `Escape` to close the frequency table
522
+
523
+ This is useful for:
524
+ - Understanding value distributions
525
+ - Quickly filtering to specific values
526
+ - Identifying rare or common values
527
+ - Finding the most/least frequent entries
528
+
529
+ ### 7. Column & Dataframe Statistics
530
+
531
+ Press `s` to see summary statistics for the current column, or press `S` for statistics across the entire dataframe.
532
+
533
+ **Column Statistics** (`s`):
534
+ - Shows calculated statistics using Polars' `describe()` method
535
+ - Displays: count, null count, mean, median, std, min, max, etc.
536
+ - Values are color-coded according to their data type
537
+ - Statistics label column has no styling for clarity
538
+
539
+ **Dataframe Statistics** (`S`):
540
+ - Shows statistics for all numeric and applicable columns simultaneously
541
+ - Data columns are color-coded by their data type (integer, float, string, etc.)
542
+
543
+ **In the Statistics Modal**:
544
+ - Press `q` or `Escape` to close the statistics table
545
+ - Use arrow keys to navigate
546
+ - Useful for quick data validation and summary reviews
547
+
548
+ This is useful for:
549
+ - Understanding data distributions and characteristics
550
+ - Identifying outliers and anomalies
551
+ - Data quality assessment
552
+ - Quick statistical summaries without external tools
553
+ - Comparing statistics across columns
554
+
555
+ ### 8. Data Editing
556
+
557
+ **Edit Cell** (`e` or **Double-click**):
558
+ - Opens modal for editing current cell
559
+ - Validates input based on column data type
560
+
561
+ **Rename Column Header** (**Double-click** column header):
562
+ - Quick rename by double-clicking the column header
563
+
564
+ **Delete Row** (`x`):
565
+ - Delete all selected rows (if any) at once
566
+ - Or delete single row at cursor
567
+
568
+ **Delete Row and Below** (`X`):
569
+ - Deletes the current row and all rows below it
570
+ - Useful for removing trailing data or the end of a dataset
571
+
572
+ **Delete Row and Above** (`Ctrl+X`):
573
+ - Deletes the current row and all rows above it
574
+ - Useful for removing leading rows or the beginning of a dataset
575
+
576
+ **Delete Column** (`-`):
577
+ - Removes the entire column from view and dataframe
578
+
579
+ **Delete Column and After** (`_`):
580
+ - Deletes the current column and all columns to the right
581
+ - Useful for removing trailing columns or the end of a dataset
582
+
583
+ **Delete Column and Before** (`Ctrl+-`):
584
+ - Deletes the current column and all columns to the left
585
+ - Useful for removing leading columns or the beginning of a dataset
586
+
587
+ ### 9. Hide & Show Columns
588
+
589
+ **Hide Column** (`h`):
590
+ - Temporarily hides the current column from display
591
+ - Column data is preserved in the dataframe
592
+ - Hidden columns are included in saves
593
+
594
+ **Show Hidden Rows/Columns** (`H`):
595
+ - Restores all previously hidden rows/columns to the display
596
+
597
+ This is useful for:
598
+ - Focusing on specific columns without deleting data
599
+ - Temporarily removing cluttered or unnecessary columns
600
+
601
+ ### 10. Duplicate Column
602
+
603
+ Press `d` to duplicate the current column:
604
+ - Creates a new column immediately after the current column
605
+ - New column has '_copy' suffix (e.g., 'price' → 'price_copy')
606
+ - Duplicate preserves all data from original column
607
+ - New column is inserted into the dataframe
608
+
609
+ This is useful for:
610
+ - Creating backup copies of columns before transformation
611
+ - Working with alternative versions of column data
612
+ - Comparing original vs. processed column values side-by-side
613
+
614
+ ### 11. Duplicate Row
615
+
616
+ Press `D` to duplicate the current row:
617
+ - Creates a new row immediately after the current row
618
+ - Duplicate preserves all data from original row
619
+ - New row is inserted into the dataframe
620
+
621
+ This is useful for:
622
+ - Creating variations of existing data records
623
+ - Batch adding similar rows with modifications
624
+
625
+ ### 12. Column & Row Reordering
626
+
627
+ **Move Columns**: `Shift+←` and `Shift+→`
628
+ - Swaps adjacent columns
629
+ - Reorder is preserved when saving
630
+
631
+ **Move Rows**: `Shift+↑` and `Shift+↓`
632
+ - Swaps adjacent rows
633
+ - Reorder is preserved when saving
634
+
635
+ ### 13. Freeze Rows and Columns
636
+
637
+ Press `z` to open the dialog:
638
+ - Enter number of fixed rows and/or columns to keep top rows/columns visible while scrolling
639
+
640
+ ### 13.5. Thousand Separator Toggle
641
+
642
+ Press `,` to toggle thousand separator formatting for numeric data:
643
+ - Applies to **integer** and **float** columns
644
+ - Formats large numbers with commas for readability (e.g., `1000000` → `1,000,000`)
645
+ - Works across all numeric columns in the table
646
+ - Toggle on/off as needed for different viewing preferences
647
+ - Display-only: does not modify underlying data in the dataframe
648
+ - State persists during the session
649
+
650
+ ### 14. Save File
651
+
652
+ Press `Ctrl+S` to save:
653
+ - Save filtered, edited, or sorted data back to file
654
+ - Choose filename in modal dialog
655
+ - Confirm if file already exists
656
+
657
+ ### 15. Undo/Redo/Reset
658
+
659
+ **Undo** (`u`):
660
+ - Reverts last action with full state restoration
661
+ - Works for edits, deletions, sorts, searches, etc.
662
+ - Shows description of reverted action
663
+
664
+ **Redo** (`U`):
665
+ - Reapplies the last undone action
666
+ - Restores the state before the undo was performed
667
+ - Useful for redoing actions you've undone by mistake
668
+ - Useful for alternating between two different states
669
+
670
+ **Reset** (`Ctrl+U`):
671
+ - Reverts all changes and returns to original data state when file was first loaded
672
+ - Clears all edits, deletions, selections, filters, and sorts
673
+ - Useful for starting fresh without reloading the file
674
+
675
+ ### 16. Column Type Conversion
676
+
677
+ Press the type conversion keys to instantly cast the current column to a different data type:
678
+
679
+ **Type Conversion Shortcuts**:
680
+ - `#` - Cast to **integer**
681
+ - `%` - Cast to **float**
682
+ - `!` - Cast to **boolean**
683
+ - `$` - Cast to **string**
684
+
685
+ **Features**:
686
+ - Instant conversion with visual feedback
687
+ - Full undo support - press `u` to revert
688
+ - Leverage Polars' robust type casting
689
+
690
+ **Note**: Type conversion attempts to preserve data where possible. Conversions may lose data (e.g., float to int rounding).
691
+
692
+ ### 17. Cursor Type Cycling
693
+
694
+ Press `K` to cycle through selection modes:
695
+ 1. **Cell mode**: Highlight individual cell (and its row/column headers)
696
+ 2. **Row mode**: Highlight entire row
697
+ 3. **Column mode**: Highlight entire column
698
+
699
+ ### 18. URL Handling
700
+
701
+ Press `@` to make URLs in the current column clickable:
702
+ - **Ctrl/Cmd + click** on URLs to open them in your default browser
703
+ - **Scans** all cells in the current column for URLs starting with `http://` or `https://`
704
+ - **Applies** link styling to make them clickable and dataframe remains unchanged
705
+
706
+ ### 19. Clipboard Operations
707
+
708
+ Copies value to system clipboard with `pbcopy` on macOS and `xclip` on Linux
709
+
710
+ Press `Ctrl+C` to copy:
711
+ - Press `c` to copy cursor value
712
+ - Press `Ctrl+C` to copy column values
713
+ - Press `Ctrl+R` to copy row values (delimited by tab)
714
+
715
+ ## Examples
716
+
717
+ ### Single File Examples
718
+
719
+ ```bash
720
+ # View Pokemon dataset
721
+ dv pokemon.csv
722
+
723
+ # Chain with other command and specify input file format
724
+ cut -d',' -f1,2,3 pokemon.csv | dv -f csv
725
+ ```
726
+
727
+ ### Multi-File/Tab Examples
728
+
729
+ ```bash
730
+ # Open multiple sheets as tabs in a single Excel
731
+ dv sales.xlsx
732
+
733
+ # Open multiple files as tabs
734
+ dv pokemon.csv titanic.csv
735
+
736
+ # Start with one file, then open others using Ctrl+O
737
+ dv initial_data.csv
738
+ ```
739
+
740
+ ## Dependencies
741
+
742
+ - **polars**: Fast DataFrame library for data loading/processing
743
+ - **textual**: Terminal UI framework
744
+ - **fastexcel**: Read Excel files
745
+ - **xlsxwriter**: Write Excel files
746
+
747
+ ## Requirements
748
+
749
+ - Python 3.11+
750
+ - POSIX-compatible terminal (macOS, Linux, WSL)
751
+ - Terminal supporting ANSI escape sequences and mouse events
752
+
753
+ ## Acknowledgments
754
+
755
+ - Inspired by [VisiData](https://visidata.org/)
756
+ - Built with [Textual](https://textual.textualize.io/) and [Polars](https://www.pola.rs/)