Reputation: 197
I'm trying to implement the simplex method in Python so I need to use the Gaussian elimination on arrays. Very often fractions come up and for more clarity and precision I would like to keep the fractional form instead of using floats. I know the 'fractions' module but I'm struggling to use it. I wrote my code using this module but the arrays are always returned with floats. Isn't it possible to print an array with fractions inside ? On this basic example :
>>> A
array([[-1., 1.],
[-2., -1.]])
>>> A[0][0] = Fraction(2, 3)
>>> A
array([[ 0.66666667, 1. ],
[-2. , -1. ]])
I would like to have
array([[2/3, 1. ],
[-2., -1. ]])
It seems numpy always switches to floats
Upvotes: 13
Views: 37333
Reputation: 2515
You can also convert the entire array to an object
array of Fraction
objects, by abusing the element-wise conversion of numpy arrays under arithmetic operations. (Note: this requires the original array to be an integer array, since arithmetic between float
s and Fractions
produce float
s.)
>>> A = np.array([[-1, 1],[-2, -1]])
array([[-1, 1],
[-2, -1]])
>>>
>>> A.dtype
dtype('int64')
>>>
>>> A = A + Fraction()
>>> A
array([[Fraction(-1, 1), Fraction(1, 1)],
[Fraction(-2, 1), Fraction(-1, 1)]], dtype=object)
With the array in this format, any further arithmetic performed will be over elements of type Fraction
.
As mentioned by @Hi-Angel in the comments, there are a number of NumPy/SciPy functions (e.g., np.linalg.inv
) that expect input arrays to use a primitive dtype (e.g., int32
, float64
, etc.); these functions tend to be C/Cython-optimized routines that only work on C-primitives. And because fractions.Fraction
is a Python object, these functions will not work on arrays of Fraction
s.
And as mentioned elsewhere, even the functions that do work on Fraction
arrays will run notably slower on them, compared to running on NumPy arrays of primitive dtypes.
However, if you just need a custom numeric object for your application, like the arbitrary-precision rational type Fraction
or the base-10 floating-point type decimal.Decimal
, and want the convenience of e.g. element-wise operations on arrays, you CAN use NumPy arrays to achieve that using the method above or similar methods.
But it's not as fast or well-supported as using arrays of primitives, so personally if I don't NEED a custom number type I just use float64
s or int64
s.
Upvotes: 11
Reputation: 880547
Since Fraction
s are not a native NumPy dtype, to store a Fraction
in a NumPy array you need to convert the array to object
dtype:
import numpy as np
from fractions import Fraction
A = np.array([[-1., 1.],
[-2., -1.]]) # <-- creates an array with a floating-point dtype (float32 or float64 depending on your OS)
A = A.astype('object')
A[0, 0] = Fraction(2,3)
print(A)
prints
[[Fraction(2, 3) 1.0]
[-2.0 -1.0]]
PS. As user2357112 suggests, you might be better off using sympy
if you wish to use rational numbers. Or, just represent the matrix as a list of lists. There are no speed advantages to using NumPy if your arrays are of object
dtype.
import sympy as sy
A = [[-1., 1.],
[-2., -1.]]
A[0][0] = sy.Rational('2/3')
print(A)
prints
[[2/3, 1.0], [-2.0, -1.0]]
Upvotes: 7