How to use groupBy?
SeifFeidi opened this issue · comments
I have a list of users with this structure: User (id, date, week_num, user_name),
I want to group all users by week_num and count number of appointments for each user_name to have at least 2 appointments by week for the same user_name.
I tried with countBi (), but I have this error (name 'countBi' is not defined)
You need to import ConstraintCollectors
from optapy.constraint
and use ConstraintCollectors
to get the collector. For instance:
from optapy.constraint import ConstraintCollectors
def each_user_name_must_have_at_least_two_appointments(constraint_factory: ConstraintFactory):
return (
constraint_factory.for_each(User)
.group_by(lambda user: user.week_num,
lambda user: user.user_name,
ConstraintCollectors.count())
.filter(lambda week, user_name, count: count < 2)
.penalize('User have less than two appointments in a given week',
HardSoftScore.ONE_HARD)
)
(as for what count
method to use, that depends on the cardinality of the stream; see https://www.optapy.org/docs/latest/constraint-streams/constraint-streams.html#collectorsCount for details)
thank you for your help
With this example I have one appointment by week, I want to have one appointment for each user. name by week!!
def nbr_rdv_resp(constraint_factory: ConstraintFactory):
return (
constraint_factory.for_each(User)
.group_by(lambda user: user.dateslot.num_week,
lambda user: user.name,
ConstraintCollectors.count())
.filter(lambda week, name, count: count >= 2)
.penalize('User have less than two appointments in a given week',
HardMediumSoftScore.ONE_HARD)
)
That look like it should work (translating into english, the constraint you written should be equivalent to "penalize having more than one appointment per (user.week_num, user.name) pair by 1 hard"). If you want exactly one (i.e. not 0 or 1), you need to use if_not_exists
to penalize the 0 case:
from optapy.constraint import Joiners
def each_user_name_must_have_at_least_one_appointment_per_week(constraint_factory: ConstraintFactory):
return (
constraint_factory.for_each(Week)
.join(UserName)
.if_not_exists(User,
Joiners.equal(lambda week, user_name: week.week_num,
lambda user: user.week_num),
Joiners.equal(lambda week, user_name: user_name.user_name,
lambda user: user.user_name),
)
.penalize('User name does not have an appointment for the given week',
HardSoftScore.ONE_HARD)
)
(Where Week
and UserName
are problem fact classes that store information about possible week numbers and user names respectively that have a corresponding @problem_fact_collection_property
fields in the @planning_solution
)
Have you tested it using constraint_verifier (see https://github.com/optapy/optapy-quickstarts/blob/stable/school-timetabling/tests.py and https://www.optapy.org/docs/latest/constraint-streams/constraint-streams.html#constraintStreamsTesting)