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