Saturday, February 7, 2015

JAVA EE 6 WAR with MAVEN

Starting with Java EE 6 developers could use WARs instead of EARs. Developers can use WAR-module, which has a compile-scoped dependency to several <packaging>jar</packaging> typed modules. This article presented a simple way to packing multiply WAR modules into only one Web application.


1. Create pom-root(root project) and multiple WAR modules(web-1, web-2, and web-parent projects).


pom.xml of root project is applied to share dependencies to other web projects.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.blogshot.wittakarn</groupId>
 <artifactId>root</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>pom</packaging>
 <name>root</name>

 <properties>
  <endorsed.dir>
                    ${project.build.directory}/endorsed
                </endorsed.dir>
  <project.build.sourceEncoding>
                    UTF-8
                </project.build.sourceEncoding>

  <!-- Maven Build Plugin Properties -->
  <my.maven.compiler>3.0</my.maven.compiler>
  <my.maven.war>2.4</my.maven.war>

  <!-- Java Properties -->
  <my.java>1.7</my.java>

  <!-- Dependency Properties -->
  <my.org.primefaces>5.1</my.org.primefaces>
  <my.commons.fileupload>1.2.2</my.commons.fileupload>
  <my.commons.io>2.4</my.commons.io>
  <my.maven.resources>2.6</my.maven.resources>

  <!-- Scope provided -->
  <my.javaee.web.api>6.0</my.javaee.web.api>
  <my.mojarra>2.2.6</my.mojarra>

 </properties>

 <build>
 <plugins>
 <plugin>
  <groupId>
   org.apache.maven.plugins
  </groupId>
  <artifactId>
   maven-compiler-plugin
  </artifactId>
  <version>
   ${my.maven.compiler}
  </version>
  <configuration>
   <source>
    ${my.java}
   </source>
   <target>
    ${my.java}
   </target>
   <encoding>
    ${project.build.sourceEncoding}
   </encoding>
   <showDeprecation>
    true
   </showDeprecation>
  </configuration>
 </plugin>

 <plugin>
  <groupId>
   org.apache.maven.plugins
  </groupId>
  <artifactId>
   maven-war-plugin
  </artifactId>
  <version>${my.maven.war}</version>
  <configuration>
   <failOnMissingWebXml>
    false
   </failOnMissingWebXml>
   <attachClasses>true</attachClasses>
   <archiveClasses>true</archiveClasses>
   <archive>
    <manifest>
     <addDefaultImplementationEntries>
      true
     </addDefaultImplementationEntries>
     <addDefaultSpecificationEntries>
      true
     </addDefaultSpecificationEntries>
    </manifest>
   </archive>
  </configuration>
 </plugin>

 <plugin>
  <inherited>true</inherited>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-resources-plugin</artifactId>
  <version>${my.maven.resources}</version>
  <executions>
   <execution>
    <id>copy-resources</id>
    <phase>prepare-package</phase>
    <goals>
     <goal>copy-resources</goal>
    </goals>
    <configuration>
     <outputDirectory>
      ${basedir}/target/classes
     </outputDirectory>
     <resources>
     <resource>
     <directory>
      ${basedir}/src/main/webapp
     </directory>
     <includes>
      <include>**/*</include>
     </includes>
     <excludes>
      <exclude>WEB-INF/**</exclude>
     </excludes>
     <targetPath>
      /META-INF/resources
     </targetPath>
     </resource>

     <resource>
     <directory>
      src/main/resources
     </directory>
     <filtering>true</filtering>
     </resource>

     <resource>
     <directory>
     ${basedir}/src/main/webapp/WEB-INF
     </directory>
     <includes>
      <include>
       faces-config.xml
      </include>
     </includes>
     <targetPath>/META-INF</targetPath>
     </resource>
     </resources>
    </configuration>
   </execution>
  </executions>
 </plugin>
 </plugins>
 </build>

 <dependencies>
  <dependency>
   <groupId>javax</groupId>
   <artifactId>javaee-web-api</artifactId>
   <version>${my.javaee.web.api}</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>${my.commons.fileupload}</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>${my.commons.io}</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>javax.faces</artifactId>
   <version>${my.mojarra}</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.primefaces</groupId>
   <artifactId>primefaces</artifactId>
   <version>${my.org.primefaces}</version>
   <scope>compile</scope>
  </dependency>

 </dependencies>
</project>

All of web projects(web-1, web-2, and web-parent projects) will reference to root project in order to get dependencies.

pom.xml of web-1 project.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

  

    <parent>

  <artifactId>root</artifactId>

  <groupId>com.blogshot.wittakarn</groupId>

  <version>0.0.1-SNAPSHOT</version>

 </parent>



    <groupId>com.blogshot.wittakarn.web</groupId>

    <artifactId>web-1</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>war</packaging>



    <name>web-1</name>

</project>



pom.xml of web-2 project.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

  

    <parent>

  <artifactId>root</artifactId>

  <groupId>com.blogshot.wittakarn</groupId>

  <version>0.0.1-SNAPSHOT</version>

 </parent>



    <groupId>com.blogshot.wittakarn.web</groupId>

    <artifactId>web-2</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>war</packaging>


    <name>web-2</name>

</project>


2. Including web-1 and web-2 projects inside web-parent project
Developers can include multiple WAR modules by packing as a jar inside another WAR module. In the case of web-1 and web-2 are sub modules of web-parent, develops just include dependency  of web-1 and web-2 as following.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

   

    <parent>

  <artifactId>root</artifactId>

  <groupId>com.blogshot.wittakarn</groupId>

  <version>0.0.1-SNAPSHOT</version>

 </parent>



    <groupId>com.blogshot.wittakarn.web</groupId>

    <artifactId>web-parent</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>war</packaging>



    <name>web-parent</name>

   

    <dependencies>

  <dependency>

   <groupId>com.blogshot.wittakarn.web</groupId>

   <artifactId>web-1</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <classifier>classes</classifier>

  </dependency>

  <dependency>

   <groupId>com.blogshot.wittakarn.web</groupId>

   <artifactId>web-2</artifactId>

   <version>0.0.1-SNAPSHOT</version>

   <classifier>classes</classifier>

  </dependency>

 </dependencies>



</project>


The dependency hierarchy is shown below.

Dependency Hierachy of web-parent

After developers build root, web-1, web-2, and web-parent respectively. The result of war will show as below figure.

Then the wab-parent-0.0.1-SNAPSHOT.war is ready to deploy on JAVA EE web application server such as Weblogic 12c, Glassfish 3.1.2.2 and so on.

Friday, January 16, 2015

JSF and PrimeFaces: Time Counter For Session Timeout Handling

      In generally, Developers can define their web applications session timeout at the web.xml as following.

web.xml
 <session-config>
  <session-timeout>10</session-timeout>
 </session-config>

      For detection session expired in web application, developers can use HttpSessionListener to detect session's expiration immediately whenever the container invoke the HttpSessionListener#sessionDestroyed event.
The below example is applyed for detecting data in session before timeout.

/**
 *
 * @author Wittakarn
 */
public class SimpleSessionListener implements 
        HttpSessionListener, Serializable {

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session created : " + se.getSession().getId());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        UserSession user = (UserSession) session.getAttribute("userSession");

        if(user != null)
            System.out.println("login user :" + user.getUsername());
        
        /*Do some bussiness logic such as save audit logs to db, 
        save logout history, and so on.*/
        
        System.out.println("session destroyed :" + 
                session.getId() + " Logging out user...");
    }
}

      At last, definition session listener in web.xml.
<listener>
  <listener-class>
    com.wittakarn.listener.SimpleSessionListener
  </listener-class>
</listener>


      However, detection session expired by using HttpSessionListener cannot resolve some user's requirements. In the case of requirement, that require to popup dialog for notifing before session out. In this proposed, I would like to present Time Counter For Session Timeout Handling by applying JSF and primefaces 5.1.

index.xhtml

index.xhtml











<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <f:facet name="first">
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <meta name="viewport" 
                  content="user-scalable=no, 
                  width=device-width, 
                  initial-scale=1.0, 
                  maximum-scale=1.0"/>
            <meta name="apple-mobile-web-app-capable" content="yes" />
        </f:facet>

        <title>Login</title>
    </h:head>

    <h:body>
        <h:form>
            <p:outputLabel value="user"/>
            <p:inputText value="#{loginBean.user.username}"
                         required="true"/>
            <br/>
            <p:outputLabel value="password"/>
            <p:password value="#{loginBean.user.password}"
                        required="true"/>
            <br/>
            <p:commandButton value="Login" 
                             process="@form" 
                             update="@form"
                             action="#{loginBean.submit}"/>
        </h:form>
    </h:body>
</html>

display.xhtml

display.xhtml












<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <f:facet name="first">
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <meta name="viewport" 
                  content="user-scalable=no, 
                  width=device-width, 
                  initial-scale=1.0, 
                  maximum-scale=1.0"/>
            <meta name="apple-mobile-web-app-capable" content="yes" />
        </f:facet>

        <title>Display</title>
    </h:head>

    <h:body>
        <h:form>
            <p:outputLabel value="user "/>
            <p:outputLabel value="#{loginBean.user.username}"/>
            <p:outputLabel value=" login success..."/>

            <p:idleMonitor timeout="5000"
                           onidle="PF('alertExpire').show();">
            </p:idleMonitor>
        </h:form>

        <p:dialog header="Basic Dialog" 
                  widgetVar="alertExpire" 
                  onShow="startIdleMonitor()">
            <h:outputText value="Your browser session is expiring in " />
            <h:outputText id="dialog-countdown" style="font-weight: bold;"/>
            <h:outputText value=" second." />
            <br/>
            <h:outputText value="Do you want to continue your session?" />
            <h:form>
                <p:commandButton value="Yes" 
                                 process="@this" 
                                 update="@all"
                                 onclick="stopCount()"/>
                <p:commandButton value="No" 
                                 process="@this" 
                                 update="@all"
                                 action="#{loginBean.logout}"/>
                <p:remoteCommand name="logout" action="#{loginBean.logout}" />
            </h:form>
            <script type="text/javascript">
                var TIME = 10; // in seconds
                var countTimer = TIME;
                var processTimer;
                var timer_is_on = 0;

                var countDownDiv = "dialog-countdown";
                var txtCountDown = null;
                if (!txtCountDown)
                    txtCountDown = document.getElementById(countDownDiv);

                function startIdleMonitor() {
                    countTimer = TIME;
                    txtCountDown.innerHTML = countTimer;
                    doTimer();
                }
                function timedCount() {
                    txtCountDown.innerHTML = countTimer;
                    if (countTimer == 0) {
                        stopCount();
                        logout();
                        return;
                    }
                    countTimer = countTimer - 1;
                    processTimer = setTimeout("timedCount()", 1000);
                }
                function doTimer() {
                    if (!timer_is_on) {
                        timer_is_on = 1;
                        timedCount();
                    }
                }
                function stopCount() {
                    clearTimeout(processTimer);
                    timer_is_on = 0;
                }
            </script>
        </p:dialog>
    </h:body>
</html>

LoginBean.java
/**
 *
 * @author Wittakarn
 */
@ViewScoped
@ManagedBean(name = "loginBean")
public class LoginBean {
    @ManagedProperty(value = "#{userSession}")
    private UserSession user;

    public UserSession getUser() {
        return user;
    }

    public void setUser(UserSession user) {
        this.user = user;
    }
    
    public String submit(){
        return "display.xhtml";
    }
    
    public String logout(){
        FacesContext.getCurrentInstance()
                .getExternalContext()
                .invalidateSession();
        return "/index.xhtml?faces-redirect=true";
    }
}

UserSession.java
/**
 *
 * @author Wittakarn
 */
@SessionScoped
@ManagedBean(name = "userSession")
public class UserSession implements Serializable{
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


Tuesday, October 28, 2014

Maven Central Repository as proxy in Nexus manager.

In nexus console

Figure 1. Set Repository Central proxy point to http://repo1.maven.org/maven2/

Figure 2. Add Repository Central proxy into Group Repositories.

The Ordered Group Repositories is used to order repositories, which is applied to look up library respectively.

In settings.xml, create a nexus profile and also active a nexus profile.

<mirrors>
<mirror>
<!--This sends everything else to /public -->
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://172.17.2.212:8089/nexus/content/groups/public</url>
</mirror>
</mirrors>

<profiles>

<profile>
<id>nexus</id>
<!--Enable snapshots for the built in central repo to direct -->
<!--all requests to nexus via the mirror -->
<repositories>
<repository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
<repository>
<id>primeface-releases</id>
<url>http://172.17.2.212:8089/nexus/content/repositories/primeface-releases/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>primeface-releases</id>
<url>http://172.17.2.212:8089/nexus/content/repositories/primeface-releases/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
  </profiles>
  
  <activeProfiles>
<!--make the profile active all the time -->
<activeProfile>nexus</activeProfile>
  </activeProfiles>

Friday, October 3, 2014

Programatically add tabs and contents using PrimeFaces.

Example code for adding new tabs and contents dynamically.

XHTML
<h:form id="form">
    <p:tabView id="tabview"

               cache="false"

               activeIndex="0"

               dynamic="true"

               binding="#{tabbedView.tabView}">

        <p:tab title="a">

            <p:outputLabel value="aaa" />

        </p:tab>

        <p:tab title="b">

            <p:outputLabel value="bbb" />

        </p:tab>

        <p:tab title="c">

            <p:outputLabel value="ccc" />

        </p:tab>

    </p:tabView>

    <p:outputLabel value="dynamic content" />

    <p:inputText id="dynamicText" value="#{tabbedView.dynamicText}" />

    <p:commandButton value="add tab"

                     process="@this,dynamicText"

                     update="tabview"

                     actionListener="#{tabbedView.addNewTab}">

    </p:commandButton>
    
</h:form>


ManagedBean
@ManagedBean

public class TabbedView {

    private TabView tabView;

    private String dynamicText;

    @PostConstruct

    public void init() {

        tabView = new TabView();

    }

    public TabView getTabView() {

        return tabView;

    }

    public void setTabView(TabView tabView) {

        this.tabView = tabView;

    }

    public String getDynamicText() {

        return dynamicText;

    }

    public void setDynamicText(String dynamicText) {

        this.dynamicText = dynamicText;

    }


    public void addNewTab(ActionEvent event) {

        OutputLabel label = new OutputLabel();

        label.setValue(dynamicText);

        Tab newTab = new Tab();

        newTab.setTitle(UUID.randomUUID().toString());

        newTab.getChildren().add(label);

        tabView.getChildren().add(newTab);

    }

}

Thursday, October 2, 2014

Default p:selectOneRadio selection in Primefaces.

This is my demonstration which present how to default p:selectOneRadio value in p:dataTable
XHTML
<h:form>

    <p:dataTable var="catalog" value="#{radioView.catalogs}">

        <p:column headerText="City">

            <p:selectOneRadio id="city"

                              value="#{catalog.city}"

                              columns="3">

                <f:selectItems value="#{radioView.cities}"

                               var="c"

                               itemLabel="#{city}"

                               itemValue="#{city}"/>

            </p:selectOneRadio>

        </p:column>

    </p:dataTable>

    <p:commandButton value="changeSelection"

                     process="@form"

                     update="@form"

                     actionListener="#{radioView.changeSelection}"/>

    <p:commandButton value="submit"

                     process="@form"

                     update="@form"

                     actionListener="#{radioView.submit}"/>

</h:form>


ManagedBean
@ManagedBean
public class RadioView {

    private List<Catalog> catalogs;
    private List<String> cities;

    @PostConstruct
    public void init() {
        cities = new ArrayList<String>();
        cities.add("San Francisco");
        cities.add("London");
        cities.add("Paris");

        //default radio value
        Catalog c1 = new Catalog("San Francisco");
        Catalog c2 = new Catalog("London");
        Catalog c3 = new Catalog("Paris");
        Catalog c4 = new Catalog("London");

        catalogs = new ArrayList<Catalog>();
        catalogs.add(c1);
        catalogs.add(c2);
        catalogs.add(c3);
        catalogs.add(c4);
    }
    public List<Catalog> getCatalogs() {
        return catalogs;
    }
    public void setCatalogs(List<Catalog> catalogs) {
        this.catalogs = catalogs;
    }
    public List<String> getCities() {
        return cities;
    }
    public void changeSelection(ActionEvent event){
        for (Catalog catalog : catalogs) {
            catalog.setCity("San Francisco");
        }
    }
    public void submit(ActionEvent event) {
        for (Catalog catalog : catalogs) {
            System.out.println(catalog.getCity());
        }
    }
}


Domain
public class Catalog implements Serializable{

    private String city;
    public Catalog(String city){
        this.city = city;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}

Friday, September 19, 2014

Upgrading Mojarra in IBM WebSphere 8.5

  1. Add the Mojarra listener to the required web.xml file.
    <listener>
         <listener-class>
              com.sun.faces.config.ConfigureListener
         </listener-class>
    </listener>
    
  2. Add the Mojarra JSF implementation, which are javax.faces-2.1.24.jar, jsf-api-2.1.24.jar and jsf-impl-2.1.24.jar, to the Environment > Shared Libraries.
    - javax.faces-2.1.24.jar (GroupId: org.glassfish) from http://search.maven.org/#artifactdetails%7Corg.glassfish%7Cjavax.faces%7C2.1.24%7Cjar
    - jsf-api-2.1.24.jar (GroupId: com.sun.faces) from http://search.maven.org/#artifactdetails%7Ccom.sun.faces%7Cjsf-api%7C2.1.24%7Cjar
    - jsf-impl-2.1.24.jar (GroupId: com.sun.faces) from http://search.maven.org/#artifactdetails%7Ccom.sun.faces%7Cjsf-impl%7C2.1.24%7Cjar

    After configuration shared libraries, the Websphere server must be Restart.
  3. After deploy your project, at the Enterprise Applications > staaec-web-1_0_10-SNAPSHOT_war(your project) > Class loader. Class loader order must be select (Classes loaded with local class loader first (parent last))
  4. For reference the shared libraries to your project, you must go to Enterprise Applications > staaec-web-1_0_10-SNAPSHOT_war > Shared library references > Shared Library Mapping in order to map shared libraries to your project.
  5. After mapping, the shared libraries columns must display as image below.
  6. Start your application.

Decision tree(Data mining)

      We can take a unique service each customer by using customer relationship management (CRM) in order to discover his/her behavior in detail. In the CRM, Decision Trees can be used to classify existing customer records into customer segments. The process starts with data related whose behavior is already known; for example, customers who have responded to a promotional campaign and someone have not; C4.5 is an algorithm developed by Ross Quinlan that generates Decision Trees. This algorithm dealing with both continuous and discrete attributes, missing values and pruning trees after construction.

Algorithm C4.5(D)
Input: an attribute-value dataset D
1: Tree = {}
2: if D is "pure" OR other stopping criteria met then
3:     terminate
4:end if
5:for all attribute a Є D do
6:     Compute information-theoretic criteria if we split on a
7:end for
8:a_best = Best attribute according to above compute criteria
9:Tree = Create a decision node that test a_est in the root
10:D_v = Induce sub-datasets from D based on a_best
11:for all D_v do
12:     Tree_v = C4.5(D_v)
13:     Attach Tree_v to the correspoding branch of Tree
14:end for
15:return Tree

Example
GenderAgeSalaryStatusNumber of times travel per yearinterest travel promotion
or not
Male21-4030001-50000Single1-3No
Female21-4010001-30000Married4-5Yes
Female> 6010001-30000Single1-3No
Female41-6030001-50000Married4-5Yes
Male41-60> 50001Single1-3Yes
Female21-40> 50001Single1-3Yes
Male41-6030001-50000Married4-5Yes
Male> 6010001-30000Single1-3No
Female41-6010001-30000Married1-3No
Male> 6030001-50000Married1-3Yes

a) Entropy using the frequency table of one attribute:
Yes of interest is 6.
No is 4.
E(travel promotion) = E(4/10, 6/10) = - (0.4log20.4) - (0.6log20.6) = 0.971
E original = 0.971

b) Entropy using the frequency table of two attributes:
GenderYesNo
Male32
Female32
E(Interest, Gender) = P(Male)*E(3/5, 2/5) + P(Female)*E(3/5, 2/5)
= 5/10*(- 0.6log20.6 - 0.4log20.4)) + 5/10*(- 0.6log20.6 - 0.4log20.4)) = 0.971

c) Information gain formula.
Gain(Original, Gender) = 0.971 - 0.971 = 0

d) SplitINFO formula.
SplitINFO(Interest, Gender) = - (5/10log25/10) - (5/10log25/10) = 1

e) GainRaTIO formula.
GainRATIO(Interest, Gender) = 0/1 = 0


The C4.5 uses "Gain Ratio" measure which is Information Gain divided by SplitInfo. The Information Gain divided by SplitInfo is call GainRaTIO.

Step 1: Calculate entropy of the target.
Step 2: Split the dataset on the different attribute, and calculate GainRATIO of each attribute.
Step 3: Choose attribute that have largest GainRATIO as the decision node.
Step 4: The C4.5 algorithm is run recursively one the non-leaf branch, until all data is classified.

Step 1:
     E(travel promotion) = E(4/10, 6/10) = - (0.4log20.4) - (0.6log20.6) = 0.971
Step 2:

GenderYesNo
Male32
Female32
E(Interest, Gender) = P(Male)*E(3/5, 2/5) + P(Female)*E(3/5, 2/5)
= 5/10*(- 0.6log20.6 - 0.4log20.4) + 5/10*(- 0.6log20.6 - 0.4log20.4) = 0.971
Gain(Original, Gender) = 0.971 - 0.971 = 0
SplitINFO(Interest, Gender) = - (5/10log25/10) - (5/10log25/10) = 1
GainRATIO(Interest, Gender) = 0/1 = 0

AgeYesNo
21-4021
41-6031
> 6021
E(Interest, Age) = P(21-40)*E(2/3, 1/3) + P(41-60)*E(3/4, 1/4) + P(> 60)*E(2/3, 1/3)
= 3/10*0.9183 + 4/10*0.8113 + 3/10*0.9183 = 0.876
Gain(Original, Age) = 0.971 - 0.876 = 0.095
SplitINFO(Interest, Age) =- (3/10log23/10) - (4/10log24/10) - (3/10log23/10) = 1.571
GainRATIO(Interest, Age) = 0.095/1.571 = 0.06

SalaryYesNo
10001-3000013
30001-5000031
> 5000120
E(Interest, Age) = P(10001-30000)*E(1/4, 3/4) + P(30001-50000)*E(3/4, 1/4) + P(> 50001)*E(2/2, 0/2)
= 4/10*0.811 + 4/10*0.811 + 2/10*0 = 0.649
Gain(Original, Age) = 0.971 - 0.649 = 0.322
SplitINFO(Interest, Age) =- (4/10log24/10) - (4/10log24/10) - (2/10log22/10) = 2.376
GainRATIO(Interest, Age) = 0.322/2.376 = 0.136

StatusYesNo
Married41
Single23
E(Interest,Status) = P(Married)*E(4/5, 1/5) + P(Single)*E(2/5, 3/5)
= 5/10*0.722 + 5/10*0.971 = 0.847
Gain(Original,Status) = 0.971 - 0.847 = 0.124
SplitINFO(Interest,Status) =- (5/10log25/10) - (5/10log25/10) = 1
GainRATIO(Interest,Status) = 0.124/1 = 0.124

Number of times travel per yearYesNo
1-334
4-530
E(Interest, travel per year) = P(1-3)*E(3/7, 4/7) + P(4-5)*E(3/3, 0/3)
= 7/10*0.985 + 3/10*0 = 0.69
Gain(Original, travel per year) = 0.971 - 0.69 = 0.281
SplitINFO(Interest, travel per year) =- (7/10log27/10) - (3/10log23/10) = 0.881
GainRATIO(Interest, travel per year) = 0.281/0.881 = 0.319
     
Step 3: GainRATIO of Number of times travel per year is come to top, so number of times travel per year is the root node.

Step 4: Next we will interest only number of times travel per year that have 1-3 value.

GenderAgeSalaryStatusinterest travel promotion
or not
Male21-4030001-50000SingleNo
Female> 6010001-30000SingleNo
Male41-60> 50001SingleYes
Female21-40> 50001SingleYes
Male> 6010001-30000SingleNo
Female41-6010001-30000MarriedNo
Male> 6030001-50000MarriedYes
Step 5: Calculate GainRATIO that have travel per year = 1-3

E(Interest[1-3]) = E(3/7, 4/7) = - (3/7log23/7) - (4/7log24/7) = 0.985

GenderYesNo
Male22
Female21
E(Interest[1-3], Gender) = P(Male)*E(2/4, 2/4) + P(Female)*E(2/3, 1/3)
= 4/7*1 + 3/7*0.918 = 0.965
Gain(Original[1-3], Gender) = 0.985 - 0.965 = 0.02
SplitINFO(Interest[1-3], Gender) =- (4/7log24/7) - (3/7log23/7) = 0.985
GainRATIO(Interest[1-3], Gender) = 0.02/0.985 = 0.02

AgeYesNo
21-4011
41-6011
> 6012
E(Interest[1-3], Age) = P(21-40)*E(1/2, 1/2) + P(41-60)*E(1/2, 1/2) + P(> 60)*E(1/3, 2/3)
= 2/7*1 + 2/7*1 + 3/7*0.918 = 0.965
Gain(Original[1-3], Age) = 0.985 - 0.965 = 0.02
SplitINFO(Interest[1-3], Age) =- (2/7log22/7) - (2/7log22/7) - (3/7log23/7) = 1.556
GainRATIO(Interest[1-3], Age) = 0.02/1.556 = 0.013

SalaryYesNo
10001-3000003
30001-5000011
> 5000120
E(Interest[1-3], Salary) = P(10001-30000)*E(3/3, 0/3) + P(30001-50000)*E(1/2, 1/2) + P(> 50001)*E(2/2, 0/2)
= 3/7*0 + 2/7*1 + 2/7*0 = 0.286
Gain(Original[1-3],Salary) = 0.985 - 0.286 = 0.699
SplitINFO(Interest[1-3],Salary) =- (3/7log23/7) - (2/7log22/7) - (2/7log22/7) = 1.556
GainRATIO(Interest[1-3],Salary) = 0.699/1.556 = 0.449

StatusYesNo
Married11
Single23
E(Interest[1-3], Status) = P(Married)*E(1/2, 1/2) + P(Single)*E(2/5, 3/5)
= 2/7*1 + 5/7*0.971 = 0.979
Gain(Original[1-3], Status) = 0.985 - 0.979 = 0.006
SplitINFO(Interest[1-3], Status) =- (2/7log22/7) - (5/7log25/7) = 0.863
GainRATIO(Interest[1-3], Status) = 0.006/0.863 = 0.007

Step 6: GainRATIO of salary is come to top, so salary is the child node of travel per year.

Step 7: we will interest only number of times travel per year that have 1-3 value and salary = 30001-50000.

GenderAgeStatusinterest travel promotion
or not
Male21-40SingleNo
Male> 60MarriedYes
Step 8: Calculate GainRATIO that have travel per year = 1-3 and salary = 30001-50000.

E(Interest[1-3][30001-50000]) = E(1/2, 1/2) = - (1/2log21/2) - (1/2log21/2) = 1
GenderYesNo
Male11
Female00
E(Interest[1-3][30001-50000], Gender) = P(Male)*E(1/2, 1/2) + P(Female)*E(0, 0) = 2/2*1 + 0 = 1
Gain(Original[1-3][30001-50000], Gender) = 1 - 1 = 0
SplitINFO(Interest[1-3][30001-50000], Gender) =- (2/2log22/2) = 0
GainRATIO(Interest[1-3][30001-50000], Gender) = 0

AgeYesNo
21-4001
41-6000
> 6010
E(Interest[1-3][30001-50000], Age) = P(21-40)*E(0, 1) + P(41-60)*E(0, 0) + P(> 60)*E(1, 0) = 0
Gain(Original[1-3][30001-50000], Age) = 1 - 0 = 1
SplitINFO(Interest[1-3][30001-50000], Age) = - (1/2log22/7) - (1/2log21/2) = 1
GainRATIO(Interest[1-3][30001-50000], Age) = 1/1 = 1

StatusYesNo
Married10
Single01
E(Interest[1-3][30001-50000], Status) = P(Married)*E(0, 1) + P(Single)*E(1, 0) = 0
Gain(Original[1-3][30001-50000], Status) = 1 - 0 = 1
SplitINFO(Interest[1-3][30001-50000], Status) =- (1/2log21/2) - (1/2log21/2) = 1
GainRATIO(Interest[1-3][30001-50000], Status) = 1

GainRATIO both age and status is equal, so we can use either age or status to child node of salary, but we should select a node that have least number of type of attribute. Therefore we got a decision tree like this.



After We constructed a decision tree completely, we can use post pruning in order to discard unreliable parts.

f is the error on the training data.
N is the number of instances covered by the left.
z from normal distribution.
e is error estimate for a node.

If the error estimate at children node greater than parent node, we do not want to keep the children.

The advantages of the C4.5 are:
  • Builds models that can be easily interpreted and explained to executives.
  • Easy to implement because construct a decision tree only once that can handle test sets.
  • Can use both categorical and continuous values.
  • Deals with noise.
The disadvantages are:
  • Small variation in data can lead to different decision trees (especially when the variables are close to each other in value).
  • Does not work very well on a small training set.
  • If in the first time we use small training set to construct a tree, It can't handle a test set accurately.
Solution 1:
When test data has been predicted by decision tree, and get Yes in class interest travel promotion that means customer should have interest in a new promotion. We'll send e-mail to him/her. Since he/she rejected, the training set can't handle correctly. If number of rejection greater than threshold(incorrect predictions/correct prediction), we'll use test sets to become training sets combination with old training sets to construct a decision tree again for preparing to predict new test set.
Example
Since we define threshold=0.3, (incorrect predictions)/(correct prediction) is greater than 0.3, we'll construct decision tree immediately.

Solution 2:
Use C5.0 instead of C4.5. The advantages of C5.0
  • Multi-threaded: can take advantage of multiple CPUs or cores that can improve speed to construct a decision tree.
  • Boosting: that is a technique for generating and combining multiple classifiers to improve predictive accuracy.
  • Decision trees generally smaller.