0

What has been done already:

  1. Each letter of a string comprised of fixed number of letters converted to a number: e.g. "TAAPAS" is now "122324"

    I was forced to perform this using the clunky code:

    s = s1.replace("A", "2").replace("P", "3").replace("T", "1").replace("S", "4") 
    

because when I tried a more standard approach suggestion on an earlier forum, the code failed to modify every letter:

numerize = {'A':'2', 'P':'3', 'T':'1', 'S':'4'}
for k, v in numerize.iteritems():
    string2 = string1.replace(k, v)

Q1: Any suggestions on why this code isn't working in my situation? I even tried the following, with the same result:

numerize = {'A':'2', 'P':'3', 'T':'1', 'S':'4'}
for k, v in numerize.iteritems():
    for i in string1: 
         string2 = string1.replace(k, v)

Anyhow, on to what I want to do next:

  1. On my sequence of numbers "122324", I want to perform a defined series of numerical operations, and when the last operation is reached, I want it to start over at the beginning...repeating this as many times as necessary to reach the end of my integer.

    e.g. +, *, /

    such that it looks something like:

     y = 1 + 2 * 2 / 3 + 2 * 4 ...
    

Q2: Must this be performed on a list of numbers? or is it possible to iterate through the positions of an integer as one can the positions of a string. If not, it's not a big deal to just split my string prior to converting it.

Q3. What is the code to accomplish such a task? And is there a name for this type of procedure? Using the keywords "recycle" and "math functions" leaves me dry...and it seems that the itertools library doesn't do math (or so there are no examples on the webpage).

Thank you!

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • How are you checking your code output? – venpa Oct 20 '14 at 05:24
  • 2
    Please ask a single question at a time. – Tim Pietzcker Oct 20 '14 at 05:24
  • 1
    Try to change `string2 = string1.replace(...)` with `string1 = string1.replace(...)`. `replace` does not modify the original string so in every iteration you are performing only one replacement, throwing away all previous changes. – Bakuriu Oct 20 '14 at 05:27
  • When you, user4109233, write `y = 1 + 2 * 2 / 3 + 2 * 4` you mean that you want exactly that, respecting operator precedences and giving 31/3, or rather do you want `y = ((((1+2)*2)/3)+2)*4` giving 16? – gboffi Oct 21 '14 at 06:26

5 Answers5

2

It's good idea to use translate for replacing character mapping as below:

from string import maketrans
instr = "APTS"
outstr = "2314"
trans = maketrans(instr, outstr)
print "TAPS".translate(trans)

output:

1234

Once you have the output like this, you can do numerical operations like this:

from string import maketrans
instr = "APTS"
outstr = "2314"
trans = maketrans(instr, outstr)
strNumber = "TAPS".translate(trans)
result=0
for n in strNumber:
    result = result + int(n) #summing all the digits

print result

output:

10
venpa
  • 4,268
  • 21
  • 23
1

user3 showed you how to do a proper character translation. One of your questions was why your code didn't work. Just take a look at what's happening:

>>> string1 = "TAAPAS"
>>> for k, v in numerize.iteritems():
...     string2 = string1.replace(k, v)
...     print string2
...
TAAPA4
TAA3AS
T22P2S
1AAPAS

In each iteration, you restart with the same string, so only the last replace operation is preserved (strings are immutable, so .replace() always returns a new string instead of changing the one it's working on). If you had used string1 = string1.replace(k, v), then the result would have been correct.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
1
>>> eval_expr(interlace(letters2numbers("TAAPAS"), cycle("+*/")))
10.333333333333332

Q1: Any suggestions on why this code isn't working in my situation?

Strings are immutable in Python. string1.replace() doesn't modify string1, and string2 is overwritten on each iteration.

Q2: Must this be performed on a list of numbers?

No. You could create the arithmetic expression in a string form without converting to ints, floats first.

Q3. What is the code to accomplish such a task? And is there a name for this type of procedure?

  • "TAAPAS" to "122324" step is translation/encoding. The translation step you could implement using .translate() method as @user3 suggested:

    import string
    
    table = string.maketrans("TAPS", "1234")
    
    def letters2numbers(letters):
        """
        >>> letters2numbers("TAAPAS")
        '122324'
        """
        return letters.translate(table)
    
  • "122324" + cycle("+*/") to "1 + 2 * 2 / 3 + 2 * 4" is interlacing:

    def interlace(left, right):
        """
        >>> from itertools import cycle
        >>> interlace("122324", cycle("+*/"))
        '1+ 2* 2/ 3+ 2* 4'
        """
        return " ".join(a+b for a, b in zip(left, right))[:-1]
    

    itertools.cycle("+*/") repeats the sequence infinitely many times until zip() stop. It adds unnecessary operator at the end that is chopped off using [:-1].

  • "1 + 2 * 2 / 3 + 2 * 4" to 10.(3) is arithmetic evaluation:

    >>> eval_expr('1+ 2* 2/ 3+ 2* 4')
    10.333333333333332
    

    where eval_expr() is a safe eval() analog for simple mathematical expressions.

Or you could combine all steps into a single function:

from itertools import cycle
from operator import add, sub, mul, floordiv

def eval_letters(letters, operators,
                 numerize={'A': 2, 'P': 3, 'T': 1, 'S': 4},
                 functions={'+': add, '-': sub, '*': mul, '/': floordiv}):
    """
    >>> eval_letters("TAAPAS", "+*/")
    16
    """
    numbers = (numerize[c] for c in letters)
    operators = cycle(operators) # repeat ad infinitum
    result = next(numbers)
    for n, op in zip(numbers, operators):
        result = functions[op](result, n)
    return result

Note: here I've arbitrarily chosen floordiv (3/2==1) for / operator instead of truediv (3/2==1.5) used by eval_expr().

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
0

You can use numerize in a more straightforward fashion

numerize = {'A':'2', 'P':'3', 'T':'1', 'S':'4'}
string2 = ""
for c in "TAAPAS":
    string2 += numerize[c]
print string2
user3556757
  • 3,469
  • 4
  • 30
  • 70
0

All the given answers (@6:30 GMT) work for just one string.

If you have to encode many strings with the provision that, for each string, you assign the code 1 to the first letter in the string, 2 to the second different letter in the input string etc you can give a try to the following function.

def numerize(s):
    nd = 0
    copy = s[::]
    for i  in range(len(s)):
        c = copy[i]
        if c.isdigit(): continue
        nd = nd + 1
        if nd > 9 :
            print "Too many different characters in \"%s\"." % s
            return None # here you may want to throw
        copy = copy.replace(c,repr(nd))
    return copy

print numerize('asdeaswes')

A tacit assumption, that I want to speak out, is that the input string doesn't contain any digit. You may want to check each character before processing if you need, e.g., that the only legal characters are latin capital letters A to Z.

ps: if you need a list of integers in place of a string of digits,

 s/return copy/return [int(d) for d in copy]/

pps: concerning the numerical post-processing, imo it's way better to follow J.F. Sebastian's advice

eval_expr(interlace(numerize("TAAPAS"), cycle("+*/")))

rather than reinventing wheel on your own...

Community
  • 1
  • 1
gboffi
  • 22,939
  • 8
  • 54
  • 85
  • Maybe my own answer is a bit too generic if the OP is in bioinformatics and the letters T, A, P, S stand for bases or whatever... – gboffi Oct 20 '14 at 06:55