Astropy cannot parse inches - python

I am trying to convert inches to mm with astropy.
In input I have unit as string ("inch","mm"). I create for it example function:
def astro_conv(self, amount: float, fromm: str, to: str) -> float:
u_from = u.Unit(fromm)
u_to = u.Unit(to)
return u_from.to(u_to, amount)
I got message:{ValueError}'inch' did not parse as unit: At col 0, inch is not a valid unit. I have check documentation and inch should be available: http://docs.astropy.org/en/stable/units/
What am I doing wrong?

Per the docs, Astropy does not include imperial units defined by default. I think this is in part to reduce the default units namespace and the overhead involved in creating and searching it, and imperial units get sacrificed in this case since they are less used in astronomy for the most part:
This package defines colloquially used Imperial units. They are available in the astropy.units.imperial namespace, but not in the top-level astropy.units namespace, e.g.:
>>> import astropy.units as u
>>> mph = u.imperial.mile / u.hour
>>> mph
Unit("mi / h")
To include them in compose and the results of find_equivalent_units, do:
>>> import astropy.units as u
>>> u.imperial.enable()

Related

How to convert voltage (or frequency) floating number read backs to mV (or kHz)?

I am successfully able to read back data from an instrument:
When the read back is a voltage, I typically read back values such as 5.34e-02 Volts.
When the read back is frequency, I typically read values like 2.95e+04or 1.49e+05 with units Hz.
I would like to convert the voltage read back of 5.34e-02 to exponent e-3 (aka millivolts), ie.. 53.4e-3. next, I would like to extract the mantissa 53.4 out of this because I want all my data needs to be in milliVolts.
Similarly, I would like to convert all the frequency such as 2.95e+04 (or 1.49e+05) to kiloHz, ie... 29.5e+03 or 149e+03. Next would like to extract the mantissa 29.5 and 149 from this since all my data needs to be kHz.
Can someone suggest how to do this?
Well, to convert volts to millivolts, you multiply by 1000. To convert Hz to kHz, you divide by 1000.
>>> reading = 5.34e-02
>>> millivolts = reading * 1000
>>> print(millivolts)
53.400000000000006
>>> hz = 2.95e+04
>>> khz = hz /1000
>>> khz
29.5
>>>
FOLLOW-UP
OK, assuming your real goal is to keep the units the same but adjust the exponent to a multiple of 3, see if this meets your needs.
def convert(val):
if isinstance(val,int):
return str(val)
cvt = f"{val:3.2e}"
if 'e' not in cvt:
return cvt
# a will be #.##
# b will be -##
a,b = cvt.split('e')
exp = int(b)
if exp % 3 == 0:
return cvt
if exp % 3 == 1:
a = a[0]+a[2]+a[1]+a[3]
exp = abs(exp-1)
return f"{a}e{b[0]}{exp:02d}"
a = a[0]+a[2]+a[3]+a[1]
exp = abs(exp-2)
return f"{a}e{b[0]}{exp:02d}"
for val in (5.34e-01, 2.95e+03, 5.34e-02, 2.95e+04, 5.34e-03, 2.95e+06):
print( f"{val:3.2e} ->", convert(val) )
Output:
5.34e-01 -> 534.e-03
2.95e+03 -> 2.95e+03
5.34e-02 -> 53.4e-03
2.95e+04 -> 29.5e+03
5.34e-03 -> 5.34e-03
2.95e+06 -> 2.95e+06
In this case, I think multiplying/dividing by 1000 is enough to move between SI prefixes. But when units get more complicated it might help to use a library like Pint to keep track of things and make sure you're calculating what you think you are.
In this case you might do:
import pint
ureg = pint.UnitRegistry()
Q = ureg.Quantity
reading_v = Q(5.34e-02, 'volts')
reading_mv = reading_v.to('millivolts')
print(reading_mv.magnitude)
but it seems overkill here.

How to convert unit prefixes in Sympy

I would like to know how to convert a sympy value with a physical unit into the same unit with another prefix. For example:
>>> import sympy.physics.units as u
>>> x = 0.001 * u.kilogram
0.001kg
should be converted to grams. The approach I have taken so far is very bloated and delivers a wrong result.
>>> x / u.kilogram * u.gram
1.0^-6kg
It should be 1g instead.
If you can accept printing 1 instead of 1g, you could just use division:
>>> x / u.g
1.0
Otherwise, you'd better switch to sympy.physics.unitsystems.
>>> from sympy.physics.unitsystems import Quantity
>>> from sympy.physics.unitsystems.systems import mks
>>> Quantity(0.001, mks['kg'])
0.001kg
>>> _.convert_to(mks['g'])
1g
>>> u.convert_to(x, u.gram)
1.0*gram

What does the .N mean in this block of Python code?

I am working through Learn Python the Hard Way and am browsing through some code on Git Hub before moving on. I am just curious what the .N does on the line with "tm.N = 1000" and how it relates to the end of the code.
import matplotlib.pyplot as plt
import random
import pandas.util.testing as tm
tm.N = 1000
df = tm.makeTimeDataFrame()
import string
foo = list(string.letters[:5]) * 200
df['indic'] = list(string.letters[:5]) * 200
random.shuffle(foo)
df['indic2'] = foo
df.boxplot(by=['indic', 'indic2'], fontsize=8, rot=90)
plt.show()
N is a global in the testing.py module, that is used all around the module to test arrays and other things. Its default value is 30. E.g.
np.arange(N * K).reshape((N, K))
Series(randn(N), index=index)
In the code you're posting it have poor usage, because makeTimeDataFrame can be feed with a nper parameter that end up being substituted by N if nper is not provided. This is the correct usage, that would not confuse you:
df = tm.makeTimeDataFrame(nper=1000)
The previous line, import pandas.util.testing as tm, imports the module pandas.util.testing and, for convenience, gives it the name tm. Thus, tm afterwards refers to this module, and so tm.N refers to the object named "N" (whatever that is) in the module.
Source: https://github.com/pydata/pandas/blob/master/pandas/util/testing.py
N is a variable in the pandas.util.testing library (imported as tm). It's used in a few of the functions defined in that library, including the makeTimeSeries function called in the getTimeSeriesData which is in turn called in the makeTimeDataFrame function that you call with df = tm.makeTimeDataFrame()
You can get information about pandas.util.testing.N from the docstring and the type() function:
>>> tm.N.__doc__
'int(x[, base]) -> integer\n\nConvert a string or number to an integer, if possible. A floating point\nargument will be truncated towards zero (this does not include a string\nrepresentation of a floating point number!) When converting a string, use\nthe optional base. It is an error to supply a base when converting a\nnon-string. If base is zero, the proper base is guessed based on the\nstring content. If the argument is outside the integer range a\nlong object will be returned instead.'
>>> print(tm.N.__doc__)
int(x[, base]) -> integer
Convert a string or number to an integer, if possible. A floating point
argument will be truncated towards zero (this does not include a string
representation of a floating point number!) When converting a string, use
the optional base. It is an error to supply a base when converting a
non-string. If base is zero, the proper base is guessed based on the
string content. If the argument is outside the integer range a
long object will be returned instead.
>>> type(tm.N)
<type 'int'>
In pandas in the module pandas.util.testing the N property means TimeSeries
See this reference in the section:
We could alternatively have used the unit testing function to create a TimeSeries of length 20:
>>>> pandas.util.testing.N = 20
>>>> ts = pandas.util.testing.makeTimeSeries()
It makes a timeseries of length 1000.
>>> df.head()
Out[7]:
A B C D
2000-01-03 -0.734093 -0.843961 -0.879394 0.415565
2000-01-04 0.028562 -1.098165 1.292156 0.512677
2000-01-05 1.135995 -0.864060 1.297646 -0.166932
2000-01-06 -0.738651 0.426662 0.505882 -0.124671
2000-01-07 -1.242401 0.225207 0.053541 -0.234740
>>> len(df)
Out[8]: 1000
.N provides the number of elements in array type. For example, if you use a colormap,
plt.get_cmap('Pastel1').N will return 9 because it consists of 9 colors whereas
plt.get_cmap('nipy_spectral').N will return 256

Parse Python String for Units

Say I have a set of strings like the following:
"5 m^2"
"17 sq feet"
"3 inches"
"89 meters"
Is there a Python package which will read such strings, convert them to SI, and return the result in an easily-usable form? For instance:
>>> a=dream_parser.parse("17 sq feet")
>>> a.quantity
1.5793517
>>> a.type
'area'
>>> a.unit
'm^2'
Quantulum will do exactly what you described
Excerpt from its description:
from quantulum import parser
quants = parser.parse('I want 2 liters of wine')
# quants [Quantity(2, 'litre')]
More recently, pint is a good place to start for most of these.
Is there an extension for ipython that can do at least part of what you want. It's called ipython-physics
It does store value and units and allows (at least) some basic math. I have never used it myself, so I don't know how easy would be to use in a python script
If you have 'nice' strings then use pint.
(best for unit conversions)
import pint
u = pint.UnitRegistry()
value = u.quantity("89 meters")
If you have text/sentences then use quantulum
from quantulum import parser
value = parser.parse('Pass me a 300 ml beer.')
If you have 'ugly' strings then use try unit_parse.
Examples of 'ugly' strings: (see unit_parse github for more examples)
2.3 mlgcm --> 2.3 cm * g * ml
5E1 g/mol --> 50.0 g / mol
5 e1 g/mol --> 50.0 g / mol
()4.0 (°C) --> 4.0 °C
37.34 kJ/mole (at 25 °C) --> [[<Quantity(37.34, 'kilojoule / mole')>, <Quantity(25, 'degree_Celsius')>]]
Detection in water: 0.73 ppm; Chemically pure --> 0.73 ppm
(uses pint under the hood)
from unit_parse import parser
result = parser("1.23 g/cm3 (at 25 °C)")
print(result) # [[<Quantity(1.23, 'g / cm ** 3')>, <Quantity(25, 'degC')>]]

Formatting Complex Numbers

For a project in one of my classes we have to output numbers up to five decimal places.It is possible that the output will be a complex number and I am unable to figure out how to output a complex number with five decimal places. For floats I know it is just:
print "%0.5f"%variable_name
Is there something similar for complex numbers?
You could do it as is shown below using the str.format() method:
>>> n = 3.4+2.3j
>>> n
(3.4+2.3j)
>>> '({0.real:.2f} + {0.imag:.2f}i)'.format(n)
'(3.40 + 2.30i)'
>>> '({c.real:.2f} + {c.imag:.2f}i)'.format(c=n)
'(3.40 + 2.30i)'
To make it handle both positive and negative imaginary portions properly, you would need a (even more) complicated formatting operation:
>>> n = 3.4-2.3j
>>> n
(3.4-2.3j)
>>> '({0:.2f} {1} {2:.2f}i)'.format(n.real, '+-'[n.imag < 0], abs(n.imag))
'(3.40 - 2.30i)'
Update - Easier Way
Although you cannot use f as a presentation type for complex numbers using the string formatting operator %:
n1 = 3.4+2.3j
n2 = 3.4-2.3j
try:
print('test: %.2f' % n1)
except Exception as exc:
print('{}: {}'.format(type(exc).__name__, exc))
Output:
TypeError: float argument required, not complex
You can however use it with complex numbers via the str.format() method. This isn't explicitly documented, but is implied by the Format Specification Mini-Language documentation which just says:
'f'  Fixed point. Displays the number as a fixed-point number. The default precision is 6.
. . .so it's easy to overlook.
In concrete terms, the following works in both Python 2.7.14 and 3.4.6:
print('n1: {:.2f}'.format(n1))
print('n2: {:.2f}'.format(n2))
Output:
n1: 3.10+4.20j
n2: 3.10-4.20j
This doesn't give you quite the control the code in my original answer does, but it's certainly much more concise (and handles both positive and negative imaginary parts automatically).
Update 2 - f-strings
Formatted string literals (aka f-strings) were added in Python 3.6, which means it could also be done like this in that version or later:
print(f'n1: {n1:.2f}') # -> n1: 3.40+2.30j
print(f'n2: {n2:.3f}') # -> n2: 3.400-2.300j
In Python 3.8.0, support for an = specifier was added to f-strings, allowing you to write:
print(f'{n1=:.2f}') # -> n1=3.40+2.30j
print(f'{n2=:.3f}') # -> n2=3.400-2.300j
Neither String Formatting Operations - i.e. the modulo (%) operator) -
nor the newer str.format() Format String Syntax support complex types.
However it is possible to call the __format__ method of all built in numeric types directly.
Here is an example:
>>> i = -3 # int
>>> l = -33L # long (only Python 2.X)
>>> f = -10./3 # float
>>> c = - 1./9 - 2.j/9 # complex
>>> [ x.__format__('.3f') for x in (i, l, f, c)]
['-3.000', '-33.000', '-3.333', '-0.111-0.222j']
Note, that this works well with negative imaginary parts too.
For questions like this, the Python documentation should be your first stop. Specifically, have a look at the section on string formatting. It lists all the string format codes; there isn't one for complex numbers.
What you can do is format the real and imaginary parts of the number separately, using x.real and x.imag, and print it out in a + bi form.
>>> n = 3.4 + 2.3j
>>> print '%05f %05fi' % (n.real, n.imag)
3.400000 2.300000i
As of Python 2.6 you can define how objects of your own classes respond to format strings. So, you can define a subclass of complex that can be formatted. Here's an example:
>>> class Complex_formatted(complex):
... def __format__(self, fmt):
... cfmt = "({:" + fmt + "}{:+" + fmt + "}j)"
... return cfmt.format(self.real, self.imag)
...
>>> z1 = Complex_formatted(.123456789 + 123.456789j)
>>> z2 = Complex_formatted(.123456789 - 123.456789j)
>>> "My complex numbers are {:0.5f} and {:0.5f}.".format(z1, z2)
'My complex numbers are (0.12346+123.45679j) and (0.12346-123.45679j).'
>>> "My complex numbers are {:0.6f} and {:0.6f}.".format(z1, z2)
'My complex numbers are (0.123457+123.456789j) and (0.123457-123.456789j).'
Objects of this class behave exactly like complex numbers except they take more space and operate more slowly; reader beware.
Check this out:
np.set_printoptions(precision=2) # Rounds up to 2 decimals all float expressions
I've successfully printed my complexfloat's expressions:
# Show poles and zeros
print( "zeros = ", zeros_H , "\n")
print( "poles = ", poles_H )
out before:
zeros = [-0.8 +0.6j -0.8 -0.6j -0.66666667+0.j ]
poles = [-0.81542318+0.60991027j -0.81542318-0.60991027j -0.8358203 +0.j ]
out after:
zeros = [-0.8 +0.6j -0.8 -0.6j -0.67+0.j ]
poles = [-0.82+0.61j -0.82-0.61j -0.84+0.j ]

Categories