Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/reshape/concat.py: 14%

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

308 statements  

1""" 

2Concat routines. 

3""" 

4 

5from __future__ import annotations 

6 

7from collections import abc 

8from itertools import pairwise 

9import types 

10from typing import ( 

11 TYPE_CHECKING, 

12 Literal, 

13 cast, 

14 overload, 

15) 

16import warnings 

17 

18import numpy as np 

19 

20from pandas._libs import lib 

21from pandas.errors import Pandas4Warning 

22from pandas.util._decorators import set_module 

23from pandas.util._exceptions import find_stack_level 

24 

25from pandas.core.dtypes.common import ( 

26 is_bool, 

27 is_scalar, 

28) 

29from pandas.core.dtypes.concat import concat_compat 

30from pandas.core.dtypes.generic import ( 

31 ABCDataFrame, 

32 ABCSeries, 

33) 

34from pandas.core.dtypes.missing import isna 

35 

36from pandas.core.arrays.categorical import ( 

37 factorize_from_iterable, 

38 factorize_from_iterables, 

39) 

40import pandas.core.common as com 

41from pandas.core.indexes.api import ( 

42 Index, 

43 MultiIndex, 

44 all_indexes_same, 

45 default_index, 

46 ensure_index, 

47 get_objs_combined_axis, 

48 get_unanimous_names, 

49 union_indexes, 

50) 

51from pandas.core.indexes.datetimes import DatetimeIndex 

52from pandas.core.internals import concatenate_managers 

53 

54if TYPE_CHECKING: 

55 from collections.abc import ( 

56 Callable, 

57 Hashable, 

58 Iterable, 

59 Mapping, 

60 ) 

61 

62 from pandas._typing import ( 

63 Axis, 

64 AxisInt, 

65 HashableT, 

66 ) 

67 

68 from pandas import ( 

69 DataFrame, 

70 Series, 

71 ) 

72 

73# --------------------------------------------------------------------- 

74# Concatenate DataFrame objects 

75 

76 

77@overload 

78def concat( 

79 objs: Iterable[DataFrame] | Mapping[HashableT, DataFrame], 

80 *, 

81 axis: Literal[0, "index"] = ..., 

82 join: str = ..., 

83 ignore_index: bool = ..., 

84 keys: Iterable[Hashable] | None = ..., 

85 levels=..., 

86 names: list[HashableT] | None = ..., 

87 verify_integrity: bool = ..., 

88 sort: bool = ..., 

89 copy: bool | lib.NoDefault = ..., 

90) -> DataFrame: ... 

91 

92 

93@overload 

94def concat( 

95 objs: Iterable[Series] | Mapping[HashableT, Series], 

96 *, 

97 axis: Literal[0, "index"] = ..., 

98 join: str = ..., 

99 ignore_index: bool = ..., 

100 keys: Iterable[Hashable] | None = ..., 

101 levels=..., 

102 names: list[HashableT] | None = ..., 

103 verify_integrity: bool = ..., 

104 sort: bool = ..., 

105 copy: bool | lib.NoDefault = ..., 

106) -> Series: ... 

107 

108 

109@overload 

110def concat( 

111 objs: Iterable[Series | DataFrame] | Mapping[HashableT, Series | DataFrame], 

112 *, 

113 axis: Literal[0, "index"] = ..., 

114 join: str = ..., 

115 ignore_index: bool = ..., 

116 keys: Iterable[Hashable] | None = ..., 

117 levels=..., 

118 names: list[HashableT] | None = ..., 

119 verify_integrity: bool = ..., 

120 sort: bool = ..., 

121 copy: bool | lib.NoDefault = ..., 

122) -> DataFrame | Series: ... 

123 

124 

125@overload 

126def concat( 

127 objs: Iterable[Series | DataFrame] | Mapping[HashableT, Series | DataFrame], 

128 *, 

129 axis: Literal[1, "columns"], 

130 join: str = ..., 

131 ignore_index: bool = ..., 

132 keys: Iterable[Hashable] | None = ..., 

133 levels=..., 

134 names: list[HashableT] | None = ..., 

135 verify_integrity: bool = ..., 

136 sort: bool = ..., 

137 copy: bool | lib.NoDefault = ..., 

138) -> DataFrame: ... 

139 

140 

141@overload 

142def concat( 

143 objs: Iterable[Series | DataFrame] | Mapping[HashableT, Series | DataFrame], 

144 *, 

145 axis: Axis = ..., 

146 join: str = ..., 

147 ignore_index: bool = ..., 

148 keys: Iterable[Hashable] | None = ..., 

149 levels=..., 

150 names: list[HashableT] | None = ..., 

151 verify_integrity: bool = ..., 

152 sort: bool = ..., 

153 copy: bool | lib.NoDefault = ..., 

154) -> DataFrame | Series: ... 

155 

156 

157@set_module("pandas") 

158def concat( 

159 objs: Iterable[Series | DataFrame] | Mapping[HashableT, Series | DataFrame], 

160 *, 

161 axis: Axis = 0, 

162 join: str = "outer", 

163 ignore_index: bool = False, 

164 keys: Iterable[Hashable] | None = None, 

165 levels=None, 

166 names: list[HashableT] | None = None, 

167 verify_integrity: bool = False, 

168 sort: bool | lib.NoDefault = lib.no_default, 

169 copy: bool | lib.NoDefault = lib.no_default, 

170) -> DataFrame | Series: 

171 """ 

172 Concatenate pandas objects along a particular axis. 

173 

174 Allows optional set logic along the other axes. 

175 

176 Can also add a layer of hierarchical indexing on the concatenation axis, 

177 which may be useful if the labels are the same (or overlapping) on 

178 the passed axis number. 

179 

180 Parameters 

181 ---------- 

182 objs : an iterable or mapping of Series or DataFrame objects 

183 If a mapping is passed, the keys will be used as the `keys` 

184 argument, unless it is passed, in which case the values will be 

185 selected (see below). Any None objects will be dropped silently unless 

186 they are all None in which case a ValueError will be raised. 

187 axis : {0/'index', 1/'columns'}, default 0 

188 The axis to concatenate along. 

189 join : {'inner', 'outer'}, default 'outer' 

190 How to handle indexes on other axis (or axes). 

191 ignore_index : bool, default False 

192 If True, do not use the index values along the concatenation axis. The 

193 resulting axis will be labeled 0, ..., n - 1. This is useful if you are 

194 concatenating objects where the concatenation axis does not have 

195 meaningful indexing information. Note the index values on the other 

196 axes are still respected in the join. 

197 keys : sequence, default None 

198 If multiple levels passed, should contain tuples. Construct 

199 hierarchical index using the passed keys as the outermost level. 

200 levels : list of sequences, default None 

201 Specific levels (unique values) to use for constructing a 

202 MultiIndex. Otherwise they will be inferred from the keys. 

203 names : list, default None 

204 Names for the levels in the resulting hierarchical index. 

205 verify_integrity : bool, default False 

206 Check whether the new concatenated axis contains duplicates. This can 

207 be very expensive relative to the actual data concatenation. 

208 sort : bool, default False 

209 Sort non-concatenation axis. One exception to this is when the 

210 non-concatenation axis is a DatetimeIndex and join='outer' and the axis is 

211 not already aligned. In that case, the non-concatenation axis is always 

212 sorted lexicographically. 

213 copy : bool, default False 

214 This keyword is now ignored; changing its value will have no 

215 impact on the method. 

216 

217 .. deprecated:: 3.0.0 

218 

219 This keyword is ignored and will be removed in pandas 4.0. Since 

220 pandas 3.0, this method always returns a new object using a lazy 

221 copy mechanism that defers copies until necessary 

222 (Copy-on-Write). See the `user guide on Copy-on-Write 

223 <https://pandas.pydata.org/docs/dev/user_guide/copy_on_write.html>`__ 

224 for more details. 

225 

226 Returns 

227 ------- 

228 object, type of objs 

229 When concatenating all ``Series`` along the index (axis=0), a 

230 ``Series`` is returned. When ``objs`` contains at least one 

231 ``DataFrame``, a ``DataFrame`` is returned. When concatenating along 

232 the columns (axis=1), a ``DataFrame`` is returned. 

233 

234 See Also 

235 -------- 

236 DataFrame.join : Join DataFrames using indexes. 

237 DataFrame.merge : Merge DataFrames by indexes or columns. 

238 

239 Notes 

240 ----- 

241 The keys, levels, and names arguments are all optional. 

242 

243 A walkthrough of how this method fits in with other tools for combining 

244 pandas objects can be found `here 

245 <https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html>`__. 

246 

247 It is not recommended to build DataFrames by adding single rows in a 

248 for loop. Build a list of rows and make a DataFrame in a single concat. 

249 

250 Examples 

251 -------- 

252 Combine two ``Series``. 

253 

254 >>> s1 = pd.Series(["a", "b"]) 

255 >>> s2 = pd.Series(["c", "d"]) 

256 >>> pd.concat([s1, s2]) 

257 0 a 

258 1 b 

259 0 c 

260 1 d 

261 dtype: str 

262 

263 Clear the existing index and reset it in the result 

264 by setting the ``ignore_index`` option to ``True``. 

265 

266 >>> pd.concat([s1, s2], ignore_index=True) 

267 0 a 

268 1 b 

269 2 c 

270 3 d 

271 dtype: str 

272 

273 Add a hierarchical index at the outermost level of 

274 the data with the ``keys`` option. 

275 

276 >>> pd.concat([s1, s2], keys=["s1", "s2"]) 

277 s1 0 a 

278 1 b 

279 s2 0 c 

280 1 d 

281 dtype: str 

282 

283 Label the index keys you create with the ``names`` option. 

284 

285 >>> pd.concat([s1, s2], keys=["s1", "s2"], names=["Series name", "Row ID"]) 

286 Series name Row ID 

287 s1 0 a 

288 1 b 

289 s2 0 c 

290 1 d 

291 dtype: str 

292 

293 Combine two ``DataFrame`` objects with identical columns. 

294 

295 >>> df1 = pd.DataFrame([["a", 1], ["b", 2]], columns=["letter", "number"]) 

296 >>> df1 

297 letter number 

298 0 a 1 

299 1 b 2 

300 >>> df2 = pd.DataFrame([["c", 3], ["d", 4]], columns=["letter", "number"]) 

301 >>> df2 

302 letter number 

303 0 c 3 

304 1 d 4 

305 >>> pd.concat([df1, df2]) 

306 letter number 

307 0 a 1 

308 1 b 2 

309 0 c 3 

310 1 d 4 

311 

312 Combine ``DataFrame`` objects with overlapping columns 

313 and return everything. Columns outside the intersection will 

314 be filled with ``NaN`` values. 

315 

316 >>> df3 = pd.DataFrame( 

317 ... [["c", 3, "cat"], ["d", 4, "dog"]], columns=["letter", "number", "animal"] 

318 ... ) 

319 >>> df3 

320 letter number animal 

321 0 c 3 cat 

322 1 d 4 dog 

323 >>> pd.concat([df1, df3], sort=False) 

324 letter number animal 

325 0 a 1 NaN 

326 1 b 2 NaN 

327 0 c 3 cat 

328 1 d 4 dog 

329 

330 Combine ``DataFrame`` objects with overlapping columns 

331 and return only those that are shared by passing ``inner`` to 

332 the ``join`` keyword argument. 

333 

334 >>> pd.concat([df1, df3], join="inner") 

335 letter number 

336 0 a 1 

337 1 b 2 

338 0 c 3 

339 1 d 4 

340 

341 Combine ``DataFrame`` objects horizontally along the x axis by 

342 passing in ``axis=1``. 

343 

344 >>> df4 = pd.DataFrame( 

345 ... [["bird", "polly"], ["monkey", "george"]], columns=["animal", "name"] 

346 ... ) 

347 >>> pd.concat([df1, df4], axis=1) 

348 letter number animal name 

349 0 a 1 bird polly 

350 1 b 2 monkey george 

351 

352 Prevent the result from including duplicate index values with the 

353 ``verify_integrity`` option. 

354 

355 >>> df5 = pd.DataFrame([1], index=["a"]) 

356 >>> df5 

357 0 

358 a 1 

359 >>> df6 = pd.DataFrame([2], index=["a"]) 

360 >>> df6 

361 0 

362 a 2 

363 >>> pd.concat([df5, df6], verify_integrity=True) 

364 Traceback (most recent call last): 

365 ... 

366 ValueError: Indexes have overlapping values: ['a'] 

367 

368 Append a single row to the end of a ``DataFrame`` object. 

369 

370 >>> df7 = pd.DataFrame({"a": 1, "b": 2}, index=[0]) 

371 >>> df7 

372 a b 

373 0 1 2 

374 >>> new_row = pd.Series({"a": 3, "b": 4}) 

375 >>> new_row 

376 a 3 

377 b 4 

378 dtype: int64 

379 >>> pd.concat([df7, new_row.to_frame().T], ignore_index=True) 

380 a b 

381 0 1 2 

382 1 3 4 

383 """ 

384 if ignore_index and keys is not None: 

385 raise ValueError( 

386 f"Cannot set {ignore_index=} and specify keys. Either should be used." 

387 ) 

388 

389 if copy is not lib.no_default: 

390 warnings.warn( 

391 "The copy keyword is deprecated and will be removed in a future " 

392 "version. Copy-on-Write is active in pandas since 3.0 which utilizes " 

393 "a lazy copy mechanism that defers copies until necessary. Use " 

394 ".copy() to make an eager copy if necessary.", 

395 Pandas4Warning, 

396 stacklevel=find_stack_level(), 

397 ) 

398 if join == "outer": 

399 intersect = False 

400 elif join == "inner": 

401 intersect = True 

402 else: # pragma: no cover 

403 raise ValueError( 

404 "Only can inner (intersect) or outer (union) join the other axis" 

405 ) 

406 

407 objs, keys, ndims = _clean_keys_and_objs(objs, keys) 

408 

409 if sort is lib.no_default: 

410 if axis == 0: 

411 non_concat_axis = [ 

412 obj.columns if isinstance(obj, ABCDataFrame) else Index([obj.name]) 

413 for obj in objs 

414 ] 

415 else: 

416 non_concat_axis = [obj.index for obj in objs] 

417 

418 if ( 

419 intersect 

420 or any(not isinstance(index, DatetimeIndex) for index in non_concat_axis) 

421 or all(prev is curr for prev, curr in pairwise(non_concat_axis)) 

422 or ( 

423 all( 

424 prev[-1] <= curr[0] and prev.is_monotonic_increasing 

425 for prev, curr in pairwise(non_concat_axis) 

426 if not prev.empty and not curr.empty 

427 ) 

428 and non_concat_axis[-1].is_monotonic_increasing 

429 ) 

430 ): 

431 # Sorting or not will not impact the result. 

432 sort = False 

433 elif not is_bool(sort): 

434 raise ValueError( 

435 f"The 'sort' keyword only accepts boolean values; {sort} was passed." 

436 ) 

437 else: 

438 sort = bool(sort) 

439 

440 # select an object to be our result reference 

441 sample, objs = _get_sample_object(objs, ndims, keys, names, levels, intersect) 

442 

443 # Standardize axis parameter to int 

444 if sample.ndim == 1: 

445 from pandas import DataFrame 

446 

447 bm_axis = DataFrame._get_axis_number(axis) 

448 is_frame = False 

449 is_series = True 

450 else: 

451 bm_axis = sample._get_axis_number(axis) 

452 is_frame = True 

453 is_series = False 

454 

455 # Need to flip BlockManager axis in the DataFrame special case 

456 bm_axis = sample._get_block_manager_axis(bm_axis) 

457 

458 # if we have mixed ndims, then convert to highest ndim 

459 # creating column numbers as needed 

460 if len(ndims) > 1: 

461 objs = _sanitize_mixed_ndim(objs, sample, ignore_index, bm_axis) 

462 

463 orig_axis = axis 

464 axis = 1 - bm_axis if is_frame else 0 

465 names = names or getattr(keys, "names", None) 

466 result = _get_result( 

467 objs, 

468 is_series, 

469 bm_axis, 

470 ignore_index, 

471 intersect, 

472 sort, 

473 keys, 

474 levels, 

475 verify_integrity, 

476 names, 

477 axis, 

478 ) 

479 

480 if sort is lib.no_default: 

481 if orig_axis == 0: 

482 non_concat_axis = [ 

483 obj.columns if isinstance(obj, ABCDataFrame) else Index([obj.name]) 

484 for obj in objs 

485 ] 

486 else: 

487 non_concat_axis = [obj.index for obj in objs] 

488 no_sort_result_index = union_indexes(non_concat_axis, sort=False) 

489 orig = result.index if orig_axis == 1 else result.columns 

490 if not no_sort_result_index.equals(orig): 

491 msg = ( 

492 "Sorting by default when concatenating all DatetimeIndex is " 

493 "deprecated. In the future, pandas will respect the default " 

494 "of `sort=False`. Specify `sort=True` or `sort=False` to " 

495 "silence this message. If you see this warnings when not " 

496 "directly calling concat, report a bug to pandas." 

497 ) 

498 warnings.warn(msg, Pandas4Warning, stacklevel=find_stack_level()) 

499 

500 return result 

501 

502 

503def _sanitize_mixed_ndim( 

504 objs: list[Series | DataFrame], 

505 sample: Series | DataFrame, 

506 ignore_index: bool, 

507 axis: AxisInt, 

508) -> list[Series | DataFrame]: 

509 # if we have mixed ndims, then convert to highest ndim 

510 # creating column numbers as needed 

511 

512 new_objs = [] 

513 

514 current_column = 0 

515 max_ndim = sample.ndim 

516 for obj in objs: 

517 ndim = obj.ndim 

518 if ndim == max_ndim: 

519 pass 

520 

521 elif ndim != max_ndim - 1: 

522 raise ValueError( 

523 "cannot concatenate unaligned mixed dimensional NDFrame objects" 

524 ) 

525 

526 else: 

527 name = getattr(obj, "name", None) 

528 rename_columns = False 

529 if ignore_index or name is None: 

530 if axis == 1: 

531 # doing a row-wise concatenation so need everything 

532 # to line up 

533 if name is None: 

534 name = 0 

535 rename_columns = True 

536 # doing a column-wise concatenation so need series 

537 # to have unique names 

538 elif name is None: 

539 rename_columns = True 

540 name = current_column 

541 current_column += 1 

542 obj = sample._constructor(obj, copy=False) 

543 if isinstance(obj, ABCDataFrame) and rename_columns: 

544 obj.columns = range(name, name + 1, 1) 

545 else: 

546 obj = sample._constructor({name: obj}, copy=False) 

547 

548 new_objs.append(obj) 

549 

550 return new_objs 

551 

552 

553def _get_result( 

554 objs: list[Series | DataFrame], 

555 is_series: bool, 

556 bm_axis: AxisInt, 

557 ignore_index: bool, 

558 intersect: bool, 

559 sort: bool | lib.NoDefault, 

560 keys: Iterable[Hashable] | None, 

561 levels, 

562 verify_integrity: bool, 

563 names: list[HashableT] | None, 

564 axis: AxisInt, 

565): 

566 cons: Callable[..., DataFrame | Series] 

567 sample: DataFrame | Series 

568 

569 # series only 

570 if is_series: 

571 sample = cast("Series", objs[0]) 

572 

573 # stack blocks 

574 if bm_axis == 0: 

575 name = com.consensus_name_attr(objs) 

576 cons = sample._constructor 

577 

578 arrs = [ser._values for ser in objs] 

579 

580 res = concat_compat(arrs, axis=0) 

581 

582 if ignore_index: 

583 new_index: Index = default_index(len(res)) 

584 else: 

585 new_index = _get_concat_axis_series( 

586 objs, 

587 ignore_index, 

588 bm_axis, 

589 keys, 

590 levels, 

591 verify_integrity, 

592 names, 

593 ) 

594 

595 mgr = type(sample._mgr).from_array(res, index=new_index) 

596 

597 result = sample._constructor_from_mgr(mgr, axes=mgr.axes) 

598 result._name = name 

599 return result.__finalize__( 

600 types.SimpleNamespace(input_objs=objs, objs=objs), method="concat" 

601 ) 

602 

603 # combine as columns in a frame 

604 else: 

605 data = dict(enumerate(objs)) 

606 

607 # GH28330 Preserves subclassed objects through concat 

608 cons = sample._constructor_expanddim 

609 

610 index = get_objs_combined_axis( 

611 objs, 

612 axis=objs[0]._get_block_manager_axis(0), 

613 intersect=intersect, 

614 sort=sort, 

615 ) 

616 columns = _get_concat_axis_series( 

617 objs, ignore_index, bm_axis, keys, levels, verify_integrity, names 

618 ) 

619 df = cons(data, index=index, copy=False) 

620 df.columns = columns 

621 return df.__finalize__( 

622 types.SimpleNamespace(input_objs=objs, objs=objs), method="concat" 

623 ) 

624 

625 # combine block managers 

626 else: 

627 sample = cast("DataFrame", objs[0]) 

628 

629 mgrs_indexers = [] 

630 result_axes = new_axes( 

631 objs, 

632 bm_axis, 

633 intersect, 

634 sort, 

635 keys, 

636 names, 

637 axis, 

638 levels, 

639 verify_integrity, 

640 ignore_index, 

641 ) 

642 for obj in objs: 

643 indexers = {} 

644 for ax, new_labels in enumerate(result_axes): 

645 # ::-1 to convert BlockManager ax to DataFrame ax 

646 if ax == bm_axis: 

647 # Suppress reindexing on concat axis 

648 continue 

649 

650 # 1-ax to convert BlockManager axis to DataFrame axis 

651 obj_labels = obj.axes[1 - ax] 

652 if not new_labels.equals(obj_labels): 

653 indexers[ax] = obj_labels.get_indexer(new_labels) 

654 

655 mgrs_indexers.append((obj._mgr, indexers)) 

656 

657 new_data = concatenate_managers( 

658 mgrs_indexers, result_axes, concat_axis=bm_axis, copy=False 

659 ) 

660 

661 out = sample._constructor_from_mgr(new_data, axes=new_data.axes) 

662 return out.__finalize__( 

663 types.SimpleNamespace(input_objs=objs, objs=objs), method="concat" 

664 ) 

665 

666 

667def new_axes( 

668 objs: list[Series | DataFrame], 

669 bm_axis: AxisInt, 

670 intersect: bool, 

671 sort: bool | lib.NoDefault, 

672 keys: Iterable[Hashable] | None, 

673 names: list[HashableT] | None, 

674 axis: AxisInt, 

675 levels, 

676 verify_integrity: bool, 

677 ignore_index: bool, 

678) -> list[Index]: 

679 """Return the new [index, column] result for concat.""" 

680 return [ 

681 _get_concat_axis_dataframe( 

682 objs, 

683 axis, 

684 ignore_index, 

685 keys, 

686 names, 

687 levels, 

688 verify_integrity, 

689 ) 

690 if i == bm_axis 

691 else get_objs_combined_axis( 

692 objs, 

693 axis=objs[0]._get_block_manager_axis(i), 

694 intersect=intersect, 

695 sort=sort, 

696 ) 

697 for i in range(2) 

698 ] 

699 

700 

701def _get_concat_axis_series( 

702 objs: list[Series | DataFrame], 

703 ignore_index: bool, 

704 bm_axis: AxisInt, 

705 keys: Iterable[Hashable] | None, 

706 levels, 

707 verify_integrity: bool, 

708 names: list[HashableT] | None, 

709) -> Index: 

710 """Return result concat axis when concatenating Series objects.""" 

711 if ignore_index: 

712 return default_index(len(objs)) 

713 elif bm_axis == 0: 

714 indexes = [x.index for x in objs] 

715 if keys is None: 

716 if levels is not None: 

717 raise ValueError("levels supported only when keys is not None") 

718 concat_axis = _concat_indexes(indexes) 

719 else: 

720 concat_axis = _make_concat_multiindex(indexes, keys, levels, names) 

721 if verify_integrity and not concat_axis.is_unique: 

722 overlap = concat_axis[concat_axis.duplicated()].unique() 

723 raise ValueError(f"Indexes have overlapping values: {overlap}") 

724 return concat_axis 

725 elif keys is None: 

726 result_names: list[Hashable] = [None] * len(objs) 

727 num = 0 

728 has_names = False 

729 for i, x in enumerate(objs): 

730 if x.ndim != 1: 

731 raise TypeError( 

732 f"Cannot concatenate type 'Series' with " 

733 f"object of type '{type(x).__name__}'" 

734 ) 

735 if x.name is not None: 

736 result_names[i] = x.name 

737 has_names = True 

738 else: 

739 result_names[i] = num 

740 num += 1 

741 if has_names: 

742 return Index(result_names) 

743 else: 

744 return default_index(len(objs)) 

745 else: 

746 return ensure_index(keys).set_names(names) # type: ignore[arg-type] 

747 

748 

749def _get_concat_axis_dataframe( 

750 objs: list[Series | DataFrame], 

751 axis: AxisInt, 

752 ignore_index: bool, 

753 keys: Iterable[Hashable] | None, 

754 names: list[HashableT] | None, 

755 levels, 

756 verify_integrity: bool, 

757) -> Index: 

758 """Return result concat axis when concatenating DataFrame objects.""" 

759 indexes_gen = (x.axes[axis] for x in objs) 

760 

761 if ignore_index: 

762 return default_index(sum(len(i) for i in indexes_gen)) 

763 else: 

764 indexes = list(indexes_gen) 

765 

766 if keys is None: 

767 if levels is not None: 

768 raise ValueError("levels supported only when keys is not None") 

769 concat_axis = _concat_indexes(indexes) 

770 else: 

771 concat_axis = _make_concat_multiindex(indexes, keys, levels, names) 

772 

773 if verify_integrity and not concat_axis.is_unique: 

774 overlap = concat_axis[concat_axis.duplicated()].unique() 

775 raise ValueError(f"Indexes have overlapping values: {overlap}") 

776 

777 return concat_axis 

778 

779 

780def _clean_keys_and_objs( 

781 objs: Iterable[Series | DataFrame] | Mapping[HashableT, Series | DataFrame], 

782 keys, 

783) -> tuple[list[Series | DataFrame], Index | None, set[int]]: 

784 """ 

785 Returns 

786 ------- 

787 clean_objs : list[Series | DataFrame] 

788 List of DataFrame and Series with Nones removed. 

789 keys : Index | None 

790 None if keys was None 

791 Index if objs was a Mapping or keys was not None. Filtered where objs was None. 

792 ndim : set[int] 

793 Unique .ndim attribute of obj encountered. 

794 """ 

795 if isinstance(objs, abc.Mapping): 

796 if keys is None: 

797 keys = objs.keys() 

798 objs = [objs[k] for k in keys] 

799 elif isinstance(objs, (ABCSeries, ABCDataFrame)) or is_scalar(objs): 

800 raise TypeError( 

801 "first argument must be an iterable of pandas " 

802 f'objects, you passed an object of type "{type(objs).__name__}"' 

803 ) 

804 elif not isinstance(objs, abc.Sized): 

805 objs = list(objs) 

806 

807 if len(objs) == 0: 

808 raise ValueError("No objects to concatenate") 

809 

810 if keys is not None: 

811 if not isinstance(keys, Index): 

812 keys = Index(keys) 

813 if len(keys) != len(objs): 

814 # GH#43485 

815 raise ValueError( 

816 f"The length of the keys ({len(keys)}) must match " 

817 f"the length of the objects to concatenate ({len(objs)})" 

818 ) 

819 

820 # GH#1649 

821 key_indices = [] 

822 clean_objs = [] 

823 ndims = set() 

824 for i, obj in enumerate(objs): 

825 if obj is None: 

826 continue 

827 elif isinstance(obj, (ABCSeries, ABCDataFrame)): 

828 key_indices.append(i) 

829 clean_objs.append(obj) 

830 ndims.add(obj.ndim) 

831 else: 

832 msg = ( 

833 f"cannot concatenate object of type '{type(obj)}'; " 

834 "only Series and DataFrame objs are valid" 

835 ) 

836 raise TypeError(msg) 

837 

838 if keys is not None and len(key_indices) < len(keys): 

839 keys = keys.take(key_indices) 

840 

841 if len(clean_objs) == 0: 

842 raise ValueError("All objects passed were None") 

843 

844 return clean_objs, keys, ndims 

845 

846 

847def _get_sample_object( 

848 objs: list[Series | DataFrame], 

849 ndims: set[int], 

850 keys, 

851 names, 

852 levels, 

853 intersect: bool, 

854) -> tuple[Series | DataFrame, list[Series | DataFrame]]: 

855 # get the sample 

856 # want the highest ndim that we have, and must be non-empty 

857 # unless all objs are empty 

858 if len(ndims) > 1: 

859 max_ndim = max(ndims) 

860 for obj in objs: 

861 if obj.ndim == max_ndim and sum(obj.shape): # type: ignore[arg-type] 

862 return obj, objs 

863 elif keys is None and names is None and levels is None and not intersect: 

864 # filter out the empties if we have not multi-index possibilities 

865 # note to keep empty Series as it affect to result columns / name 

866 if ndims.pop() == 2: 

867 non_empties = [obj for obj in objs if sum(obj.shape)] 

868 else: 

869 non_empties = objs 

870 

871 if len(non_empties): 

872 return non_empties[0], non_empties 

873 

874 return objs[0], objs 

875 

876 

877def _concat_indexes(indexes) -> Index: 

878 return indexes[0].append(indexes[1:]) 

879 

880 

881def validate_unique_levels(levels: list[Index]) -> None: 

882 for level in levels: 

883 if not level.is_unique: 

884 raise ValueError(f"Level values not unique: {level.tolist()}") 

885 

886 

887def _make_concat_multiindex(indexes, keys, levels=None, names=None) -> MultiIndex: 

888 if (levels is None and isinstance(keys[0], tuple)) or ( 

889 levels is not None and len(levels) > 1 

890 ): 

891 zipped = list(zip(*keys, strict=True)) 

892 if names is None: 

893 names = [None] * len(zipped) 

894 

895 if levels is None: 

896 _, levels = factorize_from_iterables(zipped) 

897 else: 

898 levels = [ensure_index(x) for x in levels] 

899 validate_unique_levels(levels) 

900 else: 

901 zipped = [keys] 

902 if names is None: 

903 names = [None] 

904 

905 if levels is None: 

906 levels = [ensure_index(keys).unique()] 

907 else: 

908 levels = [ensure_index(x) for x in levels] 

909 validate_unique_levels(levels) 

910 

911 if not all_indexes_same(indexes): 

912 codes_list = [] 

913 

914 # things are potentially different sizes, so compute the exact codes 

915 # for each level and pass those to MultiIndex.from_arrays 

916 

917 for hlevel, level in zip(zipped, levels, strict=True): 

918 to_concat = [] 

919 if isinstance(hlevel, Index) and hlevel.equals(level): 

920 lens = [len(idx) for idx in indexes] 

921 codes_list.append(np.repeat(np.arange(len(hlevel)), lens)) 

922 else: 

923 for key, index in zip(hlevel, indexes, strict=True): 

924 # Find matching codes, include matching nan values as equal. 

925 mask = (isna(level) & isna(key)) | (level == key) 

926 if not mask.any(): 

927 raise ValueError(f"Key {key} not in level {level}") 

928 i = np.nonzero(mask)[0][0] 

929 

930 to_concat.append(np.repeat(i, len(index))) 

931 codes_list.append(np.concatenate(to_concat)) 

932 

933 concat_index = _concat_indexes(indexes) 

934 

935 # these go at the end 

936 if isinstance(concat_index, MultiIndex): 

937 levels.extend(concat_index.levels) 

938 codes_list.extend(concat_index.codes) 

939 else: 

940 codes, categories = factorize_from_iterable(concat_index) 

941 levels.append(categories) 

942 codes_list.append(codes) 

943 

944 if len(names) == len(levels): 

945 names = list(names) 

946 else: 

947 # make sure that all of the passed indices have the same nlevels 

948 if not len({idx.nlevels for idx in indexes}) == 1: 

949 raise AssertionError( 

950 "Cannot concat indices that do not have the same number of levels" 

951 ) 

952 

953 # also copies 

954 names = list(names) + list(get_unanimous_names(*indexes)) 

955 

956 return MultiIndex( 

957 levels=levels, codes=codes_list, names=names, verify_integrity=False 

958 ) 

959 

960 new_index = indexes[0] 

961 n = len(new_index) 

962 kpieces = len(indexes) 

963 

964 # also copies 

965 new_names = list(names) 

966 new_levels = list(levels) 

967 

968 # construct codes 

969 new_codes = [] 

970 

971 # do something a bit more speedy 

972 

973 for hlevel, level in zip(zipped, levels, strict=True): 

974 hlevel_index = ensure_index(hlevel) 

975 mapped = level.get_indexer(hlevel_index) 

976 

977 mask = mapped == -1 

978 if mask.any(): 

979 raise ValueError( 

980 f"Values not found in passed level: {hlevel_index[mask]!s}" 

981 ) 

982 

983 new_codes.append(np.repeat(mapped, n)) 

984 

985 if isinstance(new_index, MultiIndex): 

986 new_levels.extend(new_index.levels) 

987 new_codes.extend(np.tile(lab, kpieces) for lab in new_index.codes) 

988 else: 

989 new_levels.append(new_index.unique()) 

990 single_codes = new_index.unique().get_indexer(new_index) 

991 new_codes.append(np.tile(single_codes, kpieces)) 

992 

993 if len(new_names) < len(new_levels): 

994 new_names.extend(new_index.names) 

995 

996 return MultiIndex( 

997 levels=new_levels, codes=new_codes, names=new_names, verify_integrity=False 

998 )