0

I am writing a function that takes a list variable and returns vertical bars that correspond to each number in the list. I have just a simple problem, really. Perhaps, I am missing something here.

def vBarMaker(nums): # Helper Function
    output = "" 
    while nums != 0: 
        nums -= 1 
        output += "*"
        if nums == 0:
            return output

def vBarGraphify(nums):
for num in nums:
    print vBarMaker(num)

print vBarGraphify( [0,1,3,2] )
# ^ This returns 
#
# *
# ***
# **

# But I want it to return:

#     * 
#     * *
#   * * *

Can someone please help me edit the function so that it returns ^. Thanks in advance.

Tahmoor Cyan
  • 129
  • 1
  • 4
  • 15
  • You've asked for vertical bars, but all the answers you got were horizontal. Your sample output was ambiguous. Can you clarify if you want the bars to be tall and side by side or wide and stacked? (Horizontal and vertical can be confusing to some) – mhlester Apr 24 '14 at 00:44
  • The expected output should be a row. – Tahmoor Cyan Apr 24 '14 at 00:47
  • 1
    Would you mind [edit]ing your question with expected output for `[0, 1, 3, 2]` instead of `[0, 1, 2, 3]`? – mhlester Apr 24 '14 at 00:49
  • 1
    It looks like everyone thought you were just trying to right-align the bars – mhlester Apr 24 '14 at 00:50
  • Yeah we thought he wanted to right-align because it was mis-formatted the first time around making it seem that way. – Kyle Falconer Apr 24 '14 at 01:04

4 Answers4

4
def vBarGraphify(nums):
   for num in nums:
      print " {:>10}".format("*" * num)

EDIT:

def vBarGraphify( cols):
   for row in range( max( cols ),0,-1 ) :
      for col in cols: 
          print " " if col < row else "*",
      print

EVEN MORE PYTHONIC:

def vBarGraphify( nums ) : 
   # create 2d array of horizontal graph 10 X 4
   original = [ [n < num for n in range(max(nums)) ] for num in nums ]   
   # rotate it 4 X 10
   rotated = zip(*original)
   # print each line.
   for line in rotated[::-1]: 
      print "".join( "*" if col else " " for col in line ) 

From Here!

Community
  • 1
  • 1
corn3lius
  • 4,857
  • 2
  • 31
  • 36
4

If you want the bars to be vertical, then every print line needs to take into account all the values, and you need as many lines as the maximum value:

def vBarGraphify(nums):
    for row in vBarMaker(nums):
        print row


def vBarMaker(nums):
    outputs = []
    for value in xrange(max(nums), 0, -1): # start with max, loop to zero
        row = ''
        for num in nums:
            if num >= value:               # is inside the bar
                row += '*'
            else:
                row += ' '
        outputs.append(row)
    return outputs

vBarGraphify([0, 1, 2, 3, 10])

Outputs:

    *
    *
    *
    *
    *
    *
    *
   **
  ***
 ****

I added a 10 to demonstrate the bars are actually vertical, unlike all the other answers...

mhlester
  • 22,781
  • 10
  • 52
  • 75
3

Currently you have a single loop which outputs the corresponding asterisk until the number is reached. You also need to take into account the whitespace. What I would do is output a space for the number opposite the current index number of the asterisk being printed out.

This is to say that if the list passed in to the function is [1,2,3] then on the first iteration, two spaces and a * should be printed: ' *' and on the next iteration, one space and two * should be printed: ' **' on the third iteration, three * should be printed: '***'

Edit:

I went ahead and coded my implementation even though corn3lius' answer was already fantastic. I included his here and also the doc tests so you can see how it works.

The main difference between my answer and corn3lius' is that the left-hand white space is hard-coded in corn3lius' answer and mine is generated based on the maximum number inside the given list.

def BarGraphify1(nums):
    '''
    see http://stackoverflow.com/a/23257715/940217
    >>> BarGraphify1([1,2,3])
              *
             **
            ***
    '''
    for num in nums:
        print " {:>10}".format("*" * num)


def BarGraphify2(nums):
    '''
    >>> BarGraphify2([1,2,3])
      *
     **
    ***
    >>> BarGraphify2([1,3,2])
      *
    ***
     **
    '''
    output = []
    maxVal = max(nums)
    for n in nums:
        space = (maxVal-n)*' '
        asterisks = n*'*'
        output.append(space + asterisks)
    print '\n'.join(output)



if __name__ == '__main__':
    import doctest
    doctest.testmod()

Edit #2:

Now that I see that the OP wanted columns as shown in the edit, I've reworked my solution to use the numpy.transpose function. Much of it remains the same, but now I treat the whole thing as rows and columns, then transpose the 2-D array to get the desired columns.

import numpy
def BarGraphify3(nums):
    '''
    >>> BarGraphify3([1,2,3])
      *
     **
    ***
    >>> BarGraphify3([1,3,2])
     * 
     **
    ***
    '''
    grid = []
    maxVal = max(nums)
    for n in nums:
        space = (maxVal-n)*' '
        asterisks = n*'*'
        grid.append(list(space + asterisks))


    cols = []
    for row in numpy.transpose(grid):
        cols.append(''.join(row))
    print '\n'.join(cols)
Kyle Falconer
  • 8,302
  • 6
  • 48
  • 68
  • What's the output for `[1, 3, 2]`? Check out the edit – mhlester Apr 24 '14 at 00:56
  • @mhlester I added a doc test for `[1,3,2]` and the output was as I expected it to be, but now I see the edit from the OP and notice the difference. I'll see what I can do – Kyle Falconer Apr 24 '14 at 00:59
0

You should take a look at str.format. It will help you to see how to align your text on the right

Cedric Morent
  • 1,719
  • 1
  • 14
  • 25