Skip to content

Commit 124585b

Browse files
authored
BUG: complex Series/DataFrame display all complex nans as nan+0j (#53844)
* BUG: complex Series/DataFrame display all complex nans as nan+0j * resolve mypy via type: ignore[arg-type]
1 parent 4392824 commit 124585b

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

pandas/io/formats/format.py

+39-7
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,35 @@ def format_with_na_rep(values: ArrayLike, formatter: Callable, na_rep: str):
14921492
).reshape(values.shape)
14931493
return formatted
14941494

1495+
def format_complex_with_na_rep(
1496+
values: ArrayLike, formatter: Callable, na_rep: str
1497+
):
1498+
real_values = np.real(values).ravel() # type: ignore[arg-type]
1499+
imag_values = np.imag(values).ravel() # type: ignore[arg-type]
1500+
real_mask, imag_mask = isna(real_values), isna(imag_values)
1501+
formatted_lst = []
1502+
for val, real_val, imag_val, re_isna, im_isna in zip(
1503+
values.ravel(),
1504+
real_values,
1505+
imag_values,
1506+
real_mask,
1507+
imag_mask,
1508+
):
1509+
if not re_isna and not im_isna:
1510+
formatted_lst.append(formatter(val))
1511+
elif not re_isna: # xxx+nanj
1512+
formatted_lst.append(f"{formatter(real_val)}+{na_rep}j")
1513+
elif not im_isna: # nan[+/-]xxxj
1514+
# The imaginary part may either start with a "-" or a space
1515+
imag_formatted = formatter(imag_val).strip()
1516+
if imag_formatted.startswith("-"):
1517+
formatted_lst.append(f"{na_rep}{imag_formatted}j")
1518+
else:
1519+
formatted_lst.append(f"{na_rep}+{imag_formatted}j")
1520+
else: # nan+nanj
1521+
formatted_lst.append(f"{na_rep}+{na_rep}j")
1522+
return np.array(formatted_lst).reshape(values.shape)
1523+
14951524
if self.formatter is not None:
14961525
return format_with_na_rep(self.values, self.formatter, self.na_rep)
14971526

@@ -1512,11 +1541,12 @@ def format_values_with(float_format):
15121541
# need to distinguish complex and float NaNs (GH #53762)
15131542
values = self.values
15141543
is_complex = is_complex_dtype(values)
1515-
if is_complex:
1516-
na_rep = f"{na_rep}+{0:.{self.digits}f}j"
15171544

15181545
# separate the wheat from the chaff
1519-
values = format_with_na_rep(values, formatter, na_rep)
1546+
if is_complex:
1547+
values = format_complex_with_na_rep(values, formatter, na_rep)
1548+
else:
1549+
values = format_with_na_rep(values, formatter, na_rep)
15201550

15211551
if self.fixed_width:
15221552
if is_complex:
@@ -1917,7 +1947,7 @@ def _trim_zeros_complex(str_complexes: np.ndarray, decimal: str = ".") -> list[s
19171947
real_part, imag_part = [], []
19181948
for x in str_complexes:
19191949
# Complex numbers are represented as "(-)xxx(+/-)xxxj"
1920-
# The split will give [maybe "-", "xxx", "+/-", "xxx", "j", ""]
1950+
# The split will give [{"", "-"}, "xxx", "+/-", "xxx", "j", ""]
19211951
# Therefore, the imaginary part is the 4th and 3rd last elements,
19221952
# and the real part is everything before the imaginary part
19231953
trimmed = re.split(r"([j+-])", x)
@@ -1929,11 +1959,13 @@ def _trim_zeros_complex(str_complexes: np.ndarray, decimal: str = ".") -> list[s
19291959
# in the array
19301960
n = len(str_complexes)
19311961
padded_parts = _trim_zeros_float(real_part + imag_part, decimal)
1962+
padded_length = max(len(part) for part in padded_parts) - 1
19321963
padded = [
1933-
padded_parts[i] # real part (including - or space, possibly "NaN")
1934-
+ padded_parts[i + n] # imaginary part (including + or -)
1964+
real_pt # real part, possibly NaN
1965+
+ imag_pt[0] # +/-
1966+
+ f"{imag_pt[1:]:>{padded_length}}" # complex part (no sign), possibly nan
19351967
+ "j"
1936-
for i in range(n)
1968+
for real_pt, imag_pt in zip(padded_parts[:n], padded_parts[n:])
19371969
]
19381970
return padded
19391971

pandas/tests/io/formats/test_printing.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,23 @@ def test_multiindex_long_element():
218218
([-2, complex("nan"), -1], ["-2.0+0.0j", " NaN+0.0j", "-1.0+0.0j"]),
219219
([-1.23j, complex("nan"), -1], ["-0.00-1.23j", " NaN+0.00j", "-1.00+0.00j"]),
220220
([1.23j, complex("nan"), 1.23], [" 0.00+1.23j", " NaN+0.00j", " 1.23+0.00j"]),
221+
(
222+
[-1.23j, complex(np.nan, np.nan), 1],
223+
["-0.00-1.23j", " NaN+ NaNj", " 1.00+0.00j"],
224+
),
225+
(
226+
[-1.23j, complex(1.2, np.nan), 1],
227+
["-0.00-1.23j", " 1.20+ NaNj", " 1.00+0.00j"],
228+
),
229+
(
230+
[-1.23j, complex(np.nan, -1.2), 1],
231+
["-0.00-1.23j", " NaN-1.20j", " 1.00+0.00j"],
232+
),
221233
],
222234
)
223235
@pytest.mark.parametrize("as_frame", [True, False])
224236
def test_ser_df_with_complex_nans(data, output, as_frame):
225-
# GH#53762
237+
# GH#53762, GH#53841
226238
obj = pd.Series(data)
227239
if as_frame:
228240
obj = obj.to_frame(name="val")

0 commit comments

Comments
 (0)