Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/numpy/polynomial/polyutils.py
7757 views
1
"""
2
Utility classes and functions for the polynomial modules.
3
4
This module provides: error and warning objects; a polynomial base class;
5
and some routines used in both the `polynomial` and `chebyshev` modules.
6
7
Warning objects
8
---------------
9
10
.. autosummary::
11
:toctree: generated/
12
13
RankWarning raised in least-squares fit for rank-deficient matrix.
14
15
Functions
16
---------
17
18
.. autosummary::
19
:toctree: generated/
20
21
as_series convert list of array_likes into 1-D arrays of common type.
22
trimseq remove trailing zeros.
23
trimcoef remove small trailing coefficients.
24
getdomain return the domain appropriate for a given set of abscissae.
25
mapdomain maps points between domains.
26
mapparms parameters of the linear map between domains.
27
28
"""
29
import operator
30
import functools
31
import warnings
32
33
import numpy as np
34
35
__all__ = [
36
'RankWarning', 'as_series', 'trimseq',
37
'trimcoef', 'getdomain', 'mapdomain', 'mapparms']
38
39
#
40
# Warnings and Exceptions
41
#
42
43
class RankWarning(UserWarning):
44
"""Issued by chebfit when the design matrix is rank deficient."""
45
pass
46
47
#
48
# Helper functions to convert inputs to 1-D arrays
49
#
50
def trimseq(seq):
51
"""Remove small Poly series coefficients.
52
53
Parameters
54
----------
55
seq : sequence
56
Sequence of Poly series coefficients. This routine fails for
57
empty sequences.
58
59
Returns
60
-------
61
series : sequence
62
Subsequence with trailing zeros removed. If the resulting sequence
63
would be empty, return the first element. The returned sequence may
64
or may not be a view.
65
66
Notes
67
-----
68
Do not lose the type info if the sequence contains unknown objects.
69
70
"""
71
if len(seq) == 0:
72
return seq
73
else:
74
for i in range(len(seq) - 1, -1, -1):
75
if seq[i] != 0:
76
break
77
return seq[:i+1]
78
79
80
def as_series(alist, trim=True):
81
"""
82
Return argument as a list of 1-d arrays.
83
84
The returned list contains array(s) of dtype double, complex double, or
85
object. A 1-d argument of shape ``(N,)`` is parsed into ``N`` arrays of
86
size one; a 2-d argument of shape ``(M,N)`` is parsed into ``M`` arrays
87
of size ``N`` (i.e., is "parsed by row"); and a higher dimensional array
88
raises a Value Error if it is not first reshaped into either a 1-d or 2-d
89
array.
90
91
Parameters
92
----------
93
alist : array_like
94
A 1- or 2-d array_like
95
trim : boolean, optional
96
When True, trailing zeros are removed from the inputs.
97
When False, the inputs are passed through intact.
98
99
Returns
100
-------
101
[a1, a2,...] : list of 1-D arrays
102
A copy of the input data as a list of 1-d arrays.
103
104
Raises
105
------
106
ValueError
107
Raised when `as_series` cannot convert its input to 1-d arrays, or at
108
least one of the resulting arrays is empty.
109
110
Examples
111
--------
112
>>> from numpy.polynomial import polyutils as pu
113
>>> a = np.arange(4)
114
>>> pu.as_series(a)
115
[array([0.]), array([1.]), array([2.]), array([3.])]
116
>>> b = np.arange(6).reshape((2,3))
117
>>> pu.as_series(b)
118
[array([0., 1., 2.]), array([3., 4., 5.])]
119
120
>>> pu.as_series((1, np.arange(3), np.arange(2, dtype=np.float16)))
121
[array([1.]), array([0., 1., 2.]), array([0., 1.])]
122
123
>>> pu.as_series([2, [1.1, 0.]])
124
[array([2.]), array([1.1])]
125
126
>>> pu.as_series([2, [1.1, 0.]], trim=False)
127
[array([2.]), array([1.1, 0. ])]
128
129
"""
130
arrays = [np.array(a, ndmin=1, copy=False) for a in alist]
131
if min([a.size for a in arrays]) == 0:
132
raise ValueError("Coefficient array is empty")
133
if any(a.ndim != 1 for a in arrays):
134
raise ValueError("Coefficient array is not 1-d")
135
if trim:
136
arrays = [trimseq(a) for a in arrays]
137
138
if any(a.dtype == np.dtype(object) for a in arrays):
139
ret = []
140
for a in arrays:
141
if a.dtype != np.dtype(object):
142
tmp = np.empty(len(a), dtype=np.dtype(object))
143
tmp[:] = a[:]
144
ret.append(tmp)
145
else:
146
ret.append(a.copy())
147
else:
148
try:
149
dtype = np.common_type(*arrays)
150
except Exception as e:
151
raise ValueError("Coefficient arrays have no common type") from e
152
ret = [np.array(a, copy=True, dtype=dtype) for a in arrays]
153
return ret
154
155
156
def trimcoef(c, tol=0):
157
"""
158
Remove "small" "trailing" coefficients from a polynomial.
159
160
"Small" means "small in absolute value" and is controlled by the
161
parameter `tol`; "trailing" means highest order coefficient(s), e.g., in
162
``[0, 1, 1, 0, 0]`` (which represents ``0 + x + x**2 + 0*x**3 + 0*x**4``)
163
both the 3-rd and 4-th order coefficients would be "trimmed."
164
165
Parameters
166
----------
167
c : array_like
168
1-d array of coefficients, ordered from lowest order to highest.
169
tol : number, optional
170
Trailing (i.e., highest order) elements with absolute value less
171
than or equal to `tol` (default value is zero) are removed.
172
173
Returns
174
-------
175
trimmed : ndarray
176
1-d array with trailing zeros removed. If the resulting series
177
would be empty, a series containing a single zero is returned.
178
179
Raises
180
------
181
ValueError
182
If `tol` < 0
183
184
See Also
185
--------
186
trimseq
187
188
Examples
189
--------
190
>>> from numpy.polynomial import polyutils as pu
191
>>> pu.trimcoef((0,0,3,0,5,0,0))
192
array([0., 0., 3., 0., 5.])
193
>>> pu.trimcoef((0,0,1e-3,0,1e-5,0,0),1e-3) # item == tol is trimmed
194
array([0.])
195
>>> i = complex(0,1) # works for complex
196
>>> pu.trimcoef((3e-4,1e-3*(1-i),5e-4,2e-5*(1+i)), 1e-3)
197
array([0.0003+0.j , 0.001 -0.001j])
198
199
"""
200
if tol < 0:
201
raise ValueError("tol must be non-negative")
202
203
[c] = as_series([c])
204
[ind] = np.nonzero(np.abs(c) > tol)
205
if len(ind) == 0:
206
return c[:1]*0
207
else:
208
return c[:ind[-1] + 1].copy()
209
210
def getdomain(x):
211
"""
212
Return a domain suitable for given abscissae.
213
214
Find a domain suitable for a polynomial or Chebyshev series
215
defined at the values supplied.
216
217
Parameters
218
----------
219
x : array_like
220
1-d array of abscissae whose domain will be determined.
221
222
Returns
223
-------
224
domain : ndarray
225
1-d array containing two values. If the inputs are complex, then
226
the two returned points are the lower left and upper right corners
227
of the smallest rectangle (aligned with the axes) in the complex
228
plane containing the points `x`. If the inputs are real, then the
229
two points are the ends of the smallest interval containing the
230
points `x`.
231
232
See Also
233
--------
234
mapparms, mapdomain
235
236
Examples
237
--------
238
>>> from numpy.polynomial import polyutils as pu
239
>>> points = np.arange(4)**2 - 5; points
240
array([-5, -4, -1, 4])
241
>>> pu.getdomain(points)
242
array([-5., 4.])
243
>>> c = np.exp(complex(0,1)*np.pi*np.arange(12)/6) # unit circle
244
>>> pu.getdomain(c)
245
array([-1.-1.j, 1.+1.j])
246
247
"""
248
[x] = as_series([x], trim=False)
249
if x.dtype.char in np.typecodes['Complex']:
250
rmin, rmax = x.real.min(), x.real.max()
251
imin, imax = x.imag.min(), x.imag.max()
252
return np.array((complex(rmin, imin), complex(rmax, imax)))
253
else:
254
return np.array((x.min(), x.max()))
255
256
def mapparms(old, new):
257
"""
258
Linear map parameters between domains.
259
260
Return the parameters of the linear map ``offset + scale*x`` that maps
261
`old` to `new` such that ``old[i] -> new[i]``, ``i = 0, 1``.
262
263
Parameters
264
----------
265
old, new : array_like
266
Domains. Each domain must (successfully) convert to a 1-d array
267
containing precisely two values.
268
269
Returns
270
-------
271
offset, scale : scalars
272
The map ``L(x) = offset + scale*x`` maps the first domain to the
273
second.
274
275
See Also
276
--------
277
getdomain, mapdomain
278
279
Notes
280
-----
281
Also works for complex numbers, and thus can be used to calculate the
282
parameters required to map any line in the complex plane to any other
283
line therein.
284
285
Examples
286
--------
287
>>> from numpy.polynomial import polyutils as pu
288
>>> pu.mapparms((-1,1),(-1,1))
289
(0.0, 1.0)
290
>>> pu.mapparms((1,-1),(-1,1))
291
(-0.0, -1.0)
292
>>> i = complex(0,1)
293
>>> pu.mapparms((-i,-1),(1,i))
294
((1+1j), (1-0j))
295
296
"""
297
oldlen = old[1] - old[0]
298
newlen = new[1] - new[0]
299
off = (old[1]*new[0] - old[0]*new[1])/oldlen
300
scl = newlen/oldlen
301
return off, scl
302
303
def mapdomain(x, old, new):
304
"""
305
Apply linear map to input points.
306
307
The linear map ``offset + scale*x`` that maps the domain `old` to
308
the domain `new` is applied to the points `x`.
309
310
Parameters
311
----------
312
x : array_like
313
Points to be mapped. If `x` is a subtype of ndarray the subtype
314
will be preserved.
315
old, new : array_like
316
The two domains that determine the map. Each must (successfully)
317
convert to 1-d arrays containing precisely two values.
318
319
Returns
320
-------
321
x_out : ndarray
322
Array of points of the same shape as `x`, after application of the
323
linear map between the two domains.
324
325
See Also
326
--------
327
getdomain, mapparms
328
329
Notes
330
-----
331
Effectively, this implements:
332
333
.. math::
334
x\\_out = new[0] + m(x - old[0])
335
336
where
337
338
.. math::
339
m = \\frac{new[1]-new[0]}{old[1]-old[0]}
340
341
Examples
342
--------
343
>>> from numpy.polynomial import polyutils as pu
344
>>> old_domain = (-1,1)
345
>>> new_domain = (0,2*np.pi)
346
>>> x = np.linspace(-1,1,6); x
347
array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ])
348
>>> x_out = pu.mapdomain(x, old_domain, new_domain); x_out
349
array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825, # may vary
350
6.28318531])
351
>>> x - pu.mapdomain(x_out, new_domain, old_domain)
352
array([0., 0., 0., 0., 0., 0.])
353
354
Also works for complex numbers (and thus can be used to map any line in
355
the complex plane to any other line therein).
356
357
>>> i = complex(0,1)
358
>>> old = (-1 - i, 1 + i)
359
>>> new = (-1 + i, 1 - i)
360
>>> z = np.linspace(old[0], old[1], 6); z
361
array([-1. -1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1. +1.j ])
362
>>> new_z = pu.mapdomain(z, old, new); new_z
363
array([-1.0+1.j , -0.6+0.6j, -0.2+0.2j, 0.2-0.2j, 0.6-0.6j, 1.0-1.j ]) # may vary
364
365
"""
366
x = np.asanyarray(x)
367
off, scl = mapparms(old, new)
368
return off + scl*x
369
370
371
def _nth_slice(i, ndim):
372
sl = [np.newaxis] * ndim
373
sl[i] = slice(None)
374
return tuple(sl)
375
376
377
def _vander_nd(vander_fs, points, degrees):
378
r"""
379
A generalization of the Vandermonde matrix for N dimensions
380
381
The result is built by combining the results of 1d Vandermonde matrices,
382
383
.. math::
384
W[i_0, \ldots, i_M, j_0, \ldots, j_N] = \prod_{k=0}^N{V_k(x_k)[i_0, \ldots, i_M, j_k]}
385
386
where
387
388
.. math::
389
N &= \texttt{len(points)} = \texttt{len(degrees)} = \texttt{len(vander\_fs)} \\
390
M &= \texttt{points[k].ndim} \\
391
V_k &= \texttt{vander\_fs[k]} \\
392
x_k &= \texttt{points[k]} \\
393
0 \le j_k &\le \texttt{degrees[k]}
394
395
Expanding the one-dimensional :math:`V_k` functions gives:
396
397
.. math::
398
W[i_0, \ldots, i_M, j_0, \ldots, j_N] = \prod_{k=0}^N{B_{k, j_k}(x_k[i_0, \ldots, i_M])}
399
400
where :math:`B_{k,m}` is the m'th basis of the polynomial construction used along
401
dimension :math:`k`. For a regular polynomial, :math:`B_{k, m}(x) = P_m(x) = x^m`.
402
403
Parameters
404
----------
405
vander_fs : Sequence[function(array_like, int) -> ndarray]
406
The 1d vander function to use for each axis, such as ``polyvander``
407
points : Sequence[array_like]
408
Arrays of point coordinates, all of the same shape. The dtypes
409
will be converted to either float64 or complex128 depending on
410
whether any of the elements are complex. Scalars are converted to
411
1-D arrays.
412
This must be the same length as `vander_fs`.
413
degrees : Sequence[int]
414
The maximum degree (inclusive) to use for each axis.
415
This must be the same length as `vander_fs`.
416
417
Returns
418
-------
419
vander_nd : ndarray
420
An array of shape ``points[0].shape + tuple(d + 1 for d in degrees)``.
421
"""
422
n_dims = len(vander_fs)
423
if n_dims != len(points):
424
raise ValueError(
425
f"Expected {n_dims} dimensions of sample points, got {len(points)}")
426
if n_dims != len(degrees):
427
raise ValueError(
428
f"Expected {n_dims} dimensions of degrees, got {len(degrees)}")
429
if n_dims == 0:
430
raise ValueError("Unable to guess a dtype or shape when no points are given")
431
432
# convert to the same shape and type
433
points = tuple(np.array(tuple(points), copy=False) + 0.0)
434
435
# produce the vandermonde matrix for each dimension, placing the last
436
# axis of each in an independent trailing axis of the output
437
vander_arrays = (
438
vander_fs[i](points[i], degrees[i])[(...,) + _nth_slice(i, n_dims)]
439
for i in range(n_dims)
440
)
441
442
# we checked this wasn't empty already, so no `initial` needed
443
return functools.reduce(operator.mul, vander_arrays)
444
445
446
def _vander_nd_flat(vander_fs, points, degrees):
447
"""
448
Like `_vander_nd`, but flattens the last ``len(degrees)`` axes into a single axis
449
450
Used to implement the public ``<type>vander<n>d`` functions.
451
"""
452
v = _vander_nd(vander_fs, points, degrees)
453
return v.reshape(v.shape[:-len(degrees)] + (-1,))
454
455
456
def _fromroots(line_f, mul_f, roots):
457
"""
458
Helper function used to implement the ``<type>fromroots`` functions.
459
460
Parameters
461
----------
462
line_f : function(float, float) -> ndarray
463
The ``<type>line`` function, such as ``polyline``
464
mul_f : function(array_like, array_like) -> ndarray
465
The ``<type>mul`` function, such as ``polymul``
466
roots
467
See the ``<type>fromroots`` functions for more detail
468
"""
469
if len(roots) == 0:
470
return np.ones(1)
471
else:
472
[roots] = as_series([roots], trim=False)
473
roots.sort()
474
p = [line_f(-r, 1) for r in roots]
475
n = len(p)
476
while n > 1:
477
m, r = divmod(n, 2)
478
tmp = [mul_f(p[i], p[i+m]) for i in range(m)]
479
if r:
480
tmp[0] = mul_f(tmp[0], p[-1])
481
p = tmp
482
n = m
483
return p[0]
484
485
486
def _valnd(val_f, c, *args):
487
"""
488
Helper function used to implement the ``<type>val<n>d`` functions.
489
490
Parameters
491
----------
492
val_f : function(array_like, array_like, tensor: bool) -> array_like
493
The ``<type>val`` function, such as ``polyval``
494
c, args
495
See the ``<type>val<n>d`` functions for more detail
496
"""
497
args = [np.asanyarray(a) for a in args]
498
shape0 = args[0].shape
499
if not all((a.shape == shape0 for a in args[1:])):
500
if len(args) == 3:
501
raise ValueError('x, y, z are incompatible')
502
elif len(args) == 2:
503
raise ValueError('x, y are incompatible')
504
else:
505
raise ValueError('ordinates are incompatible')
506
it = iter(args)
507
x0 = next(it)
508
509
# use tensor on only the first
510
c = val_f(x0, c)
511
for xi in it:
512
c = val_f(xi, c, tensor=False)
513
return c
514
515
516
def _gridnd(val_f, c, *args):
517
"""
518
Helper function used to implement the ``<type>grid<n>d`` functions.
519
520
Parameters
521
----------
522
val_f : function(array_like, array_like, tensor: bool) -> array_like
523
The ``<type>val`` function, such as ``polyval``
524
c, args
525
See the ``<type>grid<n>d`` functions for more detail
526
"""
527
for xi in args:
528
c = val_f(xi, c)
529
return c
530
531
532
def _div(mul_f, c1, c2):
533
"""
534
Helper function used to implement the ``<type>div`` functions.
535
536
Implementation uses repeated subtraction of c2 multiplied by the nth basis.
537
For some polynomial types, a more efficient approach may be possible.
538
539
Parameters
540
----------
541
mul_f : function(array_like, array_like) -> array_like
542
The ``<type>mul`` function, such as ``polymul``
543
c1, c2
544
See the ``<type>div`` functions for more detail
545
"""
546
# c1, c2 are trimmed copies
547
[c1, c2] = as_series([c1, c2])
548
if c2[-1] == 0:
549
raise ZeroDivisionError()
550
551
lc1 = len(c1)
552
lc2 = len(c2)
553
if lc1 < lc2:
554
return c1[:1]*0, c1
555
elif lc2 == 1:
556
return c1/c2[-1], c1[:1]*0
557
else:
558
quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype)
559
rem = c1
560
for i in range(lc1 - lc2, - 1, -1):
561
p = mul_f([0]*i + [1], c2)
562
q = rem[-1]/p[-1]
563
rem = rem[:-1] - q*p[:-1]
564
quo[i] = q
565
return quo, trimseq(rem)
566
567
568
def _add(c1, c2):
569
""" Helper function used to implement the ``<type>add`` functions. """
570
# c1, c2 are trimmed copies
571
[c1, c2] = as_series([c1, c2])
572
if len(c1) > len(c2):
573
c1[:c2.size] += c2
574
ret = c1
575
else:
576
c2[:c1.size] += c1
577
ret = c2
578
return trimseq(ret)
579
580
581
def _sub(c1, c2):
582
""" Helper function used to implement the ``<type>sub`` functions. """
583
# c1, c2 are trimmed copies
584
[c1, c2] = as_series([c1, c2])
585
if len(c1) > len(c2):
586
c1[:c2.size] -= c2
587
ret = c1
588
else:
589
c2 = -c2
590
c2[:c1.size] += c1
591
ret = c2
592
return trimseq(ret)
593
594
595
def _fit(vander_f, x, y, deg, rcond=None, full=False, w=None):
596
"""
597
Helper function used to implement the ``<type>fit`` functions.
598
599
Parameters
600
----------
601
vander_f : function(array_like, int) -> ndarray
602
The 1d vander function, such as ``polyvander``
603
c1, c2
604
See the ``<type>fit`` functions for more detail
605
"""
606
x = np.asarray(x) + 0.0
607
y = np.asarray(y) + 0.0
608
deg = np.asarray(deg)
609
610
# check arguments.
611
if deg.ndim > 1 or deg.dtype.kind not in 'iu' or deg.size == 0:
612
raise TypeError("deg must be an int or non-empty 1-D array of int")
613
if deg.min() < 0:
614
raise ValueError("expected deg >= 0")
615
if x.ndim != 1:
616
raise TypeError("expected 1D vector for x")
617
if x.size == 0:
618
raise TypeError("expected non-empty vector for x")
619
if y.ndim < 1 or y.ndim > 2:
620
raise TypeError("expected 1D or 2D array for y")
621
if len(x) != len(y):
622
raise TypeError("expected x and y to have same length")
623
624
if deg.ndim == 0:
625
lmax = deg
626
order = lmax + 1
627
van = vander_f(x, lmax)
628
else:
629
deg = np.sort(deg)
630
lmax = deg[-1]
631
order = len(deg)
632
van = vander_f(x, lmax)[:, deg]
633
634
# set up the least squares matrices in transposed form
635
lhs = van.T
636
rhs = y.T
637
if w is not None:
638
w = np.asarray(w) + 0.0
639
if w.ndim != 1:
640
raise TypeError("expected 1D vector for w")
641
if len(x) != len(w):
642
raise TypeError("expected x and w to have same length")
643
# apply weights. Don't use inplace operations as they
644
# can cause problems with NA.
645
lhs = lhs * w
646
rhs = rhs * w
647
648
# set rcond
649
if rcond is None:
650
rcond = len(x)*np.finfo(x.dtype).eps
651
652
# Determine the norms of the design matrix columns.
653
if issubclass(lhs.dtype.type, np.complexfloating):
654
scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1))
655
else:
656
scl = np.sqrt(np.square(lhs).sum(1))
657
scl[scl == 0] = 1
658
659
# Solve the least squares problem.
660
c, resids, rank, s = np.linalg.lstsq(lhs.T/scl, rhs.T, rcond)
661
c = (c.T/scl).T
662
663
# Expand c to include non-fitted coefficients which are set to zero
664
if deg.ndim > 0:
665
if c.ndim == 2:
666
cc = np.zeros((lmax+1, c.shape[1]), dtype=c.dtype)
667
else:
668
cc = np.zeros(lmax+1, dtype=c.dtype)
669
cc[deg] = c
670
c = cc
671
672
# warn on rank reduction
673
if rank != order and not full:
674
msg = "The fit may be poorly conditioned"
675
warnings.warn(msg, RankWarning, stacklevel=2)
676
677
if full:
678
return c, [resids, rank, s, rcond]
679
else:
680
return c
681
682
683
def _pow(mul_f, c, pow, maxpower):
684
"""
685
Helper function used to implement the ``<type>pow`` functions.
686
687
Parameters
688
----------
689
mul_f : function(array_like, array_like) -> ndarray
690
The ``<type>mul`` function, such as ``polymul``
691
c : array_like
692
1-D array of array of series coefficients
693
pow, maxpower
694
See the ``<type>pow`` functions for more detail
695
"""
696
# c is a trimmed copy
697
[c] = as_series([c])
698
power = int(pow)
699
if power != pow or power < 0:
700
raise ValueError("Power must be a non-negative integer.")
701
elif maxpower is not None and power > maxpower:
702
raise ValueError("Power is too large")
703
elif power == 0:
704
return np.array([1], dtype=c.dtype)
705
elif power == 1:
706
return c
707
else:
708
# This can be made more efficient by using powers of two
709
# in the usual way.
710
prd = c
711
for i in range(2, power + 1):
712
prd = mul_f(prd, c)
713
return prd
714
715
716
def _deprecate_as_int(x, desc):
717
"""
718
Like `operator.index`, but emits a deprecation warning when passed a float
719
720
Parameters
721
----------
722
x : int-like, or float with integral value
723
Value to interpret as an integer
724
desc : str
725
description to include in any error message
726
727
Raises
728
------
729
TypeError : if x is a non-integral float or non-numeric
730
DeprecationWarning : if x is an integral float
731
"""
732
try:
733
return operator.index(x)
734
except TypeError as e:
735
# Numpy 1.17.0, 2019-03-11
736
try:
737
ix = int(x)
738
except TypeError:
739
pass
740
else:
741
if ix == x:
742
warnings.warn(
743
f"In future, this will raise TypeError, as {desc} will "
744
"need to be an integer not just an integral float.",
745
DeprecationWarning,
746
stacklevel=3
747
)
748
return ix
749
750
raise TypeError(f"{desc} must be an integer") from e
751
752