Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/io/formats/info.py: 49%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

353 statements  

1from __future__ import annotations 

2 

3from abc import ( 

4 ABC, 

5 abstractmethod, 

6) 

7import sys 

8from textwrap import dedent 

9from typing import TYPE_CHECKING 

10 

11from pandas._config import get_option 

12 

13from pandas.io.formats import format as fmt 

14from pandas.io.formats.printing import pprint_thing 

15 

16if TYPE_CHECKING: 

17 from collections.abc import ( 

18 Iterable, 

19 Iterator, 

20 Mapping, 

21 Sequence, 

22 ) 

23 

24 from pandas._typing import ( 

25 Dtype, 

26 WriteBuffer, 

27 ) 

28 

29 from pandas import ( 

30 DataFrame, 

31 Index, 

32 Series, 

33 ) 

34 

35show_counts_sub = dedent( 

36 """\ 

37 show_counts : bool, optional 

38 Whether to show the non-null counts. By default, this is shown 

39 only if the DataFrame is smaller than 

40 ``pandas.options.display.max_info_rows`` and 

41 ``pandas.options.display.max_info_columns``. A value of True always 

42 shows the counts, and False never shows the counts.""" 

43) 

44 

45series_examples_sub = dedent( 

46 """\ 

47 >>> int_values = [1, 2, 3, 4, 5] 

48 >>> text_values = ['alpha', 'beta', 'gamma', 'delta', 'epsilon'] 

49 >>> s = pd.Series(text_values, index=int_values) 

50 >>> s.info() 

51 <class 'pandas.Series'> 

52 Index: 5 entries, 1 to 5 

53 Series name: None 

54 Non-Null Count Dtype 

55 -------------- ----- 

56 5 non-null object 

57 dtypes: object(1) 

58 memory usage: 80.0+ bytes 

59 

60 Prints a summary excluding information about its values: 

61 

62 >>> s.info(verbose=False) 

63 <class 'pandas.Series'> 

64 Index: 5 entries, 1 to 5 

65 dtypes: object(1) 

66 memory usage: 80.0+ bytes 

67 

68 Pipe output of Series.info to buffer instead of sys.stdout, get 

69 buffer content and writes to a text file: 

70 

71 >>> import io 

72 >>> buffer = io.StringIO() 

73 >>> s.info(buf=buffer) 

74 >>> s = buffer.getvalue() 

75 >>> with open("df_info.txt", "w", 

76 ... encoding="utf-8") as f: # doctest: +SKIP 

77 ... f.write(s) 

78 260 

79 

80 The `memory_usage` parameter allows deep introspection mode, specially 

81 useful for big Series and fine-tune memory optimization: 

82 

83 >>> random_strings_array = np.random.choice(['a', 'b', 'c'], 10 ** 6) 

84 >>> s = pd.Series(np.random.choice(['a', 'b', 'c'], 10 ** 6)) 

85 >>> s.info() 

86 <class 'pandas.Series'> 

87 RangeIndex: 1000000 entries, 0 to 999999 

88 Series name: None 

89 Non-Null Count Dtype 

90 -------------- ----- 

91 1000000 non-null object 

92 dtypes: object(1) 

93 memory usage: 7.6+ MB 

94 

95 >>> s.info(memory_usage='deep') 

96 <class 'pandas.Series'> 

97 RangeIndex: 1000000 entries, 0 to 999999 

98 Series name: None 

99 Non-Null Count Dtype 

100 -------------- ----- 

101 1000000 non-null object 

102 dtypes: object(1) 

103 memory usage: 55.3 MB""" 

104) 

105 

106 

107series_see_also_sub = dedent( 

108 """\ 

109 Series.describe: Generate descriptive statistics of Series. 

110 Series.memory_usage: Memory usage of Series.""" 

111) 

112series_max_cols_sub = dedent( 

113 """\ 

114 max_cols : int, optional 

115 Unused, exists only for compatibility with DataFrame.info.""" 

116) 

117 

118 

119series_sub_kwargs = { 

120 "klass": "Series", 

121 "type_sub": "", 

122 "max_cols_sub": series_max_cols_sub, 

123 "show_counts_sub": show_counts_sub, 

124 "examples_sub": series_examples_sub, 

125 "see_also_sub": series_see_also_sub, 

126 "version_added_sub": "\n.. versionadded:: 1.4.0\n", 

127} 

128 

129 

130def _put_str(s: str | Dtype, space: int) -> str: 

131 """ 

132 Make string of specified length, padding to the right if necessary. 

133 

134 Parameters 

135 ---------- 

136 s : Union[str, Dtype] 

137 String to be formatted. 

138 space : int 

139 Length to force string to be of. 

140 

141 Returns 

142 ------- 

143 str 

144 String coerced to given length. 

145 

146 Examples 

147 -------- 

148 >>> pd.io.formats.info._put_str("panda", 6) 

149 'panda ' 

150 >>> pd.io.formats.info._put_str("panda", 4) 

151 'pand' 

152 """ 

153 return str(s)[:space].ljust(space) 

154 

155 

156def _sizeof_fmt(num: float, size_qualifier: str) -> str: 

157 """ 

158 Return size in human readable format. 

159 

160 Parameters 

161 ---------- 

162 num : int 

163 Size in bytes. 

164 size_qualifier : str 

165 Either empty, or '+' (if lower bound). 

166 

167 Returns 

168 ------- 

169 str 

170 Size in human readable format. 

171 

172 Examples 

173 -------- 

174 >>> _sizeof_fmt(23028, "") 

175 '22.5 KB' 

176 

177 >>> _sizeof_fmt(23028, "+") 

178 '22.5+ KB' 

179 """ 

180 for x in ["bytes", "KB", "MB", "GB", "TB"]: 

181 if num < 1024.0: 

182 return f"{num:3.1f}{size_qualifier} {x}" 

183 num /= 1024.0 

184 return f"{num:3.1f}{size_qualifier} PB" 

185 

186 

187def _initialize_memory_usage( 

188 memory_usage: bool | str | None = None, 

189) -> bool | str: 

190 """Get memory usage based on inputs and display options.""" 

191 if memory_usage is None: 

192 memory_usage = get_option("display.memory_usage") 

193 return memory_usage 

194 

195 

196class _BaseInfo(ABC): 

197 """ 

198 Base class for DataFrameInfo and SeriesInfo. 

199 

200 Parameters 

201 ---------- 

202 data : DataFrame or Series 

203 Either dataframe or series. 

204 memory_usage : bool or str, optional 

205 If "deep", introspect the data deeply by interrogating object dtypes 

206 for system-level memory consumption, and include it in the returned 

207 values. 

208 """ 

209 

210 data: DataFrame | Series 

211 memory_usage: bool | str 

212 

213 @property 

214 @abstractmethod 

215 def dtypes(self) -> Iterable[Dtype]: 

216 """ 

217 Dtypes. 

218 

219 Returns 

220 ------- 

221 dtypes : sequence 

222 Dtype of each of the DataFrame's columns (or one series column). 

223 """ 

224 

225 @property 

226 @abstractmethod 

227 def dtype_counts(self) -> Mapping[str, int]: 

228 """Mapping dtype - number of counts.""" 

229 

230 @property 

231 @abstractmethod 

232 def non_null_counts(self) -> list[int] | Series: 

233 """Sequence of non-null counts for all columns or column (if series).""" 

234 

235 @property 

236 @abstractmethod 

237 def memory_usage_bytes(self) -> int: 

238 """ 

239 Memory usage in bytes. 

240 

241 Returns 

242 ------- 

243 memory_usage_bytes : int 

244 Object's total memory usage in bytes. 

245 """ 

246 

247 @property 

248 def memory_usage_string(self) -> str: 

249 """Memory usage in a form of human readable string.""" 

250 return f"{_sizeof_fmt(self.memory_usage_bytes, self.size_qualifier)}\n" 

251 

252 @property 

253 def size_qualifier(self) -> str: 

254 size_qualifier = "" 

255 if self.memory_usage: 

256 if self.memory_usage != "deep": 

257 # size_qualifier is just a best effort; not guaranteed to catch 

258 # all cases (e.g., it misses categorical data even with object 

259 # categories) 

260 if ( 

261 "object" in self.dtype_counts 

262 or self.data.index._is_memory_usage_qualified 

263 ): 

264 size_qualifier = "+" 

265 return size_qualifier 

266 

267 @abstractmethod 

268 def render( 

269 self, 

270 *, 

271 buf: WriteBuffer[str] | None, 

272 max_cols: int | None, 

273 verbose: bool | None, 

274 show_counts: bool | None, 

275 ) -> None: 

276 pass 

277 

278 

279class DataFrameInfo(_BaseInfo): 

280 """ 

281 Class storing dataframe-specific info. 

282 """ 

283 

284 def __init__( 

285 self, 

286 data: DataFrame, 

287 memory_usage: bool | str | None = None, 

288 ) -> None: 

289 self.data: DataFrame = data 

290 self.memory_usage = _initialize_memory_usage(memory_usage) 

291 

292 @property 

293 def dtype_counts(self) -> Mapping[str, int]: 

294 return _get_dataframe_dtype_counts(self.data) 

295 

296 @property 

297 def dtypes(self) -> Iterable[Dtype]: 

298 """ 

299 Dtypes. 

300 

301 Returns 

302 ------- 

303 dtypes 

304 Dtype of each of the DataFrame's columns. 

305 """ 

306 return self.data.dtypes 

307 

308 @property 

309 def ids(self) -> Index: 

310 """ 

311 Column names. 

312 

313 Returns 

314 ------- 

315 ids : Index 

316 DataFrame's column names. 

317 """ 

318 return self.data.columns 

319 

320 @property 

321 def col_count(self) -> int: 

322 """Number of columns to be summarized.""" 

323 return len(self.ids) 

324 

325 @property 

326 def non_null_counts(self) -> Series: 

327 """Sequence of non-null counts for all columns or column (if series).""" 

328 return self.data.count() 

329 

330 @property 

331 def memory_usage_bytes(self) -> int: 

332 deep = self.memory_usage == "deep" 

333 return self.data.memory_usage(index=True, deep=deep).sum() 

334 

335 def render( 

336 self, 

337 *, 

338 buf: WriteBuffer[str] | None, 

339 max_cols: int | None, 

340 verbose: bool | None, 

341 show_counts: bool | None, 

342 ) -> None: 

343 printer = _DataFrameInfoPrinter( 

344 info=self, 

345 max_cols=max_cols, 

346 verbose=verbose, 

347 show_counts=show_counts, 

348 ) 

349 printer.to_buffer(buf) 

350 

351 

352class SeriesInfo(_BaseInfo): 

353 """ 

354 Class storing series-specific info. 

355 """ 

356 

357 def __init__( 

358 self, 

359 data: Series, 

360 memory_usage: bool | str | None = None, 

361 ) -> None: 

362 self.data: Series = data 

363 self.memory_usage = _initialize_memory_usage(memory_usage) 

364 

365 def render( 

366 self, 

367 *, 

368 buf: WriteBuffer[str] | None = None, 

369 max_cols: int | None = None, 

370 verbose: bool | None = None, 

371 show_counts: bool | None = None, 

372 ) -> None: 

373 if max_cols is not None: 

374 raise ValueError( 

375 "Argument `max_cols` can only be passed " 

376 "in DataFrame.info, not Series.info" 

377 ) 

378 printer = _SeriesInfoPrinter( 

379 info=self, 

380 verbose=verbose, 

381 show_counts=show_counts, 

382 ) 

383 printer.to_buffer(buf) 

384 

385 @property 

386 def non_null_counts(self) -> list[int]: 

387 return [self.data.count()] 

388 

389 @property 

390 def dtypes(self) -> Iterable[Dtype]: 

391 return [self.data.dtypes] 

392 

393 @property 

394 def dtype_counts(self) -> Mapping[str, int]: 

395 from pandas.core.frame import DataFrame 

396 

397 return _get_dataframe_dtype_counts(DataFrame(self.data)) 

398 

399 @property 

400 def memory_usage_bytes(self) -> int: 

401 """Memory usage in bytes. 

402 

403 Returns 

404 ------- 

405 memory_usage_bytes : int 

406 Object's total memory usage in bytes. 

407 """ 

408 deep = self.memory_usage == "deep" 

409 return self.data.memory_usage(index=True, deep=deep) 

410 

411 

412class _InfoPrinterAbstract: 

413 """ 

414 Class for printing dataframe or series info. 

415 """ 

416 

417 def to_buffer(self, buf: WriteBuffer[str] | None = None) -> None: 

418 """Save dataframe info into buffer.""" 

419 table_builder = self._create_table_builder() 

420 lines = table_builder.get_lines() 

421 if buf is None: # pragma: no cover 

422 buf = sys.stdout 

423 fmt.buffer_put_lines(buf, lines) 

424 

425 @abstractmethod 

426 def _create_table_builder(self) -> _TableBuilderAbstract: 

427 """Create instance of table builder.""" 

428 

429 

430class _DataFrameInfoPrinter(_InfoPrinterAbstract): 

431 """ 

432 Class for printing dataframe info. 

433 

434 Parameters 

435 ---------- 

436 info : DataFrameInfo 

437 Instance of DataFrameInfo. 

438 max_cols : int, optional 

439 When to switch from the verbose to the truncated output. 

440 verbose : bool, optional 

441 Whether to print the full summary. 

442 show_counts : bool, optional 

443 Whether to show the non-null counts. 

444 """ 

445 

446 def __init__( 

447 self, 

448 info: DataFrameInfo, 

449 max_cols: int | None = None, 

450 verbose: bool | None = None, 

451 show_counts: bool | None = None, 

452 ) -> None: 

453 self.info = info 

454 self.data = info.data 

455 self.verbose = verbose 

456 self.max_cols = self._initialize_max_cols(max_cols) 

457 self.show_counts = self._initialize_show_counts(show_counts) 

458 

459 @property 

460 def max_rows(self) -> int: 

461 """Maximum info rows to be displayed.""" 

462 return get_option("display.max_info_rows") 

463 

464 @property 

465 def exceeds_info_cols(self) -> bool: 

466 """Check if number of columns to be summarized does not exceed maximum.""" 

467 return bool(self.col_count > self.max_cols) 

468 

469 @property 

470 def exceeds_info_rows(self) -> bool: 

471 """Check if number of rows to be summarized does not exceed maximum.""" 

472 return bool(len(self.data) > self.max_rows) 

473 

474 @property 

475 def col_count(self) -> int: 

476 """Number of columns to be summarized.""" 

477 return self.info.col_count 

478 

479 def _initialize_max_cols(self, max_cols: int | None) -> int: 

480 if max_cols is None: 

481 return get_option("display.max_info_columns") 

482 return max_cols 

483 

484 def _initialize_show_counts(self, show_counts: bool | None) -> bool: 

485 if show_counts is None: 

486 return bool(not self.exceeds_info_cols and not self.exceeds_info_rows) 

487 else: 

488 return show_counts 

489 

490 def _create_table_builder(self) -> _DataFrameTableBuilder: 

491 """ 

492 Create instance of table builder based on verbosity and display settings. 

493 """ 

494 if self.verbose: 

495 return _DataFrameTableBuilderVerbose( 

496 info=self.info, 

497 with_counts=self.show_counts, 

498 ) 

499 elif self.verbose is False: # specifically set to False, not necessarily None 

500 return _DataFrameTableBuilderNonVerbose(info=self.info) 

501 elif self.exceeds_info_cols: 

502 return _DataFrameTableBuilderNonVerbose(info=self.info) 

503 else: 

504 return _DataFrameTableBuilderVerbose( 

505 info=self.info, 

506 with_counts=self.show_counts, 

507 ) 

508 

509 

510class _SeriesInfoPrinter(_InfoPrinterAbstract): 

511 """Class for printing series info. 

512 

513 Parameters 

514 ---------- 

515 info : SeriesInfo 

516 Instance of SeriesInfo. 

517 verbose : bool, optional 

518 Whether to print the full summary. 

519 show_counts : bool, optional 

520 Whether to show the non-null counts. 

521 """ 

522 

523 def __init__( 

524 self, 

525 info: SeriesInfo, 

526 verbose: bool | None = None, 

527 show_counts: bool | None = None, 

528 ) -> None: 

529 self.info = info 

530 self.data = info.data 

531 self.verbose = verbose 

532 self.show_counts = self._initialize_show_counts(show_counts) 

533 

534 def _create_table_builder(self) -> _SeriesTableBuilder: 

535 """ 

536 Create instance of table builder based on verbosity. 

537 """ 

538 if self.verbose or self.verbose is None: 

539 return _SeriesTableBuilderVerbose( 

540 info=self.info, 

541 with_counts=self.show_counts, 

542 ) 

543 else: 

544 return _SeriesTableBuilderNonVerbose(info=self.info) 

545 

546 def _initialize_show_counts(self, show_counts: bool | None) -> bool: 

547 if show_counts is None: 

548 return True 

549 else: 

550 return show_counts 

551 

552 

553class _TableBuilderAbstract(ABC): 

554 """ 

555 Abstract builder for info table. 

556 """ 

557 

558 _lines: list[str] 

559 info: _BaseInfo 

560 

561 @abstractmethod 

562 def get_lines(self) -> list[str]: 

563 """Product in a form of list of lines (strings).""" 

564 

565 @property 

566 def data(self) -> DataFrame | Series: 

567 return self.info.data 

568 

569 @property 

570 def dtypes(self) -> Iterable[Dtype]: 

571 """Dtypes of each of the DataFrame's columns.""" 

572 return self.info.dtypes 

573 

574 @property 

575 def dtype_counts(self) -> Mapping[str, int]: 

576 """Mapping dtype - number of counts.""" 

577 return self.info.dtype_counts 

578 

579 @property 

580 def display_memory_usage(self) -> bool: 

581 """Whether to display memory usage.""" 

582 return bool(self.info.memory_usage) 

583 

584 @property 

585 def memory_usage_string(self) -> str: 

586 """Memory usage string with proper size qualifier.""" 

587 return self.info.memory_usage_string 

588 

589 @property 

590 def non_null_counts(self) -> list[int] | Series: 

591 return self.info.non_null_counts 

592 

593 def add_object_type_line(self) -> None: 

594 """Add line with string representation of dataframe to the table.""" 

595 self._lines.append(str(type(self.data))) 

596 

597 def add_index_range_line(self) -> None: 

598 """Add line with range of indices to the table.""" 

599 self._lines.append(self.data.index._summary()) 

600 

601 def add_dtypes_line(self) -> None: 

602 """Add summary line with dtypes present in dataframe.""" 

603 collected_dtypes = [ 

604 f"{key}({val:d})" for key, val in sorted(self.dtype_counts.items()) 

605 ] 

606 self._lines.append(f"dtypes: {', '.join(collected_dtypes)}") 

607 

608 

609class _DataFrameTableBuilder(_TableBuilderAbstract): 

610 """ 

611 Abstract builder for dataframe info table. 

612 

613 Parameters 

614 ---------- 

615 info : DataFrameInfo. 

616 Instance of DataFrameInfo. 

617 """ 

618 

619 def __init__(self, *, info: DataFrameInfo) -> None: 

620 self.info: DataFrameInfo = info 

621 

622 def get_lines(self) -> list[str]: 

623 self._lines = [] 

624 if self.col_count == 0: 

625 self._fill_empty_info() 

626 else: 

627 self._fill_non_empty_info() 

628 return self._lines 

629 

630 def _fill_empty_info(self) -> None: 

631 """Add lines to the info table, pertaining to empty dataframe.""" 

632 self.add_object_type_line() 

633 self.add_index_range_line() 

634 self._lines.append(f"Empty {type(self.data).__name__}\n") 

635 

636 @abstractmethod 

637 def _fill_non_empty_info(self) -> None: 

638 """Add lines to the info table, pertaining to non-empty dataframe.""" 

639 

640 @property 

641 def data(self) -> DataFrame: 

642 """DataFrame.""" 

643 return self.info.data 

644 

645 @property 

646 def ids(self) -> Index: 

647 """Dataframe columns.""" 

648 return self.info.ids 

649 

650 @property 

651 def col_count(self) -> int: 

652 """Number of dataframe columns to be summarized.""" 

653 return self.info.col_count 

654 

655 def add_memory_usage_line(self) -> None: 

656 """Add line containing memory usage.""" 

657 self._lines.append(f"memory usage: {self.memory_usage_string}") 

658 

659 

660class _DataFrameTableBuilderNonVerbose(_DataFrameTableBuilder): 

661 """ 

662 Dataframe info table builder for non-verbose output. 

663 """ 

664 

665 def _fill_non_empty_info(self) -> None: 

666 """Add lines to the info table, pertaining to non-empty dataframe.""" 

667 self.add_object_type_line() 

668 self.add_index_range_line() 

669 self.add_columns_summary_line() 

670 self.add_dtypes_line() 

671 if self.display_memory_usage: 

672 self.add_memory_usage_line() 

673 

674 def add_columns_summary_line(self) -> None: 

675 self._lines.append(self.ids._summary(name="Columns")) 

676 

677 

678class _TableBuilderVerboseMixin(_TableBuilderAbstract): 

679 """ 

680 Mixin for verbose info output. 

681 """ 

682 

683 SPACING: str = " " * 2 

684 strrows: Sequence[Sequence[str]] 

685 gross_column_widths: Sequence[int] 

686 with_counts: bool 

687 

688 @property 

689 @abstractmethod 

690 def headers(self) -> Sequence[str]: 

691 """Headers names of the columns in verbose table.""" 

692 

693 @property 

694 def header_column_widths(self) -> Sequence[int]: 

695 """Widths of header columns (only titles).""" 

696 return [len(col) for col in self.headers] 

697 

698 def _get_gross_column_widths(self) -> Sequence[int]: 

699 """Get widths of columns containing both headers and actual content.""" 

700 body_column_widths = self._get_body_column_widths() 

701 return [ 

702 max(*widths) 

703 for widths in zip( 

704 self.header_column_widths, body_column_widths, strict=False 

705 ) 

706 ] 

707 

708 def _get_body_column_widths(self) -> Sequence[int]: 

709 """Get widths of table content columns.""" 

710 strcols: Sequence[Sequence[str]] = list(zip(*self.strrows, strict=True)) 

711 return [max(len(x) for x in col) for col in strcols] 

712 

713 def _gen_rows(self) -> Iterator[Sequence[str]]: 

714 """ 

715 Generator function yielding rows content. 

716 

717 Each element represents a row comprising a sequence of strings. 

718 """ 

719 if self.with_counts: 

720 return self._gen_rows_with_counts() 

721 else: 

722 return self._gen_rows_without_counts() 

723 

724 @abstractmethod 

725 def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: 

726 """Iterator with string representation of body data with counts.""" 

727 

728 @abstractmethod 

729 def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: 

730 """Iterator with string representation of body data without counts.""" 

731 

732 def add_header_line(self) -> None: 

733 header_line = self.SPACING.join( 

734 [ 

735 _put_str(header, col_width) 

736 for header, col_width in zip( 

737 self.headers, self.gross_column_widths, strict=True 

738 ) 

739 ] 

740 ) 

741 self._lines.append(header_line) 

742 

743 def add_separator_line(self) -> None: 

744 separator_line = self.SPACING.join( 

745 [ 

746 _put_str("-" * header_colwidth, gross_colwidth) 

747 for header_colwidth, gross_colwidth in zip( 

748 self.header_column_widths, self.gross_column_widths, strict=True 

749 ) 

750 ] 

751 ) 

752 self._lines.append(separator_line) 

753 

754 def add_body_lines(self) -> None: 

755 for row in self.strrows: 

756 body_line = self.SPACING.join( 

757 [ 

758 _put_str(col, gross_colwidth) 

759 for col, gross_colwidth in zip( 

760 row, self.gross_column_widths, strict=True 

761 ) 

762 ] 

763 ) 

764 self._lines.append(body_line) 

765 

766 def _gen_non_null_counts(self) -> Iterator[str]: 

767 """Iterator with string representation of non-null counts.""" 

768 for count in self.non_null_counts: 

769 yield f"{count} non-null" 

770 

771 def _gen_dtypes(self) -> Iterator[str]: 

772 """Iterator with string representation of column dtypes.""" 

773 for dtype in self.dtypes: 

774 yield pprint_thing(dtype) 

775 

776 

777class _DataFrameTableBuilderVerbose(_DataFrameTableBuilder, _TableBuilderVerboseMixin): 

778 """ 

779 Dataframe info table builder for verbose output. 

780 """ 

781 

782 def __init__( 

783 self, 

784 *, 

785 info: DataFrameInfo, 

786 with_counts: bool, 

787 ) -> None: 

788 self.info = info 

789 self.with_counts = with_counts 

790 self.strrows: Sequence[Sequence[str]] = list(self._gen_rows()) 

791 self.gross_column_widths: Sequence[int] = self._get_gross_column_widths() 

792 

793 def _fill_non_empty_info(self) -> None: 

794 """Add lines to the info table, pertaining to non-empty dataframe.""" 

795 self.add_object_type_line() 

796 self.add_index_range_line() 

797 self.add_columns_summary_line() 

798 self.add_header_line() 

799 self.add_separator_line() 

800 self.add_body_lines() 

801 self.add_dtypes_line() 

802 if self.display_memory_usage: 

803 self.add_memory_usage_line() 

804 

805 @property 

806 def headers(self) -> Sequence[str]: 

807 """Headers names of the columns in verbose table.""" 

808 if self.with_counts: 

809 return [" # ", "Column", "Non-Null Count", "Dtype"] 

810 return [" # ", "Column", "Dtype"] 

811 

812 def add_columns_summary_line(self) -> None: 

813 self._lines.append(f"Data columns (total {self.col_count} columns):") 

814 

815 def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: 

816 """Iterator with string representation of body data without counts.""" 

817 yield from zip( 

818 self._gen_line_numbers(), 

819 self._gen_columns(), 

820 self._gen_dtypes(), 

821 strict=True, 

822 ) 

823 

824 def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: 

825 """Iterator with string representation of body data with counts.""" 

826 yield from zip( 

827 self._gen_line_numbers(), 

828 self._gen_columns(), 

829 self._gen_non_null_counts(), 

830 self._gen_dtypes(), 

831 strict=True, 

832 ) 

833 

834 def _gen_line_numbers(self) -> Iterator[str]: 

835 """Iterator with string representation of column numbers.""" 

836 for i, _ in enumerate(self.ids): 

837 yield f" {i}" 

838 

839 def _gen_columns(self) -> Iterator[str]: 

840 """Iterator with string representation of column names.""" 

841 for col in self.ids: 

842 yield pprint_thing(col) 

843 

844 

845class _SeriesTableBuilder(_TableBuilderAbstract): 

846 """ 

847 Abstract builder for series info table. 

848 

849 Parameters 

850 ---------- 

851 info : SeriesInfo. 

852 Instance of SeriesInfo. 

853 """ 

854 

855 def __init__(self, *, info: SeriesInfo) -> None: 

856 self.info: SeriesInfo = info 

857 

858 def get_lines(self) -> list[str]: 

859 self._lines = [] 

860 self._fill_non_empty_info() 

861 return self._lines 

862 

863 @property 

864 def data(self) -> Series: 

865 """Series.""" 

866 return self.info.data 

867 

868 def add_memory_usage_line(self) -> None: 

869 """Add line containing memory usage.""" 

870 self._lines.append(f"memory usage: {self.memory_usage_string}") 

871 

872 @abstractmethod 

873 def _fill_non_empty_info(self) -> None: 

874 """Add lines to the info table, pertaining to non-empty series.""" 

875 

876 

877class _SeriesTableBuilderNonVerbose(_SeriesTableBuilder): 

878 """ 

879 Series info table builder for non-verbose output. 

880 """ 

881 

882 def _fill_non_empty_info(self) -> None: 

883 """Add lines to the info table, pertaining to non-empty series.""" 

884 self.add_object_type_line() 

885 self.add_index_range_line() 

886 self.add_dtypes_line() 

887 if self.display_memory_usage: 

888 self.add_memory_usage_line() 

889 

890 

891class _SeriesTableBuilderVerbose(_SeriesTableBuilder, _TableBuilderVerboseMixin): 

892 """ 

893 Series info table builder for verbose output. 

894 """ 

895 

896 def __init__( 

897 self, 

898 *, 

899 info: SeriesInfo, 

900 with_counts: bool, 

901 ) -> None: 

902 self.info = info 

903 self.with_counts = with_counts 

904 self.strrows: Sequence[Sequence[str]] = list(self._gen_rows()) 

905 self.gross_column_widths: Sequence[int] = self._get_gross_column_widths() 

906 

907 def _fill_non_empty_info(self) -> None: 

908 """Add lines to the info table, pertaining to non-empty series.""" 

909 self.add_object_type_line() 

910 self.add_index_range_line() 

911 self.add_series_name_line() 

912 self.add_header_line() 

913 self.add_separator_line() 

914 self.add_body_lines() 

915 self.add_dtypes_line() 

916 if self.display_memory_usage: 

917 self.add_memory_usage_line() 

918 

919 def add_series_name_line(self) -> None: 

920 self._lines.append(f"Series name: {self.data.name}") 

921 

922 @property 

923 def headers(self) -> Sequence[str]: 

924 """Headers names of the columns in verbose table.""" 

925 if self.with_counts: 

926 return ["Non-Null Count", "Dtype"] 

927 return ["Dtype"] 

928 

929 def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: 

930 """Iterator with string representation of body data without counts.""" 

931 yield from ([dtype] for dtype in self._gen_dtypes()) 

932 

933 def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: 

934 """Iterator with string representation of body data with counts.""" 

935 yield from zip(self._gen_non_null_counts(), self._gen_dtypes(), strict=True) 

936 

937 

938def _get_dataframe_dtype_counts(df: DataFrame) -> Mapping[str, int]: 

939 """ 

940 Create mapping between datatypes and their number of occurrences. 

941 """ 

942 # groupby dtype.name to collect e.g. Categorical columns 

943 return df.dtypes.value_counts().groupby(lambda x: x.name).sum()