Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added print_bin() method #32

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 11 additions & 9 deletions binpacking/binpacking_binary.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from __future__ import print_function

import sys
from optparse import OptionParser

from binpacking.to_constant_bin_number import csv_to_constant_bin_number
from binpacking.to_constant_volume import csv_to_constant_volume

from optparse import OptionParser
import sys


def main():

parser = OptionParser()
parser.add_option("-f", "--filepath", dest="filepath", default=None,
help="path to the csv-file to be bin-packed"
Expand Down Expand Up @@ -37,6 +36,9 @@ def main():
parser.add_option("-u", "--upper-bound", dest="upper_bound", type="float", default=None,
help="weights exceeding this bound will not be considered"
)
parser.add_option("-p", "--print-bins", action="store_true", dest="print_bins", default=False,
help="print sorted bins out to console"
)

(options, args) = parser.parse_args()
opt = vars(options)
Expand All @@ -45,24 +47,24 @@ def main():
raise Exception("No weight column identifier given")
sys.exit(1)
else:
#if weight column is given try to convert it to a number
# if weight column is given try to convert it to a number
try:
opt["weight_column"] = int(opt["weight_column"])
opt["weight_column"] = int(opt["weight_column"])
except:
pass

if opt["delim"] == "tab" or opt["delim"] == '"tab"':
opt["delim"] = '\t'

if opt["V_max"] is None and opt["N_bin"] is None:
print("Neither V_max nor N_bin are given. No algorithm can be used.")
sys.exit(1)
elif opt["V_max"] is not None and opt["N_bin"] is not None:
print("Both V_max and N_bin are given. It's unclear which algorithm is to be used.")
sys.exit(1)
elif opt["V_max"] is not None and opt["N_bin"] is None:
opt.pop("N_bin",None)
opt.pop("N_bin", None)
csv_to_constant_volume(**opt)
elif opt["V_max"] is None and opt["N_bin"] is not None:
opt.pop("V_max",None)
opt.pop("V_max", None)
csv_to_constant_bin_number(**opt)
114 changes: 61 additions & 53 deletions binpacking/to_constant_bin_number.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from __future__ import print_function

from builtins import range

from binpacking.utilities import (
load_csv,
save_csvs,
print_binsizes,
get,
argmin,
revargsort,
)
load_csv,
save_csvs,
print_binsizes,
print_bin,
get,
argmin,
revargsort,
)


def csv_to_constant_bin_number(filepath,
weight_column,
Expand All @@ -18,7 +21,8 @@ def csv_to_constant_bin_number(filepath,
quotechar='"',
lower_bound=None,
upper_bound=None,
):
print_bins=False
):
"""
Load a csv file, binpack the rows according to one of the columns
to a constant number of bins.
Expand All @@ -39,8 +43,12 @@ def csv_to_constant_bin_number(filepath,
lower_bound=lower_bound,
upper_bound=upper_bound,
)

print_binsizes(bins, weight_column)

if print_bins:
print_bin(bins)

save_csvs(bins,
filepath,
header,
Expand All @@ -55,7 +63,7 @@ def to_constant_bin_number(d,
key=None,
lower_bound=None,
upper_bound=None,
):
):
"""
Distributes a list of weights, a dictionary of weights or a list of tuples containing weights
to a fixed number of bins while trying to keep the weight distribution constant.
Expand Down Expand Up @@ -85,9 +93,9 @@ def to_constant_bin_number(d,
a dict of items, depending on the type of ``d``.
"""

isdict = isinstance(d,dict)
isdict = isinstance(d, dict)

if not hasattr(d,'__len__'):
if not hasattr(d, '__len__'):
raise TypeError("d must be iterable")

if not isdict and hasattr(d[0], '__len__'):
Expand All @@ -106,32 +114,32 @@ def to_constant_bin_number(d,

if isdict:

#get keys and values (weights)
# get keys and values (weights)
keys_vals = d.items()
keys = [ k for k, v in keys_vals ]
vals = [ v for k, v in keys_vals ]
keys = [k for k, v in keys_vals]
vals = [v for k, v in keys_vals]

#sort weights decreasingly
# sort weights decreasingly
ndcs = revargsort(vals)

weights = get(vals, ndcs)
keys = get(keys, ndcs)

bins = [ {} for i in range(N_bin) ]
bins = [{} for i in range(N_bin)]
else:
weights = sorted(d,key=lambda x: -x)
bins = [ [] for i in range(N_bin) ]
weights = sorted(d, key=lambda x: -x)
bins = [[] for i in range(N_bin)]

#find the valid indices
if lower_bound is not None and upper_bound is not None and lower_bound<upper_bound:
valid_ndcs = filter(lambda i: lower_bound < weights[i] < upper_bound,range(len(weights)))
# find the valid indices
if lower_bound is not None and upper_bound is not None and lower_bound < upper_bound:
valid_ndcs = filter(lambda i: lower_bound < weights[i] < upper_bound, range(len(weights)))
elif lower_bound is not None:
valid_ndcs = filter(lambda i: lower_bound < weights[i],range(len(weights)))
valid_ndcs = filter(lambda i: lower_bound < weights[i], range(len(weights)))
elif upper_bound is not None:
valid_ndcs = filter(lambda i: weights[i] < upper_bound,range(len(weights)))
valid_ndcs = filter(lambda i: weights[i] < upper_bound, range(len(weights)))
elif lower_bound is None and upper_bound is None:
valid_ndcs = range(len(weights))
elif lower_bound>=upper_bound:
elif lower_bound >= upper_bound:
raise Exception("lower_bound is greater or equal to upper_bound")

valid_ndcs = list(valid_ndcs)
Expand All @@ -141,48 +149,48 @@ def to_constant_bin_number(d,
if isdict:
keys = get(keys, valid_ndcs)

#the total volume is the sum of all weights
# the total volume is the sum of all weights
V_total = sum(weights)

#the first estimate of the maximum bin volume is
#the total volume divided to all bins
# the first estimate of the maximum bin volume is
# the total volume divided to all bins
V_bin_max = V_total / float(N_bin)

#prepare array containing the current weight of the bins
weight_sum = [0. for n in range(N_bin) ]
# prepare array containing the current weight of the bins
weight_sum = [0. for n in range(N_bin)]

#iterate through the weight list, starting with heaviest
# iterate through the weight list, starting with heaviest
for item, weight in enumerate(weights):

if isdict:
key = keys[item]

#put next value in bin with lowest weight sum
# put next value in bin with lowest weight sum
b = argmin(weight_sum)

#calculate new weight of this bin
# calculate new weight of this bin
new_weight_sum = weight_sum[b] + weight

found_bin = False
while not found_bin:

#if this weight fits in the bin
# if this weight fits in the bin
if new_weight_sum <= V_bin_max:

#...put it in
# ...put it in
if isdict:
bins[b][key] = weight
else:
bins[b].append(weight)

#increase weight sum of the bin and continue with
#next item
# increase weight sum of the bin and continue with
# next item
weight_sum[b] = new_weight_sum
found_bin = True

else:
#if not, increase the max volume by the sum of
#the rest of the bins per bin
# if not, increase the max volume by the sum of
# the rest of the bins per bin
V_bin_max += sum(weights[item:]) / float(N_bin)

if not is_tuple_list:
Expand All @@ -195,33 +203,33 @@ def to_constant_bin_number(d,
new_bins[b].append(new_dict[key])
return new_bins

if __name__=="__main__":

if __name__ == "__main__":
import pylab as pl
import numpy as np

a = np.random.power(0.01,size=1000)
a = np.random.power(0.01, size=1000)
N_bin = 9

bins = to_constant_bin_number(a,N_bin)
bins = to_constant_bin_number(a, N_bin)
weight_sums = [np.sum(b) for b in bins]

#show max values of a and weight sums of the bins
print(np.sort(a)[-1:-11:-1],weight_sums)
# show max values of a and weight sums of the bins
print(np.sort(a)[-1:-11:-1], weight_sums)

#plot distribution
pl.plot(np.arange(N_bin),[np.sum(b) for b in bins])
pl.ylim([0,max([np.sum(b) for b in bins])+0.1])
# plot distribution
pl.plot(np.arange(N_bin), [np.sum(b) for b in bins])
pl.ylim([0, max([np.sum(b) for b in bins]) + 0.1])

b = { 'a': 10, 'b': 10, 'c':11, 'd':1, 'e': 2,'f':7 }
bins = to_constant_bin_number(b,4)
print("===== dict\n",b,"\n",bins)
b = {'a': 10, 'b': 10, 'c': 11, 'd': 1, 'e': 2, 'f': 7}
bins = to_constant_bin_number(b, 4)
print("===== dict\n", b, "\n", bins)

lower_bound = None
upper_bound = None

b = [ ('a', 10), ('b', 10), ('c',11), ('d',1), ('e', 2),('f',7,'foo') ]
bins = to_constant_bin_number(b,4,weight_pos=1,lower_bound=lower_bound,upper_bound=upper_bound)
print("===== list of tuples\n",b,"\n",bins)
b = [('a', 10), ('b', 10), ('c', 11), ('d', 1), ('e', 2), ('f', 7, 'foo')]
bins = to_constant_bin_number(b, 4, weight_pos=1, lower_bound=lower_bound, upper_bound=upper_bound)
print("===== list of tuples\n", b, "\n", bins)

pl.show()