Jkev
Jkev

Reputation: 197

How to use numpy arrays with fractions?

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

Answers (2)

CrepeGoat
CrepeGoat

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 floats and Fractions produce floats.)

>>> 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.

Edit: disclaimers

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 Fractions.

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 float64s or int64s.

Upvotes: 11

unutbu
unutbu

Reputation: 880547

Since Fractions 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

Related Questions