3
\$\begingroup\$

I have a list of \$n\$ words, and a corresponding \$m \space x \space n\$ frequency matrix (as a NumPy array). I would like to return a list/array of strings of length \$m\$ where the \$m\$th string is comprised of each word repeated according to the frequencies in the \$m\$th row of the frequency matrix. I have managed to achieve the desired result (help from here), but the code is not particularly easy to understand at a glance. Is there a cleaner and more efficient way to perform the following operation?

import numpy as np
x = ['yugoslavia', 'zealand', 'zimbabwe', 'zip', 'zone']
y = np.array([[2,1,0,0,5], [0,0,1,3,0]])
z = np.apply_along_axis(lambda b: ' '.join([ item for sublist in [[x[i]]*b[i] for i in range(len(x))] for item in sublist]),1,y)

>>> z
array(['yugoslavia yugoslavia zealand zone zone zone zone zone',
   'zimbabwe zip zip zip'],
  dtype='<U54')

I am looking for solutions compatible with Python 3.5.

\$\endgroup\$
1
  • \$\begingroup\$ Look at np.repeat \$\endgroup\$
    – hpaulj
    Commented Aug 7, 2016 at 21:53

2 Answers 2

2
\$\begingroup\$

It would seem you're doing it the right way. One thing though: you might want to replace the following piece of code:

[[x[i]]*b[i] for i in range(len(x))]

A few points as to how you could improve this:

  • I suggest you use zip to iterate over two arrays simultaneously.
  • Also, prefer using () over [], since it creates a generator expression, rather than a list.
  • A similar argument holds with the construct join([ ... ]). Simply use join( ... ) instead, which would avoid creating the list in memory.
  • Better variable names will also help with clarity.
([s] * count for s, count in zip(strings, counts))

Finally, formatting can make loads of difference:

import numpy as np

strings = ['yugoslavia', 'zealand', 'zimbabwe', 'zip', 'zone']
counts_array = np.array([[2,1,0,0,5], [0,0,1,3,0]])
result = np.apply_along_axis(
    lambda counts: ' '.join(item for sublist in
                                ([s] * count for s, count in zip(strings, counts))
                            for item in sublist),
    1, counts_array)

An equally ugly alternative might involve using two join statements:

result = np.apply_along_axis(
    lambda counts: ' '.join(filter(None,
                  (' '.join([s] * count) for (s, count) in zip(strings, counts)))),
    1, counts_array)

Note how I've had to use filter, as per this question, in order to remove the extra spaces emanating from the empty strings.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much for taking the time to go through this. I really appreciate your detailed explanations. \$\endgroup\$
    – blep
    Commented Aug 1, 2016 at 0:15
0
\$\begingroup\$
np.array([' '.join( np.repeat(x, z)) for z in y ])

repeat handles the repetition part for row of y nicely. The rest is just iteration on the rows. We don't need the generality of apply_along_axis here.

The repeat could be applied to all of y without loop with

n=y.shape[0]
X = np.repeat([x]*n, y.flat).reshape(n, -1)

But the join still has to be done iteratively.

[' '.join(I) for I in X]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.