4

I'm working in Python and have a list of hourly values for a day. For simplicity let's say there are only 10 hours in a day.

 [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0]

I want to stretch this around the centre-point to 150% to end up with:

 [0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0]

Note this is just an example and I will also need to stretch things by amounts that leave fractional amounts in a given hour. For example stretching to 125% would give:

 [0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 0.0]

My first thought for handling the fractional amounts is to multiply the list up by a factor of 10 using np.repeat, apply some method for stretching out the values around the midpoint, then finally split the list into chunks of 10 and take the mean for each hour.

My main issue is the "stretching" part but if the answer also solves the second part so much the better.

Tim
  • 41,901
  • 18
  • 127
  • 145
Jamie Bull
  • 12,889
  • 15
  • 77
  • 116
  • is the array always only filled with 1.0 and 0.0 in the initial state? –  Oct 09 '15 at 10:05
  • does your list always look like this: bunch of zeroes, some number, bunch of zeroes? is max value of an element 1.0? are the numbers always symmetric around the middle? – sve Oct 09 '15 at 10:05
  • No they can be zero or any (positive) value, and not necessarily around the middle but around some fairly central point (in reality 1pm as the middle of the working day but I'd like to be able to vary this). – Jamie Bull Oct 09 '15 at 10:08
  • so, what percentage you should stretch to, if you want to expand one element to whole list? please, explain your algorithm better – pyanzin Oct 09 '15 at 10:09
  • In this case it would be 1000% (1 value expanded to cover all 10 places) – Jamie Bull Oct 09 '15 at 10:16
  • what if I want to stretch `[1, 1, 1]` around the center with `50`% what do I get? – sve Oct 09 '15 at 10:19
  • `[1, 1, 1]` the values should be unchanged. What I'm trying to do is represent extending working hours by a factor so it's only the values in the middle that matter. – Jamie Bull Oct 09 '15 at 10:22
  • 1
    you can do workarounds, but from context of question a package like pandas might be your best bet. You can define the measurements with time and then use its functionality to fill and query different ranges. – Joop Oct 09 '15 at 10:22
  • I'm using `pandas` elsewhere in this program but not sure what function I should be using for this problem. – Jamie Bull Oct 09 '15 at 10:24
  • @JamieBull `is max value of an element 1.0` – sve Oct 09 '15 at 10:30
  • No they can be zero or any (positive) value. – Jamie Bull Oct 09 '15 at 10:31
  • with pandas store data with their actual timestamps. Reindex data to say minute intervals. Use appropriate (for your data) filling technique to fill data. then you can zoom to point that you want to view. Dont know exactly why you need only 10 points? I assume it is to view the data – Joop Oct 09 '15 at 10:44
  • No, 10 was just an arbitrary number. It's the "appropriate... filling technique to fill data" that I'm asking about. – Jamie Bull Oct 09 '15 at 10:46

3 Answers3

1

I guess, you need something like that:

def stretch(xs, coef):
  # compute new distibution
  oldDist = sum(hours[:len(hours)/2])
  newDist = oldDist * coef

  # generate new list
  def f(x):
    if newDist - x < 0:
      return 0.0
    return min(1.0, newDist - x)

  t = [f(x) for x in range(len(xs)/2)]
  res = list(reversed(t))
  res.extend(t)
  return res

But be careful with odd count of hours.

pyanzin
  • 120
  • 3
  • 10
1

If I look at the expected output, the algorithm goes something like this:

  • start with a list of numbers, values >0.0 indicate working hours
  • sum those hours
  • compute how many extra hours are requested
  • divide those extra hours over both ends of the sequence by prepending or appending half of this at each 'end'

So:

hours     = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0]
expansion = 130
extra_hrs = float(sum(hours)) * float(expansion - 100)/100

# find indices of the first and last non-zero hours
# because of floating point can't use "==" for comparison.
hr_idx    = [idx for (idx, value) in enumerate(hours) if value>0.001]

# replace the entries before the first and after the last 
# with half the extra hours
print "Before expansion:",hours
hours[ hr_idx[0]-1 ] = hours[ hr_idx[-1]+1 ] = extra_hrs/2.0
print "After  expansion:",hours

Gives as output:

Before expansion: [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0]
After  expansion: [0.0, 0.0, 0.6, 1.0, 1.0, 1.0, 1.0, 0.6, 0.0, 0.0]
emvee
  • 4,371
  • 23
  • 23
0

This is what I've ended up doing. It's a little ugly as it needs to handle stretch coefficients less than 100%.

def stretch(xs, coef, centre):
    """Scale a list by a coefficient around a point in the list.

    Parameters
    ----------
    xs : list
        Input values.
    coef : float
        Coefficient to scale by.
    centre : int
        Position in the list to use as a centre point.

    Returns
    -------
    list

    """
    grain = 100    
    stretched_array = np.repeat(xs, grain * coef)

    if coef < 1:
        # pad start and end
        total_pad_len = grain * len(xs) - len(stretched_array)
        centre_pos = float(centre) / len(xs)
        start_pad_len = centre_pos * total_pad_len
        end_pad_len = (1 - centre_pos) * total_pad_len
        start_pad = [stretched_array[0]] * int(start_pad_len)
        end_pad = [stretched_array[-1]] * int(end_pad_len)
        stretched_array = np.array(start_pad + list(stretched_array) + end_pad)
    else:
        pivot_point = (len(xs) - centre) * grain * coef
        first = int(pivot_point - (len(xs) * grain)/2)
        last = first + len(xs) * grain
        stretched_array = stretched_array[first:last]

    return [round(chunk.mean(), 2) for chunk in chunks(stretched_array, grain)]


def chunks(iterable, n):
    """
    Yield successive n-sized chunks from iterable.
    Source: http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python#answer-312464

    """
    for i in xrange(0, len(iterable), n):
        yield iterable[i:i + n]
Jamie Bull
  • 12,889
  • 15
  • 77
  • 116