Извлечение значимого и показателя степени для представления base-10 из десятичной строки
Я ищу эффективную реализацию Python функции, которая принимает строку в десятичном формате, например
2.05000
200
0.012
и возвращает кортеж из двух целых чисел, представляющих значение и экспоненту ввода в формате с плавающей запятой 10, например
(205,-2)
(2,2)
(12,-3)
Понимание списка было бы хорошим бонусом.
У меня есть ощущение, что существует эффективный (и, возможно, Pythonic) способ сделать это, но это ускользает от меня...
Раствор наносится на панд
import pandas as pd
import numpy as np
ser1 = pd.Series(['2.05000', '- 2.05000', '00 205', '-205', '-0', '-0.0', '0.00205', '0', np.nan])
ser1 = ser1.str.replace(' ', '')
parts = ser1.str.split('.').apply(pd.Series)
# remove all white spaces
# strip leading zeros (even those after a minus sign)
parts.ix[:,0] = '-'*parts.ix[:,0].str.startswith('-') + parts.ix[:,0].str.lstrip('-').str.lstrip('0')
parts.ix[:,1] = parts.ix[:,1].fillna('') # fill non-existamt decimal places
exponents = -parts.ix[:,1].str.len()
parts.ix[:,0] += parts.ix[:,1] # append decimal places to digit before decimal point
parts.ix[:,1] = parts.ix[:,0].str.rstrip('0') # strip following zeros
exponents += parts.ix[:,0].str.len() - parts.ix[:,1].str.len()
parts.ix[:,1][(parts.ix[:,1] == '') | (parts.ix[:,1] == '-')] = '0'
significands = parts.ix[:,1].astype(float)
df2 = pd.DataFrame({'exponent': exponents, 'significand': significands})
df2
Входные данные:
0 2.05000
1 - 2.05000
2 00 205
3 -205
4 -0
5 -0.0
6 0.00205
7 0
8 NaN
dtype: object
Выход:
exponent significand
0 -2 205
1 -2 -205
2 0 205
3 0 -205
4 0 0
5 0 0
6 -5 205
7 0 0
8 NaN NaN
[9 rows x 2 columns]
4 ответа
Вот простое решение для обработки строк.
def sig_exp(num_str):
parts = num_str.split('.', 2)
decimal = parts[1] if len(parts) > 1 else ''
exp = -len(decimal)
digits = parts[0].lstrip('0') + decimal
trimmed = digits.rstrip('0')
exp += len(digits) - len(trimmed)
sig = int(trimmed) if trimmed else 0
return sig, exp
>>> for x in ['2.05000', '200', '0.012', '0.0']:
print sig_exp(x)
(205, -2)
(2, 2)
(12, -3)
(0, 0)
Я оставлю обработку отрицательных чисел в качестве упражнения для читателя.
Взгляни на decimal.Decimal
:
>>> from decimal import Decimal
>>> s = '2.05000'
>>> x = Decimal(s)
>>> x
Decimal('2.05000')
>>> x.as_tuple()
DecimalTuple(sign=0, digits=(2, 0, 5, 0, 0, 0), exponent=-5)
Делает почти то, что вам нужно, просто конвертировать DecimalTuple
в желаемый формат, например:
>>> t = Decimal('2.05000').as_tuple()
>>> (''.join(str(x) for i,x in enumerate(t.digits) if any(t.digits[i:])),
... t.exponent + sum(1 for i,x in enumerate(t.digits) if not
... any (t.digits[i:])))
('205', -2)
Просто набросок, но удовлетворяет вашим трем тестам.
Вы можете захотеть .normalize()
ваш Decimal
прежде чем обрабатывать .as_tuple()
(спасибо @georg), это заботится о конечных нулях. Таким образом, вам не нужно много форматировать:
>>> Decimal('2.05000').normalize().as_tuple()
DecimalTuple(sign=0, digits=(2, 0, 5), exponent=-2)
Таким образом, ваша функция может быть записана как:
>>> def decimal_str_to_sci_tuple(s):
... t = Decimal(s).normalize().as_tuple()
... return (int(''.join(map(str,t.digits))), t.exponent)
...
>>> decimal_str_to_sci_tuple('2.05000')
(205, -2)
>>> decimal_str_to_sci_tuple('200')
(2, 2)
>>> decimal_str_to_sci_tuple('0.012')
(12, -3)
(не забудьте добавить t.sign при поддержке отрицательных чисел, хотя).
Если вы ищете научную нотацию, вы можете использовать десятичный формат и формат:
numbers = ['2.05000','200','0.01','111']
print ["{:.2E}".format(Decimal(n)) for n in numbers]
выход:
['2.05E+0', '2.00E+2', '1.00E-2']
Если вы ищете,
- Получить цифру, отличную от 0, в правой части
Получить научную запись до правой цифры
from decimal import * numbers = ['2.05000','200','0.01','111'] numbers = [ n.rstrip('0') if '.' in n else n for n in numbers ] #strip right zeros if found after . for n in numbers: if '.' in n: num = n.split('.')[0] dec = n.split('.')[1] tenthNumber = len(dec) print (Decimal(num+dec), -1 * tenthNumber) elif n.endswith('0'): tenthNumber = 0 revN = n[::-1] for i in range(len(revN)): if revN[i]=='0': tenthNumber = tenthNumber + 1 else: break print (n[:(len(n)-tenthNumber)], str(tenthNumber)) else: print (n,0)
Выход:
(Decimal('205'), -2)
('2', '2')
(Decimal('1'), -2)
('111', 0)
Вот один из методов, использующий строку форматирования venpa и начинающийся с чисел вместо строк. Если вы можете позволить себе округление значения (например, после двух цифр), вы можете просто написать:
def scd_exp(scnum):
scnum = "{:.2e}".format(scnum)
return (float(scnum[:4]),int(scnum[-3:]))
numbers = [2.05, 205, 0.0001576, 111]
for number in numbers:
print(scd_exp(number))
результат
(2.05, 0)
(2.05, 2)
(1.58, -4)
(1.11, 2)
Если вы хотите самостоятельно устанавливать значение округления при каждом вызове функции (скажем, до 6 цифр для примера), вы можете написать
def scd_exp(scnum, roundafter):
formstr = "".join(("{:.",str(roundafter),"e}"))
scnum = formstr.format(scnum)
return (float(scnum[:roundafter+2]),int(scnum[-3:]))
numbers = [2.05, 205, 0.000157595678, 111]
for number in numbers:
print(scd_exp(number, 6))
который возвращает
(2.05, 0)
(2.05, 2)
(1.575957, -4)
(1.11, 2)