<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: MAX IV Value in Mathematical Optimization, Discrete-Event Simulation, and OR</title>
    <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959995#M4286</link>
    <description>&lt;P&gt;The Infeasibility of 2 means that some constraints are violated.&amp;nbsp; You might need to change some solver options, like increasing POPSIZE= or NABSFCONV=.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Here is code to call the black-box solver for different numGroups and return the best solution found:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc optmodel printlevel=0;
   set OBS;
   str purpose {OBS};
   str good_bad {OBS};
   read data have into OBS=[_N_] purpose good_bad;
   set &amp;lt;str&amp;gt; PURPOSES init {};
   num bad  {PURPOSES} init 0;
   num good {PURPOSES} init 0;
   str pThis, gbThis;
   for {i in OBS} do;
      pThis = purpose[i];
      gbThis = good_bad[i];
      PURPOSES = PURPOSES union {pThis};
      if      gbThis = 'bad'  then bad[pThis]  = bad[pThis]  + 1;
      else if gbThis = 'good' then good[pThis] = good[pThis] + 1;
   end;
   print bad good;
   num total_n_bad  = sum {p in PURPOSES} bad[p];
   num total_n_good = sum {p in PURPOSES} good[p];

   put total_n_bad=;
   put total_n_good=;

/*   num numGroups = 2;*/
   num numGroups;
   set GROUPS = 1..numGroups;

   var IsPurposeGroup {PURPOSES, GROUPS} binary;

   impvar N_bad  {g in GROUPS} = sum {p in PURPOSES} bad[p]  * IsPurposeGroup[p,g];
   impvar N_good {g in GROUPS} = sum {p in PURPOSES} good[p] * IsPurposeGroup[p,g];

   impvar Bad_dist  {g in GROUPS} = N_bad[g]  / total_n_bad;
   impvar Good_dist {g in GROUPS} = N_good[g] / total_n_good;

   impvar Woe {g in GROUPS} = (Bad_dist[g] - Good_dist[g]) * log(Bad_dist[g]/Good_dist[g]);

   max IV = sum {g in GROUPS} Woe[g];

   con OneGroupPerPurpose {p in PURPOSES}:
      sum {g in GROUPS} IsPurposeGroup[p,g] = 1;

   con Bad_dist_lb {g in GROUPS}:
      Bad_Dist[g] &amp;gt;= 0.05;

   con Good_dist_lb {g in GROUPS}:
      Good_Dist[g] &amp;gt;= 0.05;

/*   for {p in {'1','2','3'}} fix IsPurposeGroup[p,1] = 1;*/
/*   for {p in {'8','X'}}     fix IsPurposeGroup[p,2] = 1;*/

/*   solve with blackbox;*/

   num bestIV init -1;
   num bestNumGroups init .;
   num assignedGroup {PURPOSES};
   do numGroups = 2..card(PURPOSES);
      put numGroups=;
      solve with blackbox;
      if _solution_status_ ne 'FAILED' and bestIV &amp;lt; IV then do;
         print N_bad N_good Bad_dist Good_dist Woe;
         bestIV = IV;
         bestNumGroups = numGroups;
         for {p in PURPOSES} do;
            for {g in GROUPS: IsPurposeGroup[p,g].sol &amp;gt; 0.5} do;
               assignedGroup[p] = g;
               leave;
            end;
         end;
      end;
      put bestIV= bestNumGroups=;
   end;

   print bestIV bestNumGroups;
   print assignedGroup;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;For the German credit data, the best solution found has numGroups = 6, but the objective value is slightly worse than from your GA:&lt;/P&gt;
&lt;DIV class="branch"&gt;&lt;A target="_blank" name="IDX8"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: Print Table" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt; &lt;COLGROUP&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="l b header" scope="col"&gt;[1]&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;N_bad&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;N_good&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Bad_dist&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Good_dist&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Woe&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;1&lt;/TH&gt;
&lt;TD class="r data"&gt;89&lt;/TD&gt;
&lt;TD class="r data"&gt;145&lt;/TD&gt;
&lt;TD class="r data"&gt;0.29667&lt;/TD&gt;
&lt;TD class="r data"&gt;0.207143&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0321570&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;2&lt;/TH&gt;
&lt;TD class="r data"&gt;18&lt;/TD&gt;
&lt;TD class="r data"&gt;94&lt;/TD&gt;
&lt;TD class="r data"&gt;0.06000&lt;/TD&gt;
&lt;TD class="r data"&gt;0.134286&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0598464&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;3&lt;/TH&gt;
&lt;TD class="r data"&gt;31&lt;/TD&gt;
&lt;TD class="r data"&gt;43&lt;/TD&gt;
&lt;TD class="r data"&gt;0.10333&lt;/TD&gt;
&lt;TD class="r data"&gt;0.061429&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0217940&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;4&lt;/TH&gt;
&lt;TD class="r data"&gt;42&lt;/TD&gt;
&lt;TD class="r data"&gt;77&lt;/TD&gt;
&lt;TD class="r data"&gt;0.14000&lt;/TD&gt;
&lt;TD class="r data"&gt;0.110000&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0072349&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;5&lt;/TH&gt;
&lt;TD class="r data"&gt;58&lt;/TD&gt;
&lt;TD class="r data"&gt;123&lt;/TD&gt;
&lt;TD class="r data"&gt;0.19333&lt;/TD&gt;
&lt;TD class="r data"&gt;0.175714&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0016836&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;6&lt;/TH&gt;
&lt;TD class="r data"&gt;62&lt;/TD&gt;
&lt;TD class="r data"&gt;218&lt;/TD&gt;
&lt;TD class="r data"&gt;0.20667&lt;/TD&gt;
&lt;TD class="r data"&gt;0.311429&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0429590&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;BR /&gt;&lt;A target="_blank" name="IDX9"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: Print Table" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="r b header" scope="col"&gt;bestIV&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;bestNumGroups&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;0.16567&lt;/TD&gt;
&lt;TD class="r data"&gt;6&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;BR /&gt;&lt;A target="_blank" name="IDX10"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: assignedGroup" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt; &lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="l b header" scope="col"&gt;[1]&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;assignedGroup&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;0&lt;/TH&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;1&lt;/TH&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;2&lt;/TH&gt;
&lt;TD class="r data"&gt;5&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;3&lt;/TH&gt;
&lt;TD class="r data"&gt;6&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;4&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;5&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;6&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;8&lt;/TH&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;9&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;X&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The black-box solver is not guaranteed to find a globally optimal (or even a feasible) solution.&amp;nbsp; Now that I understand the problem that you want to solve, I have an idea to find a globally optimal solution by using the MILP solver instead and will share that later.&lt;/P&gt;</description>
    <pubDate>Sat, 22 Feb 2025 16:55:59 GMT</pubDate>
    <dc:creator>RobPratt</dc:creator>
    <dc:date>2025-02-22T16:55:59Z</dc:date>
    <item>
      <title>MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959759#M4281</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/1636"&gt;@RobPratt&lt;/a&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;I am doing a Score Card. I want to bin a category variable into 5 groups and get its Max IV value.&lt;/P&gt;
&lt;P&gt;But I am runing a problem . How can I pass through this ERROR information ?&lt;/P&gt;
&lt;P&gt;Thanks in advance .&lt;/P&gt;
&lt;P&gt;The Excel file is attachement.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc import datafile='D:\ifre_backup\Ksharp\1--German Credit.xlsx' dbms=xlsx out=have replace;
run;
%let var=purpose   ;  /*字符型*/
%let group=5 ;
data temp;
 set have;
 keep &amp;amp;var good_bad ;  /*good_bad 的值： good  bad */
run;
proc freq data=temp noprint;
table &amp;amp;var./out=level;
run;




proc optmodel;
set OBS;
str good_bad{OBS};
str &amp;amp;var.{OBS};
read data temp into OBS=[_n_] good_bad &amp;amp;var.;

set BYSET_INDEX;
str BYSET{BYSET_INDEX};
read data level into BYSET_INDEX=[_n_] BYSET=&amp;amp;var.;

set GROUP=1..&amp;amp;group.;
set SUB_BYSET_INDEX;
set&amp;lt;str&amp;gt; BY;
set OBS_BY;
num n_bad{GROUP};
num n_good{GROUP};
num bad_dist{GROUP};
num good_dist{GROUP};
num woe{GROUP};
num total_n_bad;
num total_n_good;

total_n_bad =sum{i in OBS} if good_bad[i]='bad'  then 1 else 0;
total_n_good=sum{i in OBS} if good_bad[i]='good' then 1 else 0;

var v{BYSET_INDEX} &amp;gt;=1 &amp;lt;=&amp;amp;group. integer;

cofor{g in GROUP} do;
 SUB_BYSET_INDEX={k in BYSET_INDEX:v[k]=g};
 BY=setof{i in SUB_BYSET_INDEX} BYSET[i];
 OBS_BY={i in OBS:&amp;amp;var.[i] in BY};

 n_bad[g] =sum{i in OBS_BY} if good_bad[i]='bad' then 1 else 0;
 n_good[g]=sum{i in OBS_BY} if good_bad[i]='good' then 1 else 0;
 bad_dist[g]=n_bad[g]/total_n_bad ; 
 good_dist[g]=n_good[g]/total_n_good ; 
 woe[g]=(bad_dist[g]-good_dist[g])*log(bad_dist[g]/good_dist[g]);
end;

max iv=sum{g in GROUP} woe[g];
solve;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Ksharp_0-1740043187809.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/104784i58E6FF8F31AC10BF/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Ksharp_0-1740043187809.png" alt="Ksharp_0-1740043187809.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 20 Feb 2025 09:19:55 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959759#M4281</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2025-02-20T09:19:55Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959974#M4282</link>
      <description>&lt;P&gt;There are a few issues here:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;A COFOR loop does not achieve any parallelism unless the body of the loop contains at least one SOLVE statement.&lt;/LI&gt;
&lt;LI&gt;If n_good[g] = 0, then good_dist[g] = 0, yielding a division by zero in the argument of the LOG function and hence a missing value for woe[g].&lt;/LI&gt;
&lt;LI&gt;Your objective function IV depends only on numeric parameters and not on decision variables.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;It looks like you are trying to solve a separate optimization problem for each group.&amp;nbsp; If so, I recommend first writing the code to solve only one group.&amp;nbsp; If not, please provide an algebraic description of the optimization problem you want to solve.&lt;/P&gt;</description>
      <pubDate>Fri, 21 Feb 2025 22:40:23 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959974#M4282</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2025-02-21T22:40:23Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959982#M4283</link>
      <description>&lt;P&gt;OK. Take group=2 for example:&lt;/P&gt;
&lt;PRE&gt;Data looks like this,I want to generate a GROUP variable:&lt;BR /&gt;good_bad  group  purpose
good        1     1
bad         1     1
good        1     2
good        1     2
bad         1     2
good        1     3

good        2     8
good        2     8
bad         2     X
bad         2     X&lt;BR /&gt;&lt;BR /&gt;&lt;STRONG&gt;Notice: each of purpose have ONLY ONE group . &lt;/STRONG&gt;&lt;BR /&gt;&lt;STRONG&gt;        you could not include purpose=1 in both group=1 and group=2.&lt;/STRONG&gt;




total_n_bad=4   total_n_good=6

group=1
--------
n_bad=2 n_good=4 
bad_dist=n_bad/total_n_bad=2/4=0.5 
good_dist=n_good/total_n_good=4/6=0.667
woe=(Bad_Dist-Good_Dist)*log(Bad_Dist/Good_Dist)=(0.5-0.667)*log(0.5/0.667)=0.048


group=2 
--------
n_bad=2 n_good=2
bad_dist=n_bad/total_n_bad=2/4=0.5 
good_dist=n_good/total_n_good=2/6=0.333
woe=(Bad_Dist-Good_Dist)*log(Bad_Dist/Good_Dist)=(0.5-0.333)*log(0.5/0.333)=0.068


iv=0.048 + 0.068 = 0.116   &lt;STRONG&gt;&amp;lt;----- I want to maximize this iv .&lt;/STRONG&gt;



And I also have two constraints:
group=1
--------
&lt;STRONG&gt;Bad_Dist&amp;gt;0.05 and Good_Dist&amp;gt;0.05&lt;/STRONG&gt;

group=2
--------
&lt;STRONG&gt;Bad_Dist&amp;gt;0.05 and Good_Dist&amp;gt;0.05&lt;/STRONG&gt;

to avoid "If n_good[g] = 0, then good_dist[g] = 0, yielding a division by zero"



P.S.
&lt;STRONG&gt;The group could be 3,4,5,6,7,8,9,10.....&lt;/STRONG&gt;
and pick up the max IV from these group.
E.X.  group=8 have the max IV when group in (2 3 4 5 6 7 8 9 10).&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Sat, 22 Feb 2025 01:52:30 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959982#M4283</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2025-02-22T01:52:30Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959985#M4284</link>
      <description>&lt;P&gt;Here's a straightforward approach that uses the black-box solver:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;data have;
   input good_bad $ purpose $;
   datalines;
good        1
bad         1
good        2
good        2
bad         2
good        3
good        8
good        8
bad         X
bad         X
;

proc optmodel;
   set OBS;
   str purpose {OBS};
   str good_bad {OBS};
   read data have into OBS=[_N_] purpose good_bad;
   set &amp;lt;str&amp;gt; PURPOSES init {};
   num bad  {PURPOSES} init 0;
   num good {PURPOSES} init 0;
   str pThis, gbThis;
   for {i in OBS} do;
      pThis = purpose[i];
      gbThis = good_bad[i];
      PURPOSES = PURPOSES union {pThis};
      if      gbThis = 'bad'  then bad[pThis]  = bad[pThis]  + 1;
      else if gbThis = 'good' then good[pThis] = good[pThis] + 1;
   end;
   print bad good;
   num total_n_bad  = sum {p in PURPOSES} bad[p];
   num total_n_good = sum {p in PURPOSES} good[p];

   put total_n_bad=;
   put total_n_good=;

   num numGroups = 2;
   set GROUPS = 1..numGroups;

   var IsPurposeGroup {PURPOSES, GROUPS} binary;

   impvar N_bad  {g in GROUPS} = sum {p in PURPOSES} bad[p]  * IsPurposeGroup[p,g];
   impvar N_good {g in GROUPS} = sum {p in PURPOSES} good[p] * IsPurposeGroup[p,g];

   impvar Bad_dist  {g in GROUPS} = N_bad[g]  / total_n_bad;
   impvar Good_dist {g in GROUPS} = N_good[g] / total_n_good;

   impvar Woe {g in GROUPS} = (Bad_dist[g] - Good_dist[g]) * log(Bad_dist[g]/Good_dist[g]);

   max IV = sum {g in GROUPS} Woe[g];

   con OneGroupPerPurpose {p in PURPOSES}:
      sum {g in GROUPS} IsPurposeGroup[p,g] = 1;

   con Bad_dist_lb {g in GROUPS}:
      Bad_Dist[g] &amp;gt;= 0.05;

   con Good_dist_lb {g in GROUPS}:
      Good_Dist[g] &amp;gt;= 0.05;

/*   for {p in {'1','2','3'}} fix IsPurposeGroup[p,1] = 1;*/
/*   for {p in {'8','X'}}     fix IsPurposeGroup[p,2] = 1;*/

   solve with blackbox;

   print N_bad N_good Bad_dist Good_dist Woe;

   num assignedGroup {PURPOSES};
   for {p in PURPOSES} do;
      for {g in GROUPS: IsPurposeGroup[p,g].sol &amp;gt; 0.5} do;
         assignedGroup[p] = g;
         leave;
      end;
   end;

   print assignedGroup;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;On my machine, this yields a maximum IV of&amp;nbsp;1.5796959506.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Uncomment the FIX statements to recover your sample solution with IV = 0.1155245301.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Please verify whether this solves your problem for a fixed value of numGroups.&amp;nbsp; Then I can show you how to find the best numGroups.&lt;/P&gt;</description>
      <pubDate>Sat, 22 Feb 2025 04:09:41 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959985#M4284</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2025-02-22T04:09:41Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959987#M4285</link>
      <description>&lt;P&gt;RobPratt,&lt;/P&gt;
&lt;P&gt;Many thanks. But I applied your code into my excel. I got this WARNNING.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc import datafile='D:\ifre_backup\Ksharp\1--German Credit.xlsx' dbms=xlsx out=have replace;
run;

%let var=purpose   ;  /*字符型*/
%let group=6 ;

data have(rename=(&amp;amp;var.=purpose));
 set have;
 keep &amp;amp;var good_bad ;  /*good_bad 的值： good  bad */
run;


proc optmodel;
   set OBS;
   str purpose {OBS};
   str good_bad {OBS};
   read data have into OBS=[_N_] purpose good_bad;
   set &amp;lt;str&amp;gt; PURPOSES init {};
   num bad  {PURPOSES} init 0;
   num good {PURPOSES} init 0;
   str pThis, gbThis;
   for {i in OBS} do;
      pThis = purpose[i];
      gbThis = good_bad[i];
      PURPOSES = PURPOSES union {pThis};
      if      gbThis = 'bad'  then bad[pThis]  = bad[pThis]  + 1;
      else if gbThis = 'good' then good[pThis] = good[pThis] + 1;
   end;
   print bad good;
   num total_n_bad  = sum {p in PURPOSES} bad[p];
   num total_n_good = sum {p in PURPOSES} good[p];

   put total_n_bad=;
   put total_n_good=;

   num numGroups = &amp;amp;group.;
   set GROUPS = 1..numGroups;

   var IsPurposeGroup {PURPOSES, GROUPS} binary;

   impvar N_bad  {g in GROUPS} = sum {p in PURPOSES} bad[p]  * IsPurposeGroup[p,g];
   impvar N_good {g in GROUPS} = sum {p in PURPOSES} good[p] * IsPurposeGroup[p,g];

   impvar Bad_dist  {g in GROUPS} = N_bad[g]  / total_n_bad;
   impvar Good_dist {g in GROUPS} = N_good[g] / total_n_good;

   impvar Woe {g in GROUPS} = (Bad_dist[g] - Good_dist[g]) * log(Bad_dist[g]/Good_dist[g]);

   max IV = sum {g in GROUPS} Woe[g];

   con OneGroupPerPurpose {p in PURPOSES}:
      sum {g in GROUPS} IsPurposeGroup[p,g] = 1;

   con Bad_dist_lb {g in GROUPS}:
      Bad_Dist[g] &amp;gt;= 0.05;

   con Good_dist_lb {g in GROUPS}:
      Good_Dist[g] &amp;gt;= 0.05;

/*   for {p in {'1','2','3'}} fix IsPurposeGroup[p,1] = 1;*/
/*   for {p in {'8','X'}}     fix IsPurposeGroup[p,2] = 1;*/

   solve with blackbox;

   print N_bad N_good Bad_dist Good_dist Woe;

   num assignedGroup {PURPOSES};
   for {p in PURPOSES} do;
      for {g in GROUPS: IsPurposeGroup[p,g].sol &amp;gt; 0.5} do;
         assignedGroup[p] = g;
         leave;
      end;
   end;

   print assignedGroup;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;PRE&gt;NOTE: The problem has 60 variables (0 free, 0 fixed).
NOTE: The problem uses 30 implicit variables.
NOTE: The problem has 60 binary and 0 integer variables.
NOTE: The problem has 22 linear constraints (0 LE, 10 EQ, 12 GE, 0 range).
NOTE: The problem has 180 linear constraint coefficients.
NOTE: The problem has 0 nonlinear constraints (0 LE, 0 EQ, 0 GE, 0 range).
NOTE: The OPTMODEL presolver removed 0 variables, 0 linear constraints, and 0 nonlinear constraints.
NOTE: The black-box solver is using up to 4 threads.
NOTE: The black-box solver is using the EAGLS optimizer algorithm.
NOTE: The problem has 60 variables (60 integer, 0 continuous).
NOTE: The problem has 22 constraints (22 linear, 0 nonlinear).
NOTE: The problem has 1 user-defined functions.
NOTE: The deterministic parallel mode is enabled.
                         Best
        Iteration        Objective    Infeasibility    Evals     Time
                1       0.08127539       2.00000000      301        0
                2       0.08127539       2.00000000      363        0
                3       0.08127539       2.00000000      366        0
                4       0.08127539       2.00000000      372        0
                5       0.08127539       2.00000000      376        0
                6       0.08127539       2.00000000      381        0
                7       0.08127539       2.00000000      385        0
                8       0.08127539       2.00000000      389        0
                9       0.08127539       2.00000000      391        0
               10       0.08127539       2.00000000      393        0
               11       0.08127539       2.00000000      395        0
&lt;FONT color="#FF0000"&gt;&lt;STRONG&gt;WARNING: The best solution found does not satisfy the feasibility tolerance.&lt;/STRONG&gt;
&lt;STRONG&gt;NOTE: Failed.&lt;/STRONG&gt;&lt;/FONT&gt;
64
65      print N_bad N_good Bad_dist Good_dist Woe;
NOTE: Division by zero at line 47 column 77.
NOTE: Division by zero at line 47 column 77.
NOTE: Division by zero at line 47 column 77.
NOTE: Division by zero at line 47 column 77.
NOTE: Division by zero at line 47 column 77.
NOTE: Division by zero at line 47 column 77.
&lt;/PRE&gt;
&lt;P&gt;And If I used the Genetic Algorithm code of mine, I could get this: ( group=6&amp;nbsp; has the max IV)&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Ksharp_0-1740208592653.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/104853iE190BA9C3406B4AC/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Ksharp_0-1740208592653.png" alt="Ksharp_0-1740208592653.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Sat, 22 Feb 2025 07:17:01 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959987#M4285</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2025-02-22T07:17:01Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959995#M4286</link>
      <description>&lt;P&gt;The Infeasibility of 2 means that some constraints are violated.&amp;nbsp; You might need to change some solver options, like increasing POPSIZE= or NABSFCONV=.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Here is code to call the black-box solver for different numGroups and return the best solution found:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc optmodel printlevel=0;
   set OBS;
   str purpose {OBS};
   str good_bad {OBS};
   read data have into OBS=[_N_] purpose good_bad;
   set &amp;lt;str&amp;gt; PURPOSES init {};
   num bad  {PURPOSES} init 0;
   num good {PURPOSES} init 0;
   str pThis, gbThis;
   for {i in OBS} do;
      pThis = purpose[i];
      gbThis = good_bad[i];
      PURPOSES = PURPOSES union {pThis};
      if      gbThis = 'bad'  then bad[pThis]  = bad[pThis]  + 1;
      else if gbThis = 'good' then good[pThis] = good[pThis] + 1;
   end;
   print bad good;
   num total_n_bad  = sum {p in PURPOSES} bad[p];
   num total_n_good = sum {p in PURPOSES} good[p];

   put total_n_bad=;
   put total_n_good=;

/*   num numGroups = 2;*/
   num numGroups;
   set GROUPS = 1..numGroups;

   var IsPurposeGroup {PURPOSES, GROUPS} binary;

   impvar N_bad  {g in GROUPS} = sum {p in PURPOSES} bad[p]  * IsPurposeGroup[p,g];
   impvar N_good {g in GROUPS} = sum {p in PURPOSES} good[p] * IsPurposeGroup[p,g];

   impvar Bad_dist  {g in GROUPS} = N_bad[g]  / total_n_bad;
   impvar Good_dist {g in GROUPS} = N_good[g] / total_n_good;

   impvar Woe {g in GROUPS} = (Bad_dist[g] - Good_dist[g]) * log(Bad_dist[g]/Good_dist[g]);

   max IV = sum {g in GROUPS} Woe[g];

   con OneGroupPerPurpose {p in PURPOSES}:
      sum {g in GROUPS} IsPurposeGroup[p,g] = 1;

   con Bad_dist_lb {g in GROUPS}:
      Bad_Dist[g] &amp;gt;= 0.05;

   con Good_dist_lb {g in GROUPS}:
      Good_Dist[g] &amp;gt;= 0.05;

/*   for {p in {'1','2','3'}} fix IsPurposeGroup[p,1] = 1;*/
/*   for {p in {'8','X'}}     fix IsPurposeGroup[p,2] = 1;*/

/*   solve with blackbox;*/

   num bestIV init -1;
   num bestNumGroups init .;
   num assignedGroup {PURPOSES};
   do numGroups = 2..card(PURPOSES);
      put numGroups=;
      solve with blackbox;
      if _solution_status_ ne 'FAILED' and bestIV &amp;lt; IV then do;
         print N_bad N_good Bad_dist Good_dist Woe;
         bestIV = IV;
         bestNumGroups = numGroups;
         for {p in PURPOSES} do;
            for {g in GROUPS: IsPurposeGroup[p,g].sol &amp;gt; 0.5} do;
               assignedGroup[p] = g;
               leave;
            end;
         end;
      end;
      put bestIV= bestNumGroups=;
   end;

   print bestIV bestNumGroups;
   print assignedGroup;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;For the German credit data, the best solution found has numGroups = 6, but the objective value is slightly worse than from your GA:&lt;/P&gt;
&lt;DIV class="branch"&gt;&lt;A target="_blank" name="IDX8"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: Print Table" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt; &lt;COLGROUP&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="l b header" scope="col"&gt;[1]&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;N_bad&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;N_good&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Bad_dist&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Good_dist&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;Woe&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;1&lt;/TH&gt;
&lt;TD class="r data"&gt;89&lt;/TD&gt;
&lt;TD class="r data"&gt;145&lt;/TD&gt;
&lt;TD class="r data"&gt;0.29667&lt;/TD&gt;
&lt;TD class="r data"&gt;0.207143&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0321570&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;2&lt;/TH&gt;
&lt;TD class="r data"&gt;18&lt;/TD&gt;
&lt;TD class="r data"&gt;94&lt;/TD&gt;
&lt;TD class="r data"&gt;0.06000&lt;/TD&gt;
&lt;TD class="r data"&gt;0.134286&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0598464&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;3&lt;/TH&gt;
&lt;TD class="r data"&gt;31&lt;/TD&gt;
&lt;TD class="r data"&gt;43&lt;/TD&gt;
&lt;TD class="r data"&gt;0.10333&lt;/TD&gt;
&lt;TD class="r data"&gt;0.061429&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0217940&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;4&lt;/TH&gt;
&lt;TD class="r data"&gt;42&lt;/TD&gt;
&lt;TD class="r data"&gt;77&lt;/TD&gt;
&lt;TD class="r data"&gt;0.14000&lt;/TD&gt;
&lt;TD class="r data"&gt;0.110000&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0072349&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;5&lt;/TH&gt;
&lt;TD class="r data"&gt;58&lt;/TD&gt;
&lt;TD class="r data"&gt;123&lt;/TD&gt;
&lt;TD class="r data"&gt;0.19333&lt;/TD&gt;
&lt;TD class="r data"&gt;0.175714&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0016836&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;6&lt;/TH&gt;
&lt;TD class="r data"&gt;62&lt;/TD&gt;
&lt;TD class="r data"&gt;218&lt;/TD&gt;
&lt;TD class="r data"&gt;0.20667&lt;/TD&gt;
&lt;TD class="r data"&gt;0.311429&lt;/TD&gt;
&lt;TD class="r data"&gt;0.0429590&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;BR /&gt;&lt;A target="_blank" name="IDX9"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: Print Table" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="r b header" scope="col"&gt;bestIV&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;bestNumGroups&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="r data"&gt;0.16567&lt;/TD&gt;
&lt;TD class="r data"&gt;6&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;BR /&gt;&lt;A target="_blank" name="IDX10"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: assignedGroup" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt; &lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="l b header" scope="col"&gt;[1]&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;assignedGroup&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;0&lt;/TH&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;1&lt;/TH&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;2&lt;/TH&gt;
&lt;TD class="r data"&gt;5&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;3&lt;/TH&gt;
&lt;TD class="r data"&gt;6&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;4&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;5&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;6&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;8&lt;/TH&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;9&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;X&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The black-box solver is not guaranteed to find a globally optimal (or even a feasible) solution.&amp;nbsp; Now that I understand the problem that you want to solve, I have an idea to find a globally optimal solution by using the MILP solver instead and will share that later.&lt;/P&gt;</description>
      <pubDate>Sat, 22 Feb 2025 16:55:59 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959995#M4286</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2025-02-22T16:55:59Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959996#M4287</link>
      <description>&lt;P&gt;As promised, here is the MILP approach I had in mind:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc optmodel;
   set OBS;
   str purpose {OBS};
   str good_bad {OBS};
   read data have into OBS=[_N_] purpose good_bad;
   set &amp;lt;str&amp;gt; PURPOSES init {};
   num bad  {PURPOSES} init 0;
   num good {PURPOSES} init 0;
   str pThis, gbThis;
   for {i in OBS} do;
      pThis = purpose[i];
      gbThis = good_bad[i];
      PURPOSES = PURPOSES union {pThis};
      if      gbThis = 'bad'  then bad[pThis]  = bad[pThis]  + 1;
      else if gbThis = 'good' then good[pThis] = good[pThis] + 1;
   end;
   print bad good;
   num total_n_bad  = sum {p in PURPOSES} bad[p];
   num total_n_good = sum {p in PURPOSES} good[p];

   put total_n_bad=;
   put total_n_good=;

   /* use CLP solver to enumerate all candidate groups */
   var IsPurpose {PURPOSES} binary;
   con Bad_dist_lb:
      sum {p in PURPOSES} bad[p] * IsPurpose[p] &amp;gt;= 0.05 * total_n_bad;
   con Good_dist_lb:
      sum {p in PURPOSES} good[p] * IsPurpose[p] &amp;gt;= 0.05 * total_n_good;

   solve with clp / findallsolns;
   set &amp;lt;str&amp;gt; PURPOSES_THIS;
   set GROUPS init {};
   set &amp;lt;str&amp;gt; PURPOSES_g {GROUPS};
   set GROUPS_p {PURPOSES} init {};
   num n_bad  {GROUPS};
   num n_good {GROUPS};
   num bad_dist  {g in GROUPS} = n_bad[g]  / total_n_bad;
   num good_dist {g in GROUPS} = n_good[g] / total_n_good;
   num woe {g in GROUPS} = (bad_dist[g] - good_dist[g]) * log(bad_dist[g]/good_dist[g]);
   for {g in 1.._NSOL_} do;
      PURPOSES_THIS = {p in PURPOSES: IsPurpose[p].sol[g] &amp;gt; 0.5};
      GROUPS = GROUPS union {g};
      PURPOSES_g[g] = PURPOSES_THIS;
      for {p in PURPOSES_THIS} GROUPS_p[p] = GROUPS_p[p] union {g};
      n_bad[g]  = sum {p in PURPOSES_THIS} bad[p];
      n_good[g] = sum {p in PURPOSES_THIS} good[p];
   end;
/*   print n_bad n_good bad_dist good_dist woe;*/

   /* use MILP solver to partition purposes into groups */
   var IsGroup {GROUPS} binary;
   max IV = sum {g in GROUPS} woe[g] * IsGroup[g];
   con OneGroupPerPurpose {p in PURPOSES}:
      sum {g in GROUPS_p[p]} IsGroup[g] = 1;

   problem PartitionProblem include
      IsGroup IV OneGroupPerPurpose;
   use problem PartitionProblem;
   solve;

   num assignedGroup {PURPOSES};
   num count init 0;
   for {g in GROUPS: IsGroup[g].sol &amp;gt; 0.5} do;
      count = count + 1;
      for {p in PURPOSES_g[g]} assignedGroup[p] = count;
   end;
   print assignedGroup;
quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;For the German credit data, the resulting (globally optimal) solution is slightly better than your GA solution:&lt;/P&gt;
&lt;DIV class="branch"&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: Solution Summary" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="c b header" colspan="2" scope="colgroup"&gt;Solution Summary&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Solver&lt;/TH&gt;
&lt;TD class="r data"&gt;MILP&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Algorithm&lt;/TH&gt;
&lt;TD class="r data"&gt;Branch and Cut&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Objective Function&lt;/TH&gt;
&lt;TD class="r data"&gt;IV&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Solution Status&lt;/TH&gt;
&lt;TD class="r data"&gt;Optimal&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Objective Value&lt;/TH&gt;
&lt;TD class="r data"&gt;0.1676461706&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;&amp;nbsp;&lt;/TH&gt;
&lt;TD class="r data"&gt;&amp;nbsp;&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Relative Gap&lt;/TH&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Absolute Gap&lt;/TH&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Primal Infeasibility&lt;/TH&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Bound Infeasibility&lt;/TH&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Integer Infeasibility&lt;/TH&gt;
&lt;TD class="r data"&gt;0&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;&amp;nbsp;&lt;/TH&gt;
&lt;TD class="r data"&gt;&amp;nbsp;&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Best Bound&lt;/TH&gt;
&lt;TD class="r data"&gt;0.1676461706&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Nodes&lt;/TH&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Solutions Found&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Iterations&lt;/TH&gt;
&lt;TD class="r data"&gt;24&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Presolve Time&lt;/TH&gt;
&lt;TD class="r data"&gt;0.10&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;Solution Time&lt;/TH&gt;
&lt;TD class="r data"&gt;0.21&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;BR /&gt;&lt;A target="_blank" name="IDX57"&gt;&lt;/A&gt;
&lt;DIV&gt;
&lt;DIV align="center"&gt;
&lt;TABLE class="table" summary="Procedure Optmodel: assignedGroup" frame="box" rules="all" cellspacing="0" cellpadding="5"&gt;&lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt; &lt;COLGROUP&gt; &lt;COL /&gt;&lt;/COLGROUP&gt;
&lt;THEAD&gt;
&lt;TR&gt;
&lt;TH class="l b header" scope="col"&gt;[1]&lt;/TH&gt;
&lt;TH class="r b header" scope="col"&gt;assignedGroup&lt;/TH&gt;
&lt;/TR&gt;
&lt;/THEAD&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;0&lt;/TH&gt;
&lt;TD class="r data"&gt;3&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;1&lt;/TH&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;2&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;3&lt;/TH&gt;
&lt;TD class="r data"&gt;6&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;4&lt;/TH&gt;
&lt;TD class="r data"&gt;4&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;5&lt;/TH&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;6&lt;/TH&gt;
&lt;TD class="r data"&gt;5&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;8&lt;/TH&gt;
&lt;TD class="r data"&gt;2&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;9&lt;/TH&gt;
&lt;TD class="r data"&gt;1&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="l rowheader" scope="row"&gt;X&lt;/TH&gt;
&lt;TD class="r data"&gt;5&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="lia-align-left"&gt;The only difference is that purpose 5 is now in the same group as purpose 9.&lt;/P&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;
&lt;/DIV&gt;</description>
      <pubDate>Sat, 22 Feb 2025 16:54:21 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/959996#M4287</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2025-02-22T16:54:21Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/960049#M4288</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/1636"&gt;@RobPratt&lt;/a&gt; ,&lt;BR /&gt;That is awesome . If you don't mind , I have another problem similar with this problem to solve.&lt;/P&gt;
&lt;P&gt;This problem is for category variable, but I also need to do the same thing to &lt;STRONG&gt;continuous variable&lt;/STRONG&gt;.&lt;/P&gt;
&lt;P&gt;Here is algorthim:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;Data looks like this,I want to generate a GROUP variable:
Here I have a cutpoint &lt;STRONG&gt;DURATION=12&lt;/STRONG&gt; ,that could split DURATION (a continuous variable) into TWO groups.&lt;BR /&gt;But&lt;STRONG&gt; if you have TWO cutpoints,you would yield THREE groups, THREE cutpoints yield FOUR groups&lt;/STRONG&gt;........


good_bad  group  duration
good        1     2
bad         1     4
good        1     5
good        1     6
bad         1     8
good        1     10

good        2     18
good        2     28
bad         2     30
bad         2     32






total_n_bad=4   total_n_good=6

group=1
--------
n_bad=2 n_good=4 
bad_dist=n_bad/total_n_bad=2/4=0.5 
good_dist=n_good/total_n_good=4/6=0.667
woe=(Bad_Dist-Good_Dist)*log(Bad_Dist/Good_Dist)=(0.5-0.667)*log(0.5/0.667)=0.048


group=2 
--------
n_bad=2 n_good=2
bad_dist=n_bad/total_n_bad=2/4=0.5 
good_dist=n_good/total_n_good=2/6=0.333
woe=(Bad_Dist-Good_Dist)*log(Bad_Dist/Good_Dist)=(0.5-0.333)*log(0.5/0.333)=0.068


iv=0.048 + 0.068 = 0.116   &amp;lt;----- I want to maximize this iv .



And I also have &lt;STRONG&gt;THREE &lt;/STRONG&gt;constraints:
group=1
--------
&lt;STRONG&gt;Bad_Dist&amp;gt;0.05 and Good_Dist&amp;gt;0.05&lt;/STRONG&gt;

group=2
--------
&lt;STRONG&gt;Bad_Dist&amp;gt;0.05 and Good_Dist&amp;gt;0.05&lt;/STRONG&gt;

to avoid "If n_good[g] = 0, then good_dist[g] = 0, yielding a division by zero"&lt;BR /&gt;&lt;BR /&gt;
&lt;BR /&gt;&lt;STRONG&gt;woe[1]&amp;lt;woe[2]&amp;lt;woe[3]&amp;lt;woe[4]...........&lt;/STRONG&gt;&lt;BR /&gt;&lt;STRONG&gt;or&lt;/STRONG&gt;&lt;BR /&gt;&lt;STRONG&gt;woe[1]&amp;gt;woe[2]&amp;gt;woe[3]&amp;gt;woe[4]...........&lt;/STRONG&gt;&lt;BR /&gt;&lt;STRONG&gt;a.k.a &lt;FONT color="#FF0000"&gt;woe is monotonic&lt;/FONT&gt; .&lt;/STRONG&gt;&lt;BR /&gt;
&lt;BR /&gt;

P.S.
The group could be 3,4,5,6,7,8,9,10.....
and pick up the max IV from these group.
E.X.  group=8 have the max IV when group in (2 3 4 5 6 7 8 9 10).&lt;/PRE&gt;
&lt;P&gt;Here is an example used by my GA code:&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Ksharp_0-1740366507290.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/104881i7D9A817997BCD74A/image-size/medium?v=v2&amp;amp;px=400" role="button" title="Ksharp_0-1740366507290.png" alt="Ksharp_0-1740366507290.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 24 Feb 2025 06:43:46 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/960049#M4288</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2025-02-24T06:43:46Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/960092#M4289</link>
      <description>&lt;P&gt;Glad to help.&amp;nbsp; Please start a new thread for your new question.&lt;/P&gt;</description>
      <pubDate>Mon, 24 Feb 2025 15:33:13 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/960092#M4289</guid>
      <dc:creator>RobPratt</dc:creator>
      <dc:date>2025-02-24T15:33:13Z</dc:date>
    </item>
    <item>
      <title>Re: MAX IV Value</title>
      <link>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/960127#M4290</link>
      <description>OK. Already start a brand-new thread at OR forum.</description>
      <pubDate>Tue, 25 Feb 2025 01:58:27 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Mathematical-Optimization/MAX-IV-Value/m-p/960127#M4290</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2025-02-25T01:58:27Z</dc:date>
    </item>
  </channel>
</rss>

