4
4
import functools
5
5
from datetime import datetime , timedelta
6
6
from itertools import chain , zip_longest
7
- from typing import Hashable
7
+ from typing import Collection , Hashable , Optional
8
8
9
9
import numpy as np
10
10
import pandas as pd
@@ -97,6 +97,16 @@ def last_item(array):
97
97
return np .ravel (np .asarray (array [indexer ])).tolist ()
98
98
99
99
100
+ def calc_max_rows_first (max_rows : int ) -> int :
101
+ """Calculate the first rows to maintain the max number of rows."""
102
+ return max_rows // 2 + max_rows % 2
103
+
104
+
105
+ def calc_max_rows_last (max_rows : int ) -> int :
106
+ """Calculate the last rows to maintain the max number of rows."""
107
+ return max_rows // 2
108
+
109
+
100
110
def format_timestamp (t ):
101
111
"""Cast given object to a Timestamp and return a nicely formatted string"""
102
112
# Timestamp is only valid for 1678 to 2262
@@ -384,11 +394,11 @@ def _mapping_repr(
384
394
summary = [f"{ summary [0 ]} ({ len_mapping } )" ]
385
395
elif max_rows is not None and len_mapping > max_rows :
386
396
summary = [f"{ summary [0 ]} ({ max_rows } /{ len_mapping } )" ]
387
- first_rows = max_rows // 2 + max_rows % 2
397
+ first_rows = calc_max_rows_first ( max_rows )
388
398
keys = list (mapping .keys ())
389
399
summary += [summarizer (k , mapping [k ], col_width ) for k in keys [:first_rows ]]
390
400
if max_rows > 1 :
391
- last_rows = max_rows // 2
401
+ last_rows = calc_max_rows_last ( max_rows )
392
402
summary += [pretty_print (" ..." , col_width ) + " ..." ]
393
403
summary += [
394
404
summarizer (k , mapping [k ], col_width ) for k in keys [- last_rows :]
@@ -441,11 +451,74 @@ def dim_summary(obj):
441
451
return ", " .join (elements )
442
452
443
453
444
- def unindexed_dims_repr (dims , coords ):
454
+ def _element_formatter (
455
+ elements : Collection [Hashable ],
456
+ col_width : int ,
457
+ max_rows : Optional [int ] = None ,
458
+ delimiter : str = ", " ,
459
+ ) -> str :
460
+ """
461
+ Formats elements for better readability.
462
+
463
+ Once it becomes wider than the display width it will create a newline and
464
+ continue indented to col_width.
465
+ Once there are more rows than the maximum displayed rows it will start
466
+ removing rows.
467
+
468
+ Parameters
469
+ ----------
470
+ elements : Collection of hashable
471
+ Elements to join together.
472
+ col_width : int
473
+ The width to indent to if a newline has been made.
474
+ max_rows : int, optional
475
+ The maximum number of allowed rows. The default is None.
476
+ delimiter : str, optional
477
+ Delimiter to use between each element. The default is ", ".
478
+ """
479
+ elements_len = len (elements )
480
+ out = ["" ]
481
+ length_row = 0
482
+ for i , v in enumerate (elements ):
483
+ delim = delimiter if i < elements_len - 1 else ""
484
+ v_delim = f"{ v } { delim } "
485
+ length_element = len (v_delim )
486
+ length_row += length_element
487
+
488
+ # Create a new row if the next elements makes the print wider than
489
+ # the maximum display width:
490
+ if col_width + length_row > OPTIONS ["display_width" ]:
491
+ out [- 1 ] = out [- 1 ].rstrip () # Remove trailing whitespace.
492
+ out .append ("\n " + pretty_print ("" , col_width ) + v_delim )
493
+ length_row = length_element
494
+ else :
495
+ out [- 1 ] += v_delim
496
+
497
+ # If there are too many rows of dimensions trim some away:
498
+ if max_rows and (len (out ) > max_rows ):
499
+ first_rows = calc_max_rows_first (max_rows )
500
+ last_rows = calc_max_rows_last (max_rows )
501
+ out = (
502
+ out [:first_rows ]
503
+ + ["\n " + pretty_print ("" , col_width ) + "..." ]
504
+ + (out [- last_rows :] if max_rows > 1 else [])
505
+ )
506
+ return "" .join (out )
507
+
508
+
509
+ def dim_summary_limited (obj , col_width : int , max_rows : Optional [int ] = None ) -> str :
510
+ elements = [f"{ k } : { v } " for k , v in obj .sizes .items ()]
511
+ return _element_formatter (elements , col_width , max_rows )
512
+
513
+
514
+ def unindexed_dims_repr (dims , coords , max_rows : Optional [int ] = None ):
445
515
unindexed_dims = [d for d in dims if d not in coords ]
446
516
if unindexed_dims :
447
- dims_str = ", " .join (f"{ d } " for d in unindexed_dims )
448
- return "Dimensions without coordinates: " + dims_str
517
+ dims_start = "Dimensions without coordinates: "
518
+ dims_str = _element_formatter (
519
+ unindexed_dims , col_width = len (dims_start ), max_rows = max_rows
520
+ )
521
+ return dims_start + dims_str
449
522
else :
450
523
return None
451
524
@@ -505,6 +578,8 @@ def short_data_repr(array):
505
578
def array_repr (arr ):
506
579
from .variable import Variable
507
580
581
+ max_rows = OPTIONS ["display_max_rows" ]
582
+
508
583
# used for DataArray, Variable and IndexVariable
509
584
if hasattr (arr , "name" ) and arr .name is not None :
510
585
name_str = f"{ arr .name !r} "
@@ -520,16 +595,23 @@ def array_repr(arr):
520
595
else :
521
596
data_repr = inline_variable_array_repr (arr .variable , OPTIONS ["display_width" ])
522
597
598
+ start = f"<xarray.{ type (arr ).__name__ } { name_str } "
599
+ dims = dim_summary_limited (arr , col_width = len (start ) + 1 , max_rows = max_rows )
523
600
summary = [
524
- "<xarray.{} {}({ })>". format ( type ( arr ). __name__ , name_str , dim_summary ( arr )) ,
601
+ f" { start } ( { dims } )>" ,
525
602
data_repr ,
526
603
]
527
604
528
605
if hasattr (arr , "coords" ):
529
606
if arr .coords :
530
- summary .append (repr (arr .coords ))
607
+ col_width = _calculate_col_width (_get_col_items (arr .coords ))
608
+ summary .append (
609
+ coords_repr (arr .coords , col_width = col_width , max_rows = max_rows )
610
+ )
531
611
532
- unindexed_dims_str = unindexed_dims_repr (arr .dims , arr .coords )
612
+ unindexed_dims_str = unindexed_dims_repr (
613
+ arr .dims , arr .coords , max_rows = max_rows
614
+ )
533
615
if unindexed_dims_str :
534
616
summary .append (unindexed_dims_str )
535
617
@@ -546,12 +628,13 @@ def dataset_repr(ds):
546
628
max_rows = OPTIONS ["display_max_rows" ]
547
629
548
630
dims_start = pretty_print ("Dimensions:" , col_width )
549
- summary .append ("{}({})" .format (dims_start , dim_summary (ds )))
631
+ dims_values = dim_summary_limited (ds , col_width = col_width + 1 , max_rows = max_rows )
632
+ summary .append (f"{ dims_start } ({ dims_values } )" )
550
633
551
634
if ds .coords :
552
635
summary .append (coords_repr (ds .coords , col_width = col_width , max_rows = max_rows ))
553
636
554
- unindexed_dims_str = unindexed_dims_repr (ds .dims , ds .coords )
637
+ unindexed_dims_str = unindexed_dims_repr (ds .dims , ds .coords , max_rows = max_rows )
555
638
if unindexed_dims_str :
556
639
summary .append (unindexed_dims_str )
557
640
0 commit comments