dataframe-textual 1.0.0__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.
- dataframe_textual/__init__.py +1 -2
- dataframe_textual/__main__.py +8 -5
- dataframe_textual/common.py +143 -2
- dataframe_textual/data_frame_table.py +342 -161
- dataframe_textual/data_frame_viewer.py +16 -114
- dataframe_textual/table_screen.py +14 -8
- {dataframe_textual-1.0.0.dist-info → dataframe_textual-1.2.0.dist-info}/METADATA +64 -41
- dataframe_textual-1.2.0.dist-info/RECORD +13 -0
- dataframe_textual-1.2.0.dist-info/entry_points.txt +2 -0
- dataframe_textual-1.0.0.dist-info/RECORD +0 -13
- dataframe_textual-1.0.0.dist-info/entry_points.txt +0 -2
- {dataframe_textual-1.0.0.dist-info → dataframe_textual-1.2.0.dist-info}/WHEEL +0 -0
- {dataframe_textual-1.0.0.dist-info → dataframe_textual-1.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""DataFrame Viewer application and utilities."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
-
import sys
|
|
5
4
|
from functools import partial
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from textwrap import dedent
|
|
@@ -13,7 +12,7 @@ from textual.theme import BUILTIN_THEMES
|
|
|
13
12
|
from textual.widgets import TabbedContent, TabPane
|
|
14
13
|
from textual.widgets.tabbed_content import ContentTab, ContentTabs
|
|
15
14
|
|
|
16
|
-
from .common import get_next_item
|
|
15
|
+
from .common import get_next_item, load_file
|
|
17
16
|
from .data_frame_help_panel import DataFrameHelpPanel
|
|
18
17
|
from .data_frame_table import DataFrameTable
|
|
19
18
|
from .yes_no_screen import OpenFileScreen, SaveFileScreen
|
|
@@ -79,21 +78,20 @@ class DataFrameViewer(App):
|
|
|
79
78
|
}
|
|
80
79
|
"""
|
|
81
80
|
|
|
82
|
-
def __init__(self, *
|
|
81
|
+
def __init__(self, *sources: str) -> None:
|
|
83
82
|
"""Initialize the DataFrame Viewer application.
|
|
84
83
|
|
|
85
|
-
Loads
|
|
84
|
+
Loads data from provided sources and prepares the tabbed interface.
|
|
86
85
|
|
|
87
86
|
Args:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
has_header: Whether the input files have a header row. Defaults to True.
|
|
87
|
+
sources: sources to load dataframes from, each as a tuple of
|
|
88
|
+
(DataFrame | LazyFrame, filename, tabname).
|
|
91
89
|
|
|
92
90
|
Returns:
|
|
93
91
|
None
|
|
94
92
|
"""
|
|
95
93
|
super().__init__()
|
|
96
|
-
self.sources =
|
|
94
|
+
self.sources = sources
|
|
97
95
|
self.tabs: dict[TabPane, DataFrameTable] = {}
|
|
98
96
|
self.help_panel = None
|
|
99
97
|
|
|
@@ -111,12 +109,18 @@ class DataFrameViewer(App):
|
|
|
111
109
|
with self.tabbed:
|
|
112
110
|
seen_names = set()
|
|
113
111
|
for idx, (df, filename, tabname) in enumerate(self.sources, start=1):
|
|
112
|
+
tab_id = f"tab_{idx}"
|
|
113
|
+
|
|
114
|
+
if not tabname:
|
|
115
|
+
tabname = Path(filename).stem or tab_id
|
|
116
|
+
|
|
114
117
|
# Ensure unique tab names
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
counter = 1
|
|
119
|
+
while tabname in seen_names:
|
|
120
|
+
tabname = f"{tabname}_{counter}"
|
|
121
|
+
counter += 1
|
|
117
122
|
seen_names.add(tabname)
|
|
118
123
|
|
|
119
|
-
tab_id = f"tab_{idx}"
|
|
120
124
|
try:
|
|
121
125
|
table = DataFrameTable(df, filename, name=tabname, id=tab_id, zebra_stripes=True)
|
|
122
126
|
tab = TabPane(tabname, table, name=tabname, id=tab_id)
|
|
@@ -300,7 +304,7 @@ class DataFrameViewer(App):
|
|
|
300
304
|
if filename and os.path.exists(filename):
|
|
301
305
|
try:
|
|
302
306
|
n_tab = 0
|
|
303
|
-
for lf, filename, tabname in
|
|
307
|
+
for lf, filename, tabname in load_file(filename, prefix_sheet=True):
|
|
304
308
|
self._add_tab(lf.collect(), filename, tabname)
|
|
305
309
|
n_tab += 1
|
|
306
310
|
self.notify(f"Added [$accent]{n_tab}[/] tab(s) for [$success]{filename}[/]", title="Open")
|
|
@@ -368,105 +372,3 @@ class DataFrameViewer(App):
|
|
|
368
372
|
self.notify(f"Closed tab [$success]{active_pane.name}[/]", title="Close")
|
|
369
373
|
except NoMatches:
|
|
370
374
|
pass
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
def _load_dataframe(
|
|
374
|
-
filenames: list[str], file_format: str | None = None, has_header: bool = True
|
|
375
|
-
) -> list[tuple[pl.LazyFrame, str, str]]:
|
|
376
|
-
"""Load DataFrames from file specifications.
|
|
377
|
-
|
|
378
|
-
Handles loading from multiple files, single files, or stdin. For Excel files,
|
|
379
|
-
loads all sheets as separate entries. For other formats, loads as single file.
|
|
380
|
-
|
|
381
|
-
Args:
|
|
382
|
-
filenames: List of filenames to load. If single filename is "-", read from stdin.
|
|
383
|
-
file_format: Optional format specifier for input files (e.g., 'csv', 'excel').
|
|
384
|
-
has_header: Whether the input files have a header row. Defaults to True.
|
|
385
|
-
|
|
386
|
-
Returns:
|
|
387
|
-
List of tuples of (LazyFrame, filename, tabname) ready for display.
|
|
388
|
-
"""
|
|
389
|
-
sources = []
|
|
390
|
-
|
|
391
|
-
prefix_sheet = len(filenames) > 1
|
|
392
|
-
|
|
393
|
-
for filename in filenames:
|
|
394
|
-
sources.extend(_load_file(filename, prefix_sheet=prefix_sheet, file_format=file_format, has_header=has_header))
|
|
395
|
-
return sources
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
def _load_file(
|
|
399
|
-
filename: str,
|
|
400
|
-
first_sheet: bool = False,
|
|
401
|
-
prefix_sheet: bool = False,
|
|
402
|
-
file_format: str | None = None,
|
|
403
|
-
has_header: bool = True,
|
|
404
|
-
) -> list[tuple[pl.LazyFrame, str, str]]:
|
|
405
|
-
"""Load a single file and return list of sources.
|
|
406
|
-
|
|
407
|
-
For Excel files, when `first_sheet` is True, returns only the first sheet. Otherwise, returns one entry per sheet.
|
|
408
|
-
For other files or multiple files, returns one entry per file.
|
|
409
|
-
|
|
410
|
-
Args:
|
|
411
|
-
filename: Path to file to load.
|
|
412
|
-
first_sheet: If True, only load first sheet for Excel files. Defaults to False.
|
|
413
|
-
prefix_sheet: If True, prefix filename to sheet name as the tab name for Excel files. Defaults to False.
|
|
414
|
-
file_format: Optional format specifier for input files (e.g., 'csv', 'excel', 'tsv', 'parquet', 'json', 'ndjson').
|
|
415
|
-
|
|
416
|
-
Returns:
|
|
417
|
-
List of tuples of (LazyFrame, filename, tabname).
|
|
418
|
-
"""
|
|
419
|
-
sources = []
|
|
420
|
-
|
|
421
|
-
if filename == "-":
|
|
422
|
-
from io import StringIO
|
|
423
|
-
|
|
424
|
-
# Read from stdin into memory first (stdin is not seekable)
|
|
425
|
-
stdin_data = sys.stdin.read()
|
|
426
|
-
lf = pl.scan_csv(StringIO(stdin_data), has_header=has_header, separator="," if file_format == "csv" else "\t")
|
|
427
|
-
|
|
428
|
-
# Reopen stdin to /dev/tty for proper terminal interaction
|
|
429
|
-
try:
|
|
430
|
-
tty = open("/dev/tty")
|
|
431
|
-
os.dup2(tty.fileno(), sys.stdin.fileno())
|
|
432
|
-
except (OSError, FileNotFoundError):
|
|
433
|
-
pass
|
|
434
|
-
|
|
435
|
-
sources.append((lf, "stdin.tsv" if file_format == "tsv" else "stdin.csv", "stdin"))
|
|
436
|
-
return sources
|
|
437
|
-
|
|
438
|
-
filepath = Path(filename)
|
|
439
|
-
ext = filepath.suffix.lower()
|
|
440
|
-
|
|
441
|
-
if file_format == "csv" or ext == ".csv":
|
|
442
|
-
lf = pl.scan_csv(filename, has_header=has_header)
|
|
443
|
-
sources.append((lf, filename, filepath.stem))
|
|
444
|
-
elif file_format == "excel" or ext in (".xlsx", ".xls"):
|
|
445
|
-
if first_sheet:
|
|
446
|
-
# Read only the first sheet for multiple files
|
|
447
|
-
lf = pl.read_excel(filename).lazy()
|
|
448
|
-
sources.append((lf, filename, filepath.stem))
|
|
449
|
-
else:
|
|
450
|
-
# For single file, expand all sheets
|
|
451
|
-
sheets = pl.read_excel(filename, sheet_id=0)
|
|
452
|
-
for sheet_name, df in sheets.items():
|
|
453
|
-
tabname = f"{filepath.stem}_{sheet_name}" if prefix_sheet else sheet_name
|
|
454
|
-
sources.append((df.lazy(), filename, tabname))
|
|
455
|
-
elif file_format == "tsv" or ext in (".tsv", ".tab"):
|
|
456
|
-
lf = pl.scan_csv(filename, has_header=has_header, separator="\t")
|
|
457
|
-
sources.append((lf, filename, filepath.stem))
|
|
458
|
-
elif file_format == "parquet" or ext == ".parquet":
|
|
459
|
-
lf = pl.scan_parquet(filename)
|
|
460
|
-
sources.append((lf, filename, filepath.stem))
|
|
461
|
-
elif file_format == "json" or ext == ".json":
|
|
462
|
-
df = pl.read_json(filename)
|
|
463
|
-
sources.append((df, filename, filepath.stem))
|
|
464
|
-
elif file_format == "ndjson" or ext == ".ndjson":
|
|
465
|
-
lf = pl.scan_ndjson(filename)
|
|
466
|
-
sources.append((lf, filename, filepath.stem))
|
|
467
|
-
else:
|
|
468
|
-
# Treat other formats as TSV
|
|
469
|
-
lf = pl.scan_csv(filename, has_header=has_header, separator="\t")
|
|
470
|
-
sources.append((lf, filename, filepath.stem))
|
|
471
|
-
|
|
472
|
-
return sources
|
|
@@ -13,7 +13,7 @@ from textual.renderables.bar import Bar
|
|
|
13
13
|
from textual.screen import ModalScreen
|
|
14
14
|
from textual.widgets import DataTable
|
|
15
15
|
|
|
16
|
-
from .common import NULL, NULL_DISPLAY, RIDX, DtypeConfig, format_row
|
|
16
|
+
from .common import NULL, NULL_DISPLAY, RIDX, DtypeConfig, format_float, format_row
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class TableScreen(ModalScreen):
|
|
@@ -30,8 +30,10 @@ class TableScreen(ModalScreen):
|
|
|
30
30
|
|
|
31
31
|
TableScreen > DataTable {
|
|
32
32
|
width: auto;
|
|
33
|
-
|
|
33
|
+
height: auto;
|
|
34
34
|
border: solid $primary;
|
|
35
|
+
max-width: 100%;
|
|
36
|
+
overflow: auto;
|
|
35
37
|
}
|
|
36
38
|
"""
|
|
37
39
|
|
|
@@ -271,10 +273,10 @@ class StatisticsScreen(TableScreen):
|
|
|
271
273
|
value = stat_value
|
|
272
274
|
if stat_value is None:
|
|
273
275
|
value = NULL_DISPLAY
|
|
274
|
-
elif dc.gtype == "
|
|
276
|
+
elif dc.gtype == "integer" and self.thousand_separator:
|
|
275
277
|
value = f"{stat_value:,}"
|
|
276
278
|
elif dc.gtype == "float":
|
|
277
|
-
value =
|
|
279
|
+
value = format_float(stat_value, self.thousand_separator)
|
|
278
280
|
else:
|
|
279
281
|
value = str(stat_value)
|
|
280
282
|
|
|
@@ -291,6 +293,10 @@ class StatisticsScreen(TableScreen):
|
|
|
291
293
|
if False in self.dftable.visible_rows:
|
|
292
294
|
lf = lf.filter(self.dftable.visible_rows)
|
|
293
295
|
|
|
296
|
+
# Apply only to non-hidden columns
|
|
297
|
+
if self.dftable.hidden_columns:
|
|
298
|
+
lf = lf.select(pl.exclude(self.dftable.hidden_columns))
|
|
299
|
+
|
|
294
300
|
# Get dataframe statistics
|
|
295
301
|
stats_df = lf.collect().describe()
|
|
296
302
|
|
|
@@ -321,10 +327,10 @@ class StatisticsScreen(TableScreen):
|
|
|
321
327
|
value = stat_value
|
|
322
328
|
if stat_value is None:
|
|
323
329
|
value = NULL_DISPLAY
|
|
324
|
-
elif dc.gtype == "
|
|
330
|
+
elif dc.gtype == "integer" and self.thousand_separator:
|
|
325
331
|
value = f"{stat_value:,}"
|
|
326
332
|
elif dc.gtype == "float":
|
|
327
|
-
value =
|
|
333
|
+
value = format_float(stat_value, self.thousand_separator)
|
|
328
334
|
else:
|
|
329
335
|
value = str(stat_value)
|
|
330
336
|
|
|
@@ -413,10 +419,10 @@ class FrequencyScreen(TableScreen):
|
|
|
413
419
|
|
|
414
420
|
if column is None:
|
|
415
421
|
value = NULL_DISPLAY
|
|
416
|
-
elif dc.gtype == "
|
|
422
|
+
elif dc.gtype == "integer" and self.thousand_separator:
|
|
417
423
|
value = f"{column:,}"
|
|
418
424
|
elif dc.gtype == "float":
|
|
419
|
-
value =
|
|
425
|
+
value = format_float(column, self.thousand_separator)
|
|
420
426
|
else:
|
|
421
427
|
value = str(column)
|
|
422
428
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframe-textual
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: Interactive
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Interactive terminal viewer/editor for tabular data
|
|
5
5
|
Project-URL: Homepage, https://github.com/need47/dataframe-textual
|
|
6
6
|
Project-URL: Repository, https://github.com/need47/dataframe-textual.git
|
|
7
7
|
Project-URL: Documentation, https://github.com/need47/dataframe-textual#readme
|
|
@@ -9,7 +9,7 @@ Project-URL: Bug Tracker, https://github.com/need47/dataframe-textual/issues
|
|
|
9
9
|
Author-email: Tiejun Cheng <need47@gmail.com>
|
|
10
10
|
License: MIT
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Keywords: csv,data-analysis,excel,interactive,polars,terminal,textual,tui,viewer
|
|
12
|
+
Keywords: csv,data-analysis,editor,excel,interactive,polars,terminal,textual,tui,viewer
|
|
13
13
|
Classifier: Development Status :: 3 - Alpha
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -29,7 +29,7 @@ Classifier: Topic :: Utilities
|
|
|
29
29
|
Classifier: Typing :: Typed
|
|
30
30
|
Requires-Python: >=3.11
|
|
31
31
|
Requires-Dist: polars>=1.34.0
|
|
32
|
-
Requires-Dist: textual>=
|
|
32
|
+
Requires-Dist: textual>=6.5.0
|
|
33
33
|
Provides-Extra: dev
|
|
34
34
|
Requires-Dist: textual-dev>=1.8.0; extra == 'dev'
|
|
35
35
|
Provides-Extra: excel
|
|
@@ -45,11 +45,11 @@ A powerful, interactive terminal-based viewer/editor for CSV/TSV/Excel/Parquet/J
|
|
|
45
45
|
|
|
46
46
|
## Features
|
|
47
47
|
|
|
48
|
-
###
|
|
48
|
+
### Data Viewing
|
|
49
49
|
- 🚀 **Fast Loading** - Powered by Polars for efficient data handling
|
|
50
|
-
- 🎨 **Rich Terminal UI** - Beautiful, color-coded columns with
|
|
50
|
+
- 🎨 **Rich Terminal UI** - Beautiful, color-coded columns with various data types (e.g., integer, float, string)
|
|
51
51
|
- ⌨️ **Comprehensive Keyboard Navigation** - Intuitive controls for browsing, editing, and manipulating data
|
|
52
|
-
- 📊 **Flexible Input** - Read from files or stdin (pipes/redirects)
|
|
52
|
+
- 📊 **Flexible Input** - Read from files and/or stdin (pipes/redirects)
|
|
53
53
|
- 🔄 **Smart Pagination** - Lazy load rows on demand for handling large datasets
|
|
54
54
|
|
|
55
55
|
### Data Manipulation
|
|
@@ -60,7 +60,7 @@ A powerful, interactive terminal-based viewer/editor for CSV/TSV/Excel/Parquet/J
|
|
|
60
60
|
- 💾 **Save & Undo** - Save edits back to file with full undo/redo support
|
|
61
61
|
|
|
62
62
|
### Advanced Features
|
|
63
|
-
- 📂 **Multi-File Support** - Open multiple files in tabs
|
|
63
|
+
- 📂 **Multi-File Support** - Open multiple files in separate tabs
|
|
64
64
|
- 🔄 **Tab Management** - Seamlessly switch between open files with keyboard shortcuts
|
|
65
65
|
- 📌 **Freeze Rows/Columns** - Keep important rows and columns visible while scrolling
|
|
66
66
|
- 🎯 **Cursor Type Cycling** - Switch between cell, row, and column selection modes
|
|
@@ -77,9 +77,11 @@ pip install dataframe-textual
|
|
|
77
77
|
pip install dataframe-textual[excel]
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
This installs an executable `dv`.
|
|
81
|
+
|
|
80
82
|
Then run:
|
|
81
83
|
```bash
|
|
82
|
-
|
|
84
|
+
dv <csv_file>
|
|
83
85
|
```
|
|
84
86
|
|
|
85
87
|
### Using [uv](https://docs.astral.sh/uv/)
|
|
@@ -90,9 +92,10 @@ uvx https://github.com/need47/dataframe-textual.git <csvfile>
|
|
|
90
92
|
|
|
91
93
|
# Clone or download the project
|
|
92
94
|
cd dataframe-textual
|
|
95
|
+
uv sync --extra excel # with Excel support
|
|
93
96
|
|
|
94
97
|
# Run directly with uv
|
|
95
|
-
uv run
|
|
98
|
+
uv run dv <csv_file>
|
|
96
99
|
```
|
|
97
100
|
|
|
98
101
|
### Development installation
|
|
@@ -115,7 +118,7 @@ pip install -e ".[excel,dev]"
|
|
|
115
118
|
|
|
116
119
|
```bash
|
|
117
120
|
# After pip install dataframe-textual
|
|
118
|
-
|
|
121
|
+
dv pokemon.csv
|
|
119
122
|
|
|
120
123
|
# Or if running from source
|
|
121
124
|
python main.py pokemon.csv
|
|
@@ -124,21 +127,21 @@ python main.py pokemon.csv
|
|
|
124
127
|
uv run python main.py pokemon.csv
|
|
125
128
|
|
|
126
129
|
# Read from stdin (auto-detects format; defaults to TSV if not recognized)
|
|
127
|
-
cat data.tsv |
|
|
128
|
-
|
|
130
|
+
cat data.tsv | dv
|
|
131
|
+
dv < data.tsv
|
|
129
132
|
```
|
|
130
133
|
|
|
131
134
|
### Multi-File Usage - Multiple Tabs
|
|
132
135
|
|
|
133
136
|
```bash
|
|
134
137
|
# Open multiple files in tabs
|
|
135
|
-
|
|
138
|
+
dv file1.csv file2.csv file3.csv
|
|
136
139
|
|
|
137
140
|
# Open multiple sheets in tabs in an Excel file
|
|
138
|
-
|
|
141
|
+
dv file.xlsx
|
|
139
142
|
|
|
140
143
|
# Mix files and stdin (read from stdin, then open file)
|
|
141
|
-
|
|
144
|
+
dv data1.tsv < data2.tsv
|
|
142
145
|
```
|
|
143
146
|
|
|
144
147
|
When multiple files are opened:
|
|
@@ -203,17 +206,21 @@ When multiple files are opened:
|
|
|
203
206
|
| Key | Action |
|
|
204
207
|
|-----|--------|
|
|
205
208
|
| `Double-click` | Edit cell or rename column header |
|
|
206
|
-
| `
|
|
209
|
+
| `delete` | Clear current cell (set to NULL) |
|
|
207
210
|
| `e` | Edit current cell (respects data type) |
|
|
208
211
|
| `E` | Edit entire column with expression |
|
|
209
212
|
| `a` | Add empty column after current |
|
|
210
213
|
| `A` | Add column with name and value/expression |
|
|
211
214
|
| `-` (minus) | Delete current column |
|
|
215
|
+
| `_` (underscore) | Delete current column and all columns after |
|
|
216
|
+
| `Ctrl+-` | Delete current column and all columns before |
|
|
212
217
|
| `x` | Delete current row |
|
|
218
|
+
| `X` | Delete current row and all rows below |
|
|
219
|
+
| `Ctrl+X` | Delete current row and all rows above |
|
|
213
220
|
| `d` | Duplicate current column (appends '_copy' suffix) |
|
|
214
221
|
| `D` | Duplicate current row |
|
|
215
222
|
| `h` | Hide current column |
|
|
216
|
-
| `H` | Show all hidden columns |
|
|
223
|
+
| `H` | Show all hidden rows/columns |
|
|
217
224
|
|
|
218
225
|
#### Searching & Filtering
|
|
219
226
|
|
|
@@ -280,7 +287,8 @@ When multiple files are opened:
|
|
|
280
287
|
| `Ctrl+R` | Copy row to clipboard (tab-separated) |
|
|
281
288
|
| `Ctrl+S` | Save current tab to file |
|
|
282
289
|
| `u` | Undo last action |
|
|
283
|
-
| `U` |
|
|
290
|
+
| `U` | Redo last undone action |
|
|
291
|
+
| `Ctrl+U` | Reset to initial state |
|
|
284
292
|
|
|
285
293
|
## Features in Detail
|
|
286
294
|
|
|
@@ -509,7 +517,7 @@ Press `F` to see how many times each value appears in the current column. The mo
|
|
|
509
517
|
**In the Frequency Table**:
|
|
510
518
|
- Press `[` and `]` to sort by any column (value, count, or percentage)
|
|
511
519
|
- Press `v` to **filter** the main table to show only rows with the selected value
|
|
512
|
-
- Press `"` to **
|
|
520
|
+
- Press `"` to **exclude** all rows except those containing the selected value
|
|
513
521
|
- Press `q` or `Escape` to close the frequency table
|
|
514
522
|
|
|
515
523
|
This is useful for:
|
|
@@ -557,9 +565,25 @@ This is useful for:
|
|
|
557
565
|
- Delete all selected rows (if any) at once
|
|
558
566
|
- Or delete single row at cursor
|
|
559
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
|
+
|
|
560
576
|
**Delete Column** (`-`):
|
|
561
577
|
- Removes the entire column from view and dataframe
|
|
562
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
|
+
|
|
563
587
|
### 9. Hide & Show Columns
|
|
564
588
|
|
|
565
589
|
**Hide Column** (`h`):
|
|
@@ -567,9 +591,8 @@ This is useful for:
|
|
|
567
591
|
- Column data is preserved in the dataframe
|
|
568
592
|
- Hidden columns are included in saves
|
|
569
593
|
|
|
570
|
-
**Show Hidden Columns** (`H`):
|
|
571
|
-
- Restores all previously hidden columns to the display
|
|
572
|
-
- Returns table to full column view
|
|
594
|
+
**Show Hidden Rows/Columns** (`H`):
|
|
595
|
+
- Restores all previously hidden rows/columns to the display
|
|
573
596
|
|
|
574
597
|
This is useful for:
|
|
575
598
|
- Focusing on specific columns without deleting data
|
|
@@ -631,14 +654,23 @@ Press `Ctrl+S` to save:
|
|
|
631
654
|
- Choose filename in modal dialog
|
|
632
655
|
- Confirm if file already exists
|
|
633
656
|
|
|
634
|
-
### 15. Undo/Redo
|
|
657
|
+
### 15. Undo/Redo/Reset
|
|
635
658
|
|
|
636
|
-
|
|
659
|
+
**Undo** (`u`):
|
|
637
660
|
- Reverts last action with full state restoration
|
|
638
661
|
- Works for edits, deletions, sorts, searches, etc.
|
|
639
662
|
- Shows description of reverted action
|
|
640
663
|
|
|
641
|
-
|
|
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
|
|
642
674
|
|
|
643
675
|
### 16. Column Type Conversion
|
|
644
676
|
|
|
@@ -686,32 +718,23 @@ Press `Ctrl+C` to copy:
|
|
|
686
718
|
|
|
687
719
|
```bash
|
|
688
720
|
# View Pokemon dataset
|
|
689
|
-
|
|
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
|
|
721
|
+
dv pokemon.csv
|
|
699
722
|
|
|
700
|
-
# Chain with other
|
|
701
|
-
|
|
723
|
+
# Chain with other command and specify input file format
|
|
724
|
+
cut -d',' -f1,2,3 pokemon.csv | dv -f csv
|
|
702
725
|
```
|
|
703
726
|
|
|
704
727
|
### Multi-File/Tab Examples
|
|
705
728
|
|
|
706
729
|
```bash
|
|
707
730
|
# Open multiple sheets as tabs in a single Excel
|
|
708
|
-
|
|
731
|
+
dv sales.xlsx
|
|
709
732
|
|
|
710
733
|
# Open multiple files as tabs
|
|
711
|
-
|
|
734
|
+
dv pokemon.csv titanic.csv
|
|
712
735
|
|
|
713
736
|
# Start with one file, then open others using Ctrl+O
|
|
714
|
-
|
|
737
|
+
dv initial_data.csv
|
|
715
738
|
```
|
|
716
739
|
|
|
717
740
|
## Dependencies
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
dataframe_textual/__init__.py,sha256=IFPb8RMUgghw0eRomehkkC684Iny_gs1VkiZMQ5ZpFk,813
|
|
2
|
+
dataframe_textual/__main__.py,sha256=hgjKLT3ggGxVVeRBunkArTy7PTqOHVJUf3fsH4P5dfU,2146
|
|
3
|
+
dataframe_textual/common.py,sha256=q4bXS7oiJAsdcMEfHkPm5-e8SlfcwCBNhFN9TinQqV0,16171
|
|
4
|
+
dataframe_textual/data_frame_help_panel.py,sha256=XgKGEPJr2hnDWpZ5mavLRcBSPa9cvrXdzVUGFQavXm4,3353
|
|
5
|
+
dataframe_textual/data_frame_table.py,sha256=UbUlPwrcLbN_yNz9ECMgKHypTgVV5qDLvSQUJsDby8Q,109452
|
|
6
|
+
dataframe_textual/data_frame_viewer.py,sha256=4mV3k7MNTf9TKBmGJ8fDx7itA1vo4qSmaWpvZozwfjs,12987
|
|
7
|
+
dataframe_textual/table_screen.py,sha256=KCmvKAdHexIFQKGob6WMPrcAITHO6CMVpRJzzs0pbrE,17793
|
|
8
|
+
dataframe_textual/yes_no_screen.py,sha256=vyUKMBbbwgt5At1U430eLg3WbJvqUNoz2GpvdnMd7q0,22921
|
|
9
|
+
dataframe_textual-1.2.0.dist-info/METADATA,sha256=zLQtFr7h6-7fZ5dJoNYjGX5cghyKpGDKX0c7-wSbUy0,26688
|
|
10
|
+
dataframe_textual-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
+
dataframe_textual-1.2.0.dist-info/entry_points.txt,sha256=Z0XKp1ACvmbJymekkxt-C81I0OoInksr5Ib0w2OT_a4,55
|
|
12
|
+
dataframe_textual-1.2.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
|
|
13
|
+
dataframe_textual-1.2.0.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
dataframe_textual/__init__.py,sha256=uzB3bjlbm8JbsjxEgwqvPcYERktm3F9d9Op_6cWJ1sk,853
|
|
2
|
-
dataframe_textual/__main__.py,sha256=ANe7s2SKO53ksFg-0VrCT2GRCG48wDSAZsLrWvoQwmQ,2082
|
|
3
|
-
dataframe_textual/common.py,sha256=XVTzv565MnxD8B7tXKJSprhV5oOwvW_rv0RzMA9icmk,10896
|
|
4
|
-
dataframe_textual/data_frame_help_panel.py,sha256=XgKGEPJr2hnDWpZ5mavLRcBSPa9cvrXdzVUGFQavXm4,3353
|
|
5
|
-
dataframe_textual/data_frame_table.py,sha256=NzxtsOpSj6s-ToP5PBKlCHcSK3Fj5Q1eucUnQfTWLzQ,102638
|
|
6
|
-
dataframe_textual/data_frame_viewer.py,sha256=9vUBdIgSoOf4fdDtI4sAPY6cF1tFdsXwpWatyDsuXh0,17196
|
|
7
|
-
dataframe_textual/table_screen.py,sha256=qcfaSaDlM9Jb6793boyXt8HbHqM21-6O6eWwYn9z9l4,17624
|
|
8
|
-
dataframe_textual/yes_no_screen.py,sha256=vyUKMBbbwgt5At1U430eLg3WbJvqUNoz2GpvdnMd7q0,22921
|
|
9
|
-
dataframe_textual-1.0.0.dist-info/METADATA,sha256=SoeKzKEYNlKM2eKh_xzzlMegW83SDqkrXv_UouPQDsI,25753
|
|
10
|
-
dataframe_textual-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
-
dataframe_textual-1.0.0.dist-info/entry_points.txt,sha256=FkXDHVYYtGud6F2Jm2X9OMFAuFrSflNfgcNP5c2469M,70
|
|
12
|
-
dataframe_textual-1.0.0.dist-info/licenses/LICENSE,sha256=AVTg0gk1X-LHI-nnHlAMDQetrwuDZK4eypgSMDO46Yc,1069
|
|
13
|
-
dataframe_textual-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|