Hopefully a simple one, I have a number, say 1234567.890 this number could be anything but will be this length.
How do I truncate the first 3 numbers so it turns into 4567.890?
This could be any number so subtracting 123000 will not work.
I'm working with map data in UTM coordinates (but that should not matter)
Example
x = 580992.528
y = 4275267.719
For x, I want 992.528
For y, I want 267.719
Note: y has an extra digit to the left of the decimal so 4 need removing
You can use slices for this:
x = 1234567.890
# This is still a float
x = float(str(x)[3:])
print(x)
Outputs:
4567.89
As [3:] gets the starts the index at 3 and continues to the end of the string
Update after your edit
The simplest way is to use Decimal:
from decimal import Decimal
def fmod(v, m=1000, /):
return float(Decimal(str(v)) % m)
print(fmod(x))
print(fmod(y))
Output
992.528
267.719
If you don't use string, you will have some problems with floating point in Python.
Demo:
n = 1234567.890
i = 0
while True:
m = int(n // 10**i)
if m < 1000:
break
i += 1
r = n % 10**i
Output:
>>> r
4567.889999999898
>>> round(r, 3)
4567.89
Same with Decimal from decimal module:
from decimal import Decimal
n = 1234567.890
n = Decimal(str(n))
i = 0
while True:
m = int(n // 10**i)
if m < 1000:
break
i += 1
r = n % 10**i
Output:
>>> r
Decimal('4567.89')
>>> float(r)
4567.89
This approach simply implements your idea.
int_len is the length of the integer part that we keep
sub is the rounded value that we will subtract the original float by
Code
Here is the code that implements your idea.
import math
def trim(n, digits):
int_len = len(str(int(n))) - digits # length of 4567
sub = math.floor(n / 10 **int_len) * 10**int_len
print(n - sub)
But as Kelly Bundy has pointed out, you can use modulo operation to avoid the complicated process of finding the subtrahend.
def trim(n, digits):
int_len = len(str(int(n))) - digits # length of 4567
print(n % 10**int_len)
Output
The floating point thing is a bit cursed and you may want to take Corralien's answer as an alternative.
>>> n = 1234567.890
>>> trim(n, 3)
4567.889999999898
def get_slice(number, split_n):
return number - (number // 10**split_n) * 10**split_n
Im stuck on a problem where I have to write a function that converts a denary number into a binary number using the repeated division by two algorithm. Steps Include:
The number to be converted is divided by two.
The remainder from the division is the next binary digit. Digits are added to the front of the sequence.
The result is truncated so that the input to the next division by two is always an integer.
The algorithm continues until the result is 0.
Please click the link below to see what the output should be like:
https://i.stack.imgur.com/pifUO.png
def dentobi(user):
denary = user
divide = user / 2
remainder = user % 2
binary = remainder
if user != 0:
print("Denary:", denary)
print("Divide by 2:", divide)
print("Remainder:", remainder)
print("Binary:", binary)
user = int(input("Please enter a number: "))
dentobi(user)
This is what I have done so far but Im not getting anywhere.
Can someone explain how I would do this?
The Answer provided by #user2390182 is functionally correct except that it returns an empty string when num is zero. However, I have noted on several occasions that divmod() is rather slow. Here are three slightly different techniques and their performance statistics.
import time
# This is the OP's original code edited to allow for num == 0
def binaryx(num):
b = ""
while num:
num, digit = divmod(num, 2)
b = f"{digit}{b}"
return b or '0'
# This is my preferred solution
def binaryo(n):
r = []
while n > 0:
r.append('1' if n & 1 else '0')
n >>= 1
return ''.join(reversed(r)) or '0'
# This uses techniques suggested by my namesake
def binaryy(n):
r = ''
while n > 0:
r = str(n & 1) + r
n >>= 1
return r or '0'
M = 250_000
for func in [binaryx, binaryo, binaryy]:
s = time.perf_counter()
for _ in range(M):
func(987654321)
e = time.perf_counter()
print(f'{func.__name__} -> {e-s:.4f}s')
Output:
binaryx -> 1.3817s
binaryo -> 0.9861s
binaryy -> 1.6052s
One way, using divmod to divide by 2 and get the remainder in one step:
def binary(num):
b = ""
while num:
num, digit = divmod(num, 2)
b = f"{digit}{b}"
return b
binary(26)
'11010'
This assumes a positive number but can easily be extended to work for 0 and negatives.
I've seen a couple posts explaning how to iterate through the digits of a number in Python, but they all turn the number into a string before iterating through it...
For example:
n=578
print d for d in str(n)
How can I do this without the conversion into a string?
10**int(log(n, 10)) is basically 10*, such that it is the same length as n. The floor division of n by that will give us the leading digit, while the modulo % gives us the rest of the number.
from math import log
def digits(n):
if n < 0:
yield '-'
n = -1 * n
elif n == 0:
yield 0
return
xp = int(log(n, 10).real)
factor = 10**xp
while n:
yield int(n/factor)
n = n % factor
try:
xp, old_xp = int(log(n, 10).real), xp
except ValueError:
for _ in range(xp):
yield 0
return
factor = 10**xp
for _ in range(1, old_xp-xp):
yield 0
for x in digits(12345):
print(x)
prints
1
2
3
4
5
Edit: I switched to this version, which is much less readable, but more robust. This version correctly handles negative and zero values, as well as trailing and internal 0 digits.
Outputting/finding first n numbers of Natural Binary Code:
import math
def binary_print(n):
m = int(math.ceil(math.log(n, 2)))
for i in range(n):
b = str(bin(i)[2:])
print((m - len(b)) * '0' + b)
My question is:
Do you know any other way to do this in Python? Maybe faster? Or shorter (less code)?
Well this is shorter, not sure about faster:
def binary_print(n):
print '\n'.join('{:0{}b}'.format(x, (n-1).bit_length()) for x in range(n))
Example usage:
>>> binary_print(6)
000
001
010
011
100
101
def binary_values(n):
fmt = "{0:0"+str((n-1).bit_length())+"b}"
for i in range(n):
print fmt.format(i)
Note: (n-1).bit_length() fixes a fencepost error (otherwise if n is a power of two it prints 1 too many leading zeros).
Might also be able to speed it up a bit more by unrolling a lookup,
def binary_values(n):
fmt = ("{0:0"+str((n-1).bit_length())+"b}").format
for i in range(n):
print fmt(i)
Suppose you take the strings 'a' and 'z' and list all the strings that come between them in alphabetical order: ['a','b','c' ... 'x','y','z']. Take the midpoint of this list and you find 'm'. So this is kind of like taking an average of those two strings.
You could extend it to strings with more than one character, for example the midpoint between 'aa' and 'zz' would be found in the middle of the list ['aa', 'ab', 'ac' ... 'zx', 'zy', 'zz'].
Might there be a Python method somewhere that does this? If not, even knowing the name of the algorithm would help.
I began making my own routine that simply goes through both strings and finds midpoint of the first differing letter, which seemed to work great in that 'aa' and 'az' midpoint was 'am', but then it fails on 'cat', 'doggie' midpoint which it thinks is 'c'. I tried Googling for "binary search string midpoint" etc. but without knowing the name of what I am trying to do here I had little luck.
I added my own solution as an answer
If you define an alphabet of characters, you can just convert to base 10, do an average, and convert back to base-N where N is the size of the alphabet.
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def enbase(x):
n = len(alphabet)
if x < n:
return alphabet[x]
return enbase(x/n) + alphabet[x%n]
def debase(x):
n = len(alphabet)
result = 0
for i, c in enumerate(reversed(x)):
result += alphabet.index(c) * (n**i)
return result
def average(a, b):
a = debase(a)
b = debase(b)
return enbase((a + b) / 2)
print average('a', 'z') #m
print average('aa', 'zz') #mz
print average('cat', 'doggie') #budeel
print average('google', 'microsoft') #gebmbqkil
print average('microsoft', 'google') #gebmbqkil
Edit: Based on comments and other answers, you might want to handle strings of different lengths by appending the first letter of the alphabet to the shorter word until they're the same length. This will result in the "average" falling between the two inputs in a lexicographical sort. Code changes and new outputs below.
def pad(x, n):
p = alphabet[0] * (n - len(x))
return '%s%s' % (x, p)
def average(a, b):
n = max(len(a), len(b))
a = debase(pad(a, n))
b = debase(pad(b, n))
return enbase((a + b) / 2)
print average('a', 'z') #m
print average('aa', 'zz') #mz
print average('aa', 'az') #m (equivalent to ma)
print average('cat', 'doggie') #cumqec
print average('google', 'microsoft') #jlilzyhcw
print average('microsoft', 'google') #jlilzyhcw
If you mean the alphabetically, simply use FogleBird's algorithm but reverse the parameters and the result!
>>> print average('cat'[::-1], 'doggie'[::-1])[::-1]
cumdec
or rewriting average like so
>>> def average(a, b):
... a = debase(a[::-1])
... b = debase(b[::-1])
... return enbase((a + b) / 2)[::-1]
...
>>> print average('cat', 'doggie')
cumdec
>>> print average('google', 'microsoft')
jlvymlupj
>>> print average('microsoft', 'google')
jlvymlupj
It sounds like what you want, is to treat alphabetical characters as a base-26 value between 0 and 1. When you have strings of different length (an example in base 10), say 305 and 4202, your coming out with a midpoint of 3, since you're looking at the characters one at a time. Instead, treat them as a floating point mantissa: 0.305 and 0.4202. From that, it's easy to come up with a midpoint of .3626 (you can round if you'd like).
Do the same with base 26 (a=0...z=25, ba=26, bb=27, etc.) to do the calculations for letters:
cat becomes 'a.cat' and doggie becomes 'a.doggie', doing the math gives cat a decimal value of 0.078004096, doggie a value of 0.136390697, with an average of 0.107197397 which in base 26 is roughly "cumcqo"
Based on your proposed usage, consistent hashing ( http://en.wikipedia.org/wiki/Consistent_hashing ) seems to make more sense.
Thanks for everyone who answered, but I ended up writing my own solution because the others weren't exactly what I needed. I am trying to average app engine key names, and after studying them a bit more I discovered they actually allow any 7-bit ASCII characters in the names. Additionally I couldn't really rely on the solutions that converted the key names first to floating point, because I suspected floating point accuracy just isn't enough.
To take an average, first you add two numbers together and then divide by two. These are both such simple operations that I decided to just make functions to add and divide base 128 numbers represented as lists. This solution hasn't been used in my system yet so I might still find some bugs in it. Also it could probably be a lot shorter, but this is just something I needed to get done instead of trying to make it perfect.
# Given two lists representing a number with one digit left to decimal point and the
# rest after it, for example 1.555 = [1,5,5,5] and 0.235 = [0,2,3,5], returns a similar
# list representing those two numbers added together.
#
def ladd(a, b, base=128):
i = max(len(a), len(b))
lsum = [0] * i
while i > 1:
i -= 1
av = bv = 0
if i < len(a): av = a[i]
if i < len(b): bv = b[i]
lsum[i] += av + bv
if lsum[i] >= base:
lsum[i] -= base
lsum[i-1] += 1
return lsum
# Given a list of digits after the decimal point, returns a new list of digits
# representing that number divided by two.
#
def ldiv2(vals, base=128):
vs = vals[:]
vs.append(0)
i = len(vs)
while i > 0:
i -= 1
if (vs[i] % 2) == 1:
vs[i] -= 1
vs[i+1] += base / 2
vs[i] = vs[i] / 2
if vs[-1] == 0: vs = vs[0:-1]
return vs
# Given two app engine key names, returns the key name that comes between them.
#
def average(a_kn, b_kn):
m = lambda x:ord(x)
a = [0] + map(m, a_kn)
b = [0] + map(m, b_kn)
avg = ldiv2(ladd(a, b))
return "".join(map(lambda x:chr(x), avg[1:]))
print average('a', 'z') # m#
print average('aa', 'zz') # n-#
print average('aa', 'az') # am#
print average('cat', 'doggie') # d(mstr#
print average('google', 'microsoft') # jlim.,7s:
print average('microsoft', 'google') # jlim.,7s:
import math
def avg(str1,str2):
y = ''
s = 'abcdefghijklmnopqrstuvwxyz'
for i in range(len(str1)):
x = s.index(str2[i])+s.index(str1[i])
x = math.floor(x/2)
y += s[x]
return y
print(avg('z','a')) # m
print(avg('aa','az')) # am
print(avg('cat','dog')) # chm
Still working on strings with different lengths... any ideas?
This version thinks 'abc' is a fraction like 0.abc. In this approach space is zero and a valid input/output.
MAX_ITER = 10
letters = " abcdefghijklmnopqrstuvwxyz"
def to_double(name):
d = 0
for i, ch in enumerate(name):
idx = letters.index(ch)
d += idx * len(letters) ** (-i - 1)
return d
def from_double(d):
name = ""
for i in range(MAX_ITER):
d *= len(letters)
name += letters[int(d)]
d -= int(d)
return name
def avg(w1, w2):
w1 = to_double(w1)
w2 = to_double(w2)
return from_double((w1 + w2) * 0.5)
print avg('a', 'a') # 'a'
print avg('a', 'aa') # 'a mmmmmmmm'
print avg('aa', 'aa') # 'a zzzzzzzz'
print avg('car', 'duck') # 'cxxemmmmmm'
Unfortunately, the naïve algorithm is not able to detect the periodic 'z's, this would be something like 0.99999 in decimal; therefore 'a zzzzzzzz' is actually 'aa' (the space before the 'z' periodicity must be increased by one.
In order to normalise this, you can use the following function
def remove_z_period(name):
if len(name) != MAX_ITER:
return name
if name[-1] != 'z':
return name
n = ""
overflow = True
for ch in reversed(name):
if overflow:
if ch == 'z':
ch = ' '
else:
ch=letters[(letters.index(ch)+1)]
overflow = False
n = ch + n
return n
print remove_z_period('a zzzzzzzz') # 'aa'
I haven't programmed in python in a while and this seemed interesting enough to try.
Bear with my recursive programming. Too many functional languages look like python.
def stravg_half(a, ln):
# If you have a problem it will probably be in here.
# The floor of the character's value is 0, but you may want something different
f = 0
#f = ord('a')
L = ln - 1
if 0 == L:
return ''
A = ord(a[0])
return chr(A/2) + stravg_half( a[1:], L)
def stravg_helper(a, b, ln, x):
L = ln - 1
A = ord(a[0])
B = ord(b[0])
D = (A + B)/2
if 0 == L:
if 0 == x:
return chr(D)
# NOTE: The caller of helper makes sure that len(a)>=len(b)
return chr(D) + stravg_half(a[1:], x)
return chr(D) + stravg_helper(a[1:], b[1:], L, x)
def stravg(a, b):
la = len(a)
lb = len(b)
if 0 == la:
if 0 == lb:
return a # which is empty
return stravg_half(b, lb)
if 0 == lb:
return stravg_half(a, la)
x = la - lb
if x > 0:
return stravg_helper(a, b, lb, x)
return stravg_helper(b, a, la, -x) # Note the order of the args