upload to trustie

This commit is contained in:
qyzh1996 2020-10-26 15:41:48 +08:00
parent 1c00a71758
commit c3d8d1b23e
386 changed files with 22398 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

16
.idea/checkstyle-idea.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA">
<option name="configuration">
<map>
<entry key="checkstyle-version" value="8.23" />
<entry key="copy-libs" value="true" />
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
<entry key="scan-before-checkin" value="false" />
<entry key="scanscope" value="JavaOnly" />
<entry key="suppress-errors" value="false" />
</map>
</option>
</component>
</project>

View File

@ -0,0 +1,6 @@
This folder contains libraries copied from the "stream-data-processing" project.
It is managed by the CheckStyle-IDEA IDE plugin.
Do not modify this folder while the IDE is running.
When the IDE is stopped, you may delete this folder at any time. It will be recreated as needed.
In order to prevent the CheckStyle-IDEA IDE plugin from creating this folder,
uncheck the "Copy libraries from project directory" option in the CheckStyle-IDEA settings dialog.

View File

@ -0,0 +1,52 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<DBN-PSQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false" />
</DBN-PSQL>
<DBN-SQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false">
<option name="STATEMENT_SPACING" value="one_line" />
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
</formatting-settings>
</DBN-SQL>
<DBN-PSQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false" />
</DBN-PSQL>
<DBN-SQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false">
<option name="STATEMENT_SPACING" value="one_line" />
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
</formatting-settings>
</DBN-SQL>
</code_scheme>
</component>

13
.idea/compiler.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="stream-data-processing" />
</profile>
</annotationProcessing>
</component>
</project>

464
.idea/dbnavigator.xml Normal file
View File

@ -0,0 +1,464 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DataEditorManager">
<record-view-column-sorting-type value="BY_INDEX" />
<value-preview-text-wrapping value="true" />
<value-preview-pinned value="false" />
</component>
<component name="DBNavigator.Project.DataExportManager">
<export-instructions>
<create-header value="true" />
<quote-values-containing-separator value="true" />
<quote-all-values value="false" />
<value-separator value="" />
<file-name value="" />
<file-location value="" />
<scope value="GLOBAL" />
<destination value="FILE" />
<format value="EXCEL" />
<charset value="GBK" />
</export-instructions>
</component>
<component name="DBNavigator.Project.DatabaseBrowserManager">
<autoscroll-to-editor value="false" />
<autoscroll-from-editor value="true" />
<show-object-properties value="true" />
<loaded-nodes />
</component>
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.EditorStateManager">
<last-used-providers />
</component>
<component name="DBNavigator.Project.MethodExecutionManager">
<method-browser />
<execution-history>
<group-entries value="true" />
<execution-inputs />
</execution-history>
<argument-values-cache />
</component>
<component name="DBNavigator.Project.ObjectDependencyManager">
<last-used-dependency-type value="INCOMING" />
</component>
<component name="DBNavigator.Project.ObjectQuickFilterManager">
<last-used-operator value="EQUAL" />
<filters />
</component>
<component name="DBNavigator.Project.ScriptExecutionManager" clear-outputs="true">
<recently-used-interfaces />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="NESTED TABLE" enabled="false" />
<object-type name="COLUMN" enabled="false" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE ATTRIBUTE" enabled="false" />
<object-type name="ARGUMENT" enabled="false" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="true" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<tracking-columns>
<columnNames value="" />
<visible value="true" />
<editable value="false" />
</tracking-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="CSS" enabled="true" />
<content-type name="Java" enabled="true" />
<content-type name="JavaScript" enabled="true" />
<content-type name="JSP" enabled="true" />
<content-type name="JSPx" enabled="true" />
<content-type name="Groovy" enabled="true" />
<content-type name="FTL" enabled="true" />
<content-type name="TML" enabled="true" />
<content-type name="GSP" enabled="true" />
<content-type name="ASP" enabled="true" />
<content-type name="VTL" enabled="true" />
<content-type name="AIDL" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="Flex" enabled="true" />
<content-type name="Manifest" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
<debugger>
<debugger-type value="ASK" />
<use-generic-runners value="true" />
</debugger>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
<component name="DBNavigator.Project.StatementExecutionManager">
<execution-variables />
</component>
</project>

6
.idea/encodings.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8" addBOMForNewFiles="with NO BOM">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="GLPKSolverPack">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/GLPKSolverPack.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="SCPSolver">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/SCPSolver.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

17
.idea/misc.xml Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

BIN
lib/GLPKSolverPack.jar Normal file

Binary file not shown.

BIN
lib/SCPSolver.jar Normal file

Binary file not shown.

90
pom.xml Normal file
View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>edu.nju.ics.frontier</groupId>
<artifactId>stream-data-processing</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>edu.nju.ics.frontier</groupId>
<artifactId>common-io</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>nz.ac.waikato.cms.weka</groupId>
<artifactId>weka-stable</artifactId>
<version>3.8.3</version>
</dependency>
<dependency>
<groupId>nz.ac.waikato.cms.weka</groupId>
<artifactId>LibSVM</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<!-- use 2.9.1 for Java 7 projects -->
<version>3.16.1</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<forkMode>once</forkMode>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,13 @@
package edu.nju.ics.frontier.bean.action;
public abstract class Action {
protected String type;
public Action(String type) {
this.type = type;
}
public String getType() {
return type;
}
}

View File

@ -0,0 +1,450 @@
package edu.nju.ics.frontier.bean.action;
import edu.nju.ics.frontier.bean.intellij.ActionEvent;
import edu.nju.ics.frontier.bean.intellij.DebugEvent;
import edu.nju.ics.frontier.bean.intellij.ExecuteEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.*;
public class ExecuteAction extends IntervalAction {
/**
* [required] {@code id} is one of the values including {@code Run}, {@code Debug}, and {@code Coverage}
*/
private String id;
/**
* [required] {@code executorLifeCycle} including scheduled, starting, started,
* cancelled, terminating, terminated
*/
private List<ExecuteEvent> executorLifeCycle;
/**
* [optional] {@code executorAction} is actions triggered by developers to start/stop
* executing some task, such as left-single-clicking the 'Run...' button
*/
private List<ActionEvent> executorActions;
/**
* [optional] {@code debuggerLiftCycle} consists of states and events.
* states including STOPPED, RUNNING, WAITING_ATTACH, PAUSED, WAIT_EVALUATION, DISPOSED.
* events including ATTACHED, DETACHED, RESUME, STEP, PAUSE, REFRESH, CONTEXT, START_WAIT_ATTACH, DISPOSE, REFRESH_WITH_STACK, THREADS_REFRESH.
*/
private List<DebugEvent> debuggerLiftCycle;
/**
* [optional] actions to add, remove, and change break points.
* {@code breakPointEvents} is not used because break points can be changed at any time.
*/
// private List<DebugEvent> breakPointEvents;
/**
* [optional] actions done during debugging such as Step Over, Step In, Step Out, and so on.
* It worth noting that there are two special debugger actions 'Attach to Process...' and
* 'Attach Debugger to Android Process', which can convert the 'Run' task to the 'Debug' task.
*/
private List<ActionEvent> debuggerActions;
public ExecuteAction(List<ExecuteEvent> executorLifeCycle) {
this(executorLifeCycle.get(0).getIdField(),
executorLifeCycle.get(0).getTime(),
executorLifeCycle.get(executorLifeCycle.size() - 1).getTime(),
executorLifeCycle);
}
public ExecuteAction(String id, long begTime, long endTime,
List<ExecuteEvent> executorLifeCycle) {
this(id, begTime, endTime, executorLifeCycle,
null,
null,
null);
}
public ExecuteAction(String id, long begTime, long endTime,
List<ExecuteEvent> executorLifeCycle,
List<ActionEvent> executorActions,
List<DebugEvent> debuggerLiftCycle,
List<ActionEvent> debuggerActions) {
super("Execute", begTime, endTime);
this.id = id;
this.executorLifeCycle = executorLifeCycle;
this.executorActions = executorActions;
this.debuggerLiftCycle = debuggerLiftCycle;
this.debuggerActions = debuggerActions;
}
public String getId() {
return id;
}
public List<ExecuteEvent> getExecutorLifeCycle() {
return executorLifeCycle;
}
public List<ActionEvent> getExecutorActions() {
return executorActions;
}
public List<ActionEvent> getDebuggerActions() {
return debuggerActions;
}
public List<DebugEvent> getDebuggerLiftCycle() {
return debuggerLiftCycle;
}
public void setExecutorActions(List<ActionEvent> executorActions) {
this.executorActions = executorActions;
}
public void setDebuggerLiftCycle(List<DebugEvent> debuggerLiftCycle) {
this.debuggerLiftCycle = debuggerLiftCycle;
}
public void setDebuggerActions(List<ActionEvent> debuggerActions) {
this.debuggerActions = debuggerActions;
}
@Override
public String toString() {
return String.format("{id:%s,executorLifeCycle:%s,executorActions:%s,debuggerLifeCycle:%s,debuggerActions:%s}",
id,
(executorLifeCycle == null ? "null" : executorLifeCycle.toString()),
(executorActions == null ? "null" : executorActions.toString()),
(debuggerLiftCycle == null ? "null" : debuggerLiftCycle.toString()),
(debuggerActions == null ? "null" : debuggerActions.toString()));
}
/**
* construct execute actions from the given IntelliJ event sequence. An execute action
* consists an unique id and a life cycle of the corresponding executor, if the execute
* action represents a debug task, it also includes a life cycle of the corresponding
* debugger and several debug-related actions. In addition, an execute action is triggered
* by an executor action, which also be involved in an execute action.
* @param events the given IntelliJ event sequence
* @return the execute actions
*/
public static List<ExecuteAction> createExecuteActions(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<ExecuteEvent> executorLifeCycleEvents = new ArrayList<ExecuteEvent>();
List<DebugEvent> debuggerLifeCycleEvents = new ArrayList<DebugEvent>();
List<ActionEvent> executorActions = new ArrayList<ActionEvent>();
List<ActionEvent> debuggerActions = new ArrayList<ActionEvent>();
for (IntelliJEvent event : events) {
if (IntelliJEvent.isExecuteEvent(event)) {
ExecuteEvent executeEvent = IntelliJEvent.toExecuteEvent(event);
if (executeEvent.getIdField() != null) {
executorLifeCycleEvents.add(executeEvent);
}
} else if (IntelliJEvent.isDebugEvent(event)) {
DebugEvent debugEvent = IntelliJEvent.toDebugEvent(event);
if (debugEvent.isWhenContextChanged() &&
debugEvent.getProjectField() != null &&
debugEvent.getSessionField() != null) {
debuggerLifeCycleEvents.add(debugEvent);
}
} else if (IntelliJEvent.isActionEvent(event)) {
ActionEvent actionEvent = IntelliJEvent.toActionEvent(event);
String actionClass = actionEvent.getClassField();
// if (ActionCategory.EXECUTE_CATEGORY.match(actionClass)) {
// executorActions.add(actionEvent);
// } else if (ActionCategory.DEBUG_CATEGORY.match(actionClass)) {
// debuggerActions.add(actionEvent);
// }
}
}
List<List<ExecuteEvent>> executorLifeCycles = createExecutorLifeCycles(executorLifeCycleEvents);
List<List<DebugEvent>> debuggerLifeCycles = createDebuggerLifeCycles(debuggerLifeCycleEvents);
if (executorLifeCycles == null) {
return null;
}
List<ExecuteAction> executeActions = new ArrayList<ExecuteAction>();
for (List<ExecuteEvent> executorLifeCycle : executorLifeCycles) {
ExecuteAction executeAction = new ExecuteAction(executorLifeCycle);
executeActions.add(executeAction);
}
attachDebuggerLifeCycles(executeActions, debuggerLifeCycles);
attachExecutorActions(executeActions, executorActions);
attachDebuggerActions(executeActions, debuggerActions);
return executeActions;
}
private static List<List<ExecuteEvent>> createExecutorLifeCycles(List<ExecuteEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
Map<String, List<ExecuteEvent>> map = new HashMap<String, List<ExecuteEvent>>();
for (ExecuteEvent event : events) {
String key = event.getIdField();
List<ExecuteEvent> value = map.get(key);
if (value == null) {
value = new ArrayList<ExecuteEvent>();
}
value.add(event);
map.put(key, value);
}
List<List<ExecuteEvent>> result = new ArrayList<List<ExecuteEvent>>();
for (Map.Entry<String, List<ExecuteEvent>> entry : map.entrySet()) {
List<List<ExecuteEvent>> tmp = createSpecificExecutorLifeCycles(entry.getValue());
if (tmp != null) {
result.addAll(tmp);
}
}
if (result.isEmpty()) {
return null;
}
Collections.sort(result, new Comparator<List<ExecuteEvent>>() {
public int compare(List<ExecuteEvent> o1, List<ExecuteEvent> o2) {
long t1 = o1.get(0).getTime();
long t2 = o2.get(0).getTime();
return (int) (t1 - t2);
}
});
return result;
}
private static List<List<ExecuteEvent>> createSpecificExecutorLifeCycles(List<ExecuteEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<List<ExecuteEvent>> result = new ArrayList<List<ExecuteEvent>>();
List<ExecuteEvent> tmp = null;
for (ExecuteEvent event : events) {
if (event.isWhenScheduled()) {
if (tmp == null) {
tmp = new ArrayList<ExecuteEvent>();
tmp.add(event);
}
} else if (event.isWhenTerminated()) {
if (tmp != null) {
tmp.add(event);
result.add(tmp);
tmp = null;
}
} else {
if (tmp != null) {
tmp.add(event);
}
}
}
if (tmp != null) {
throw new IllegalArgumentException("[incomplete executor life cycle] " + tmp);
}
return result.isEmpty() ? null : result;
}
private static List<List<DebugEvent>> createDebuggerLifeCycles(List<DebugEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
Map<String, List<DebugEvent>> map = new HashMap<String, List<DebugEvent>>();
for (DebugEvent event : events) {
String project = event.getProjectField();
String session = event.getSessionField();
String key = project + "#" + session;
List<DebugEvent> value = map.get(key);
if (value == null) {
value = new ArrayList<DebugEvent>();
}
value.add(event);
map.put(key, value);
}
List<List<DebugEvent>> result = new ArrayList<List<DebugEvent>>();
for (Map.Entry<String, List<DebugEvent>> entry : map.entrySet()) {
List<List<DebugEvent>> tmp = createSpecificDebuggerLifeCycles(entry.getValue());
if (tmp != null) {
result.addAll(tmp);
}
}
if (result.isEmpty()) {
return null;
}
Collections.sort(result, new Comparator<List<DebugEvent>>() {
public int compare(List<DebugEvent> o1, List<DebugEvent> o2) {
long t1 = o1.get(0).getTime();
long t2 = o2.get(0).getTime();
return (int) (t1 - t2);
}
});
return result;
}
private static List<List<DebugEvent>> createSpecificDebuggerLifeCycles(List<DebugEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<List<DebugEvent>> result = new ArrayList<List<DebugEvent>>();
List<DebugEvent> tmp = null;
for (DebugEvent event : events) {
if (event.isWaitingAttachState() && event.isContextEvent()) {
if (tmp == null) {
tmp = new ArrayList<DebugEvent>();
tmp.add(event);
}
} else if (event.isDisposedState() && event.isDisposeEvent()) {
if (tmp != null) {
tmp.add(event);
result.add(tmp);
tmp = null;
}
} else {
if (tmp != null) {
tmp.add(event);
}
}
}
if (tmp != null) {
throw new IllegalArgumentException("[incomplete debugger life cycle] " + tmp);
}
return result.isEmpty() ? null : result;
}
private static void attachDebuggerLifeCycles(List<ExecuteAction> executeActions, List<List<DebugEvent>> debuggerLifeCycles) {
if (executeActions == null || executeActions.isEmpty()) {
return;
}
if (debuggerLifeCycles == null || debuggerLifeCycles.isEmpty()) {
return;
}
for (List<DebugEvent> debuggerLifeCycle : debuggerLifeCycles) {
long debuggerBegTime = debuggerLifeCycle.get(0).getTime();
ExecuteAction nearest = null;
long minDelta = Long.MAX_VALUE;
boolean isInvolved = false;
for (ExecuteAction executeAction : executeActions) {
long executorBegTime = executeAction.getBegTime();
long executorEndTime = executeAction.getEndTime();
if (debuggerBegTime >= executorBegTime && debuggerBegTime < executorEndTime) {
nearest = executeAction;
isInvolved = true;
break;
}
long delta = Math.abs(debuggerBegTime - executorBegTime);
if (delta < minDelta) {
minDelta = delta;
nearest = executeAction;
}
}
if (!"Debug".equals(nearest.getId())) {
throw new IllegalArgumentException("[attach debugger life cycle: class] executor:" + nearest +
", debugger:" + debuggerLifeCycle);
}
if (nearest.getDebuggerLiftCycle() != null) {
throw new IllegalArgumentException("[attach debugger life cycle: value] executor:" + nearest +
", oldDebugger:" + nearest.getDebuggerLiftCycle() +
", newDebugger:" + debuggerLifeCycle);
}
if (!isInvolved) {
if (minDelta > 1000) {
throw new IllegalArgumentException("[attach debugger life cycle: delta] executor:" + nearest +
", debugger:" + debuggerLifeCycle);
}
}
nearest.setDebuggerLiftCycle(debuggerLifeCycle);
}
}
private static void attachExecutorActions(List<ExecuteAction> executeActions, List<ActionEvent> executorActions) {
if (executeActions == null || executeActions.isEmpty()) {
return;
}
if (executorActions == null || executorActions.isEmpty()) {
return;
}
for (ActionEvent executorAction : executorActions) {
long actionTime = executorAction.getTime();
ExecuteAction nearest = null;
long minDelta = Long.MAX_VALUE;
for (ExecuteAction executeAction : executeActions) {
long executorBegTime = executeAction.getBegTime();
long executorEndTime = executeAction.getEndTime();
if (actionTime >= executorBegTime && actionTime < executorEndTime) {
nearest = executeAction;
break;
}
long delta = Math.abs(executorBegTime - actionTime);
if (delta < minDelta) {
minDelta = delta;
nearest = executeAction;
}
}
List<ActionEvent> container = nearest.getExecutorActions();
if (container == null) {
container = new ArrayList<ActionEvent>();
nearest.setExecutorActions(container);
}
container.add(executorAction);
}
}
private static void attachDebuggerActions(List<ExecuteAction> executeActions, List<ActionEvent> debuggerActions) {
if (executeActions == null || executeActions.isEmpty()) {
return;
}
if (debuggerActions == null || debuggerActions.isEmpty()) {
return;
}
for (ActionEvent debuggerAction : debuggerActions) {
long actionTime = debuggerAction.getTime();
ExecuteAction nearest = null;
long minDelta = Long.MAX_VALUE;
for (ExecuteAction executeAction : executeActions) {
long executorBegTime = executeAction.getBegTime();
long executorEndTime = executeAction.getEndTime();
if (actionTime >= executorBegTime && actionTime < executorEndTime) {
nearest = executeAction;
break;
}
long delta = Math.abs(executorBegTime - actionTime);
if (delta < minDelta) {
minDelta = delta;
nearest = executeAction;
}
}
List<ActionEvent> container = nearest.getDebuggerActions();
if (container == null) {
container = new ArrayList<ActionEvent>();
nearest.setDebuggerActions(container);
}
container.add(debuggerAction);
}
}
}

View File

@ -0,0 +1,279 @@
package edu.nju.ics.frontier.bean.action;
import edu.nju.ics.frontier.bean.intellij.CursorEvent;
import edu.nju.ics.frontier.bean.intellij.FileEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.List;
public class FileAction extends IntervalAction {
private String path;
private List<FileEvent> fileChangedTrajectory;
private List<CursorEvent> cursorChangedTrajectory;
public FileAction(String path, long begTime, long endTime,
List<FileEvent> fileChangedTrajectory,
List<CursorEvent> cursorChangedTrajectory) {
super("File", begTime, endTime);
this.path = path;
this.fileChangedTrajectory = fileChangedTrajectory;
this.cursorChangedTrajectory = cursorChangedTrajectory;
}
public String getPath() {
return path;
}
public List<FileEvent> getFileChangedTrajectory() {
return fileChangedTrajectory;
}
public List<CursorEvent> getCursorChangedTrajectory() {
return cursorChangedTrajectory;
}
public boolean isCursorChangedInSuccession(boolean isPrintCheckInfo) {
if (cursorChangedTrajectory == null || cursorChangedTrajectory.isEmpty()) {
return true;
}
String lastPoint = cursorChangedTrajectory.get(0).getNewPosField();
for (int i = 1, s = cursorChangedTrajectory.size(); i < s; i++) {
CursorEvent cursorEvent = cursorChangedTrajectory.get(i);
if (!lastPoint.equals(cursorEvent.getOldPosField())) {
if (isPrintCheckInfo) {
int begIndex = Math.max(0, i - 5);
int endIndex = Math.min(i + 4, cursorChangedTrajectory.size() - 1);
System.out.printf("[%d] %s [%d] {\n", begTime, path, cursorChangedTrajectory.size());
for (int j = begIndex; j < i; j++) {
CursorEvent ce = cursorChangedTrajectory.get(j);
System.out.printf(" [%d] %s -> %s\n", ce.getTime(), ce.getOldPosField(), ce.getNewPosField());
}
System.out.printf("* [%d] %s -> %s\n", cursorEvent.getTime(), cursorEvent.getOldPosField(), cursorEvent.getNewPosField());
for (int j = i + 1; j <= endIndex; j++) {
CursorEvent ce = cursorChangedTrajectory.get(j);
System.out.printf(" [%d] %s -> %s\n", ce.getTime(), ce.getOldPosField(), ce.getNewPosField());
}
System.out.println("}");
}
return false;
}
lastPoint = cursorEvent.getNewPosField();
}
return true;
}
public void printCursorChangedTrajectory() {
int size = cursorChangedTrajectory == null ? 0 : cursorChangedTrajectory.size();
System.out.printf("[%d] %s [%d] {\n", begTime, path, size);
if (!(cursorChangedTrajectory == null || cursorChangedTrajectory.isEmpty())) {
for (int i = 0, s = cursorChangedTrajectory.size(); i < s; i++) {
CursorEvent ce = cursorChangedTrajectory.get(i);
System.out.printf(" [%d] %s -> %s\n", ce.getTime(), ce.getOldPosField(), ce.getNewPosField());
}
}
System.out.println("}");
}
/**
* For the given event sequence, segmenting it into several file actions by
* splitting the sequence at each file selected point.
* @param events IntelliJ event sequence
* @return list of file actions
* @see FileAction#removeOutliers(List, int, int)
*/
public static List<FileAction> createFileActions(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<FileAction> fileActions = new ArrayList<FileAction>();
FileEvent splitPoint = null;
List<FileEvent> fileChangedEvents = null;
List<CursorEvent> cursorChangedEvents = null;
List<IntelliJEvent> terminalForFileAction = null;
for (IntelliJEvent event : events) {
if (IntelliJEvent.isFileEvent(event)) {
FileEvent fileEvent = IntelliJEvent.toFileEvent(event);
if (fileEvent.isWhenSelected()) {
// when file was selected. The new path must be not null and different from the old one.
String oldPath = fileEvent.getOldPathField();
String newPath = fileEvent.getNewPathField();
if (newPath == null || newPath.equals(oldPath)) {
continue;
}
if (splitPoint != null) {
String path = splitPoint.getNewPathField();
long begTime = splitPoint.getTime();
long endTime = fileEvent.getTime();
if (!terminalForFileAction.isEmpty()) {
long t = terminalForFileAction.get(0).getTime();
if (t < endTime) {
endTime = t;
}
}
removeOutliers(cursorChangedEvents);
FileAction fileAction = new FileAction(
path, begTime, endTime,
fileChangedEvents,
cursorChangedEvents);
fileActions.add(fileAction);
}
splitPoint = fileEvent;
fileChangedEvents = new ArrayList<FileEvent>();
cursorChangedEvents = new ArrayList<CursorEvent>();
terminalForFileAction = new ArrayList<IntelliJEvent>();
} else if (fileEvent.isWhenChanged()) {
// when file changed. The path must be same as the last split point.
if (splitPoint == null) {
continue;
}
if (splitPoint.getNewPathField().equals(fileEvent.getPathField())) {
fileChangedEvents.add(fileEvent);
}
} else if (fileEvent.isWhenClosed()) {
// when file closed. The last file closed event is used to determine the end time of the last file filter.
if (splitPoint == null) {
continue;
}
if (splitPoint.getNewPathField().equals(fileEvent.getPathField())) {
terminalForFileAction.add(fileEvent);
}
}
} else if (IntelliJEvent.isCursorEvent(event)) {
if (splitPoint == null) {
continue;
}
CursorEvent cursorEvent = IntelliJEvent.toCursorEvent(event);
if (cursorEvent.isWhenCursorChanged()) {
// when cursor changed. The editor must be 'UNTYPED' and the old position does not equals (0,0).
String oldPos = cursorEvent.getOldPosField();
String newPos = cursorEvent.getNewPosField();
if (cursorEvent.isUntypedEditor() && oldPos != null &&
newPos != null && (!"(0,0)".equals(oldPos))) {
cursorChangedEvents.add(cursorEvent);
}
}
}
}
if (splitPoint != null) {
String path = splitPoint.getNewPathField();
long begTime = splitPoint.getTime();
long endTime = events.get(events.size() - 1).getTime();
if (!terminalForFileAction.isEmpty()) {
long t = terminalForFileAction.get(0).getTime();
if (t < endTime) {
endTime = t;
}
}
removeOutliers(cursorChangedEvents);
FileAction fileAction = new FileAction(
path, begTime, endTime,
fileChangedEvents,
cursorChangedEvents);
fileActions.add(fileAction);
}
return fileActions.isEmpty() ? null : fileActions;
}
/**
* @see FileAction#removeOutliers(List, int, int)
* @param events
*/
private static void removeOutliers(List<CursorEvent> events) {
int maxDis = 5;
int minHop = Math.min(events.size(), Math.max(5, events.size() / 10));
removeOutliers(events, maxDis, minHop);
}
/**
* remove outliers in the cursor changed sequence.
* @param events IntelliJ event sequence
* @param maxDis for any two adjacent cursor changed events, the difference between
* the former's {@code newPos} and the latter's {@code oldPos} must be
* less than the given {@code maxDis}.
* @param minHop the continuous sub cursor changed sequence is valid
* if and only if its hops exceed {@code minHop}.
*/
private static void removeOutliers(List<CursorEvent> events, int maxDis, int minHop) {
if (events == null || events.isEmpty()) {
return;
}
List<List<CursorEvent>> traces = new ArrayList<List<CursorEvent>>();
List<CursorEvent> tmp = null;
for (CursorEvent event : events) {
if (tmp == null) {
tmp = new ArrayList<CursorEvent>();
tmp.add(event);
traces.add(tmp);
} else {
long[] lastNewPos = CursorEvent.getLineAndColumn(tmp.get(tmp.size() - 1).getNewPosField());
long lastNewLine = lastNewPos[0];
long[] currOldPos = CursorEvent.getLineAndColumn(event.getOldPosField());
long currOldLine = currOldPos[0];
if (Math.abs(currOldLine - lastNewLine) < maxDis) {
tmp.add(event);
} else {
tmp = new ArrayList<CursorEvent>();
tmp.add(event);
traces.add(tmp);
}
}
// if (traces.isEmpty()) {
// List<CursorEvent> trace = new ArrayList<CursorEvent>();
// trace.add(event);
// traces.add(trace);
// } else {
// long[] currOldPos = CursorEvent.getLineAndColumn(event.getOldPosField());
// long currOldLine = currOldPos[0];
// long currOldColumn = currOldPos[1];
//
// long minDelta = Long.MAX_VALUE;
// long minLineDelta = Long.MAX_VALUE;
// List<CursorEvent> pinnedTrace = null;
//
// for (List<CursorEvent> trace : traces) {
// long[] lastNewPos = CursorEvent.getLineAndColumn(trace.get(trace.size() - 1).getNewPosField());
// long lastNewLine = lastNewPos[0];
// long lastNewColumn = lastNewPos[1];
//
// long lineDelta = Math.abs(currOldLine - lastNewLine);
// long columnDelta = Math.abs(currOldColumn - lastNewColumn);
// long delta = lineDelta + columnDelta;
// if (delta < minDelta) {
// minDelta = delta;
// minLineDelta = lineDelta;
// pinnedTrace = trace;
// if (delta == 0) {
// break;
// }
// }
// }
//
// if (minLineDelta < maxDis) {
// pinnedTrace.add(event);
// } else {
// List<CursorEvent> trace = new ArrayList<CursorEvent>();
// trace.add(event);
// traces.add(trace);
// }
// }
}
for (List<CursorEvent> trace : traces) {
if (trace.size() < minHop) {
events.removeAll(trace);
}
}
}
}

View File

@ -0,0 +1,14 @@
package edu.nju.ics.frontier.bean.action;
public abstract class InstantAction extends Action {
protected long time;
public InstantAction(String type, long time) {
super(type);
this.time = time;
}
public long getTime() {
return time;
}
}

View File

@ -0,0 +1,20 @@
package edu.nju.ics.frontier.bean.action;
public abstract class IntervalAction extends Action {
protected long begTime;
protected long endTime;
public IntervalAction(String type, long begTime, long endTime) {
super(type);
this.begTime = begTime;
this.endTime = endTime;
}
public long getBegTime() {
return begTime;
}
public long getEndTime() {
return endTime;
}
}

View File

@ -0,0 +1,43 @@
package edu.nju.ics.frontier.bean.block;
import java.text.SimpleDateFormat;
import java.util.Date;
public abstract class Block {
protected String type;
protected long begTime;
protected long endTime;
public Block(String type, long begTime, long endTime) {
this.type = type;
this.begTime = begTime;
this.endTime = endTime;
}
public String getType() {
return type;
}
public long getBegTime() {
return begTime;
}
public long getEndTime() {
return endTime;
}
public long getDeltaTime() {
return endTime - begTime;
}
public abstract Block slice(long t1, long t2);
@Override
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return String.format("{type:%s,begTime:%s,endTime:%s}",
type,
sdf.format(new Date(begTime)),
sdf.format(new Date(endTime)));
}
}

View File

@ -0,0 +1,114 @@
package edu.nju.ics.frontier.bean.block;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class BlockChain {
public static BlockChain createBlockChain(List<IntelliJEvent> events, long shortThreshold, long longThreshold) {
if (events == null || events.isEmpty()) {
return null;
}
List<ProjectBlock> projectBlocks = ProjectBlock.createProjectBlocks(events);
List<FileBlock> fileBlocks = FileBlock.createFileBlocks(events);
List<EditorBlock> editorBlocks = EditorBlock.createEditorBlocks(events);
List<IdleBlock> idleBlocks = IdleBlock.createIdleBlocks(events, shortThreshold);
List<OutsideBlock> outsideBlocks = OutsideBlock.createOutsideBlocks(events);
List<? extends Block> fileEditorBlocks = linkBlocks(editorBlocks, fileBlocks);
List<? extends Block> fileEditorIdleBlocks = linkBlocks(idleBlocks, fileEditorBlocks);
List<? extends Block> fileEditorIdleOutsideBlocks = linkBlocks(outsideBlocks, fileEditorIdleBlocks);
List<IdleBlock> longIdleBlocks = null;
if (!(idleBlocks == null || idleBlocks.isEmpty())) {
longIdleBlocks = new ArrayList<IdleBlock>();
for (IdleBlock idleBlock : idleBlocks) {
if ((idleBlock.getEndTime() - idleBlock.getBegTime()) >= longThreshold) {
longIdleBlocks.add(idleBlock);
}
}
if (longIdleBlocks.isEmpty()) {
longIdleBlocks = null;
}
}
List<? extends Block> projectLongIdleBlocks = linkBlocks(longIdleBlocks, projectBlocks);
if (projectLongIdleBlocks != null) {
projectBlocks = new ArrayList<ProjectBlock>();
for (Block projectLongIdleBlock : projectLongIdleBlocks) {
if (projectLongIdleBlock instanceof ProjectBlock) {
projectBlocks.add((ProjectBlock) projectLongIdleBlock);
}
}
if (projectBlocks.isEmpty()) {
projectBlocks = null;
}
}
// TODO
return null;
}
public static <A extends Block, B extends Block> List<? extends Block> linkBlocks(
List<A> aboveBlocks, List<B> belowBlocks) {
if (aboveBlocks == null && belowBlocks == null) {
return null;
} else if (aboveBlocks == null) {
return belowBlocks;
} else if (belowBlocks == null) {
return aboveBlocks;
}
List<Block> result = new ArrayList<Block>();
LinkedList<Block> copiedAboveBlocks = new LinkedList<Block>(aboveBlocks);
LinkedList<Block> copiedBelowBlocks = new LinkedList<Block>(belowBlocks);
while (!(copiedAboveBlocks.isEmpty() || copiedBelowBlocks.isEmpty())) {
Block aboveBlock = copiedAboveBlocks.removeFirst();
Block belowBlock = copiedBelowBlocks.removeFirst();
if (aboveBlock.getBegTime() <= belowBlock.getBegTime()) {
result.add(aboveBlock);
if (aboveBlock.getEndTime() < belowBlock.getEndTime()) {
copiedBelowBlocks.addFirst(belowBlock.slice(aboveBlock.getEndTime(), belowBlock.getEndTime()));
} else {
while (!copiedBelowBlocks.isEmpty()) {
belowBlock = copiedBelowBlocks.removeFirst();
if (belowBlock.getEndTime() <= aboveBlock.getEndTime()) {
continue;
}
if (belowBlock.getBegTime() >= aboveBlock.getEndTime()) {
copiedBelowBlocks.addFirst(belowBlock);
break;
}
copiedBelowBlocks.addFirst(belowBlock.slice(aboveBlock.getEndTime(), belowBlock.getEndTime()));
break;
}
}
} else {
if (belowBlock.getEndTime() <= aboveBlock.getBegTime()) {
result.add(belowBlock);
} else {
result.add(belowBlock.slice(belowBlock.getBegTime(), aboveBlock.getBegTime()));
if (belowBlock.getEndTime() > aboveBlock.getEndTime()) {
copiedBelowBlocks.addFirst(belowBlock.slice(aboveBlock.getEndTime(), belowBlock.getEndTime()));
}
}
copiedAboveBlocks.addFirst(aboveBlock);
}
}
if (!copiedAboveBlocks.isEmpty()) {
result.addAll(copiedAboveBlocks);
}
if (!copiedBelowBlocks.isEmpty()) {
result.addAll(copiedBelowBlocks);
}
return result;
}
}

View File

@ -0,0 +1,237 @@
package edu.nju.ics.frontier.bean.block;
import edu.nju.ics.frontier.bean.intellij.CursorEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.List;
public class EditorBlock extends Block {
protected List<CursorEvent> cursorChangedTrajectory;
public EditorBlock(String type, long begTime, long endTime, List<CursorEvent> cursorChangedTrajectory) {
super(type, begTime, endTime);
this.cursorChangedTrajectory = cursorChangedTrajectory;
}
public List<CursorEvent> getCursorChangedTrajectory() {
return cursorChangedTrajectory;
}
@Override
public EditorBlock slice(long t1, long t2) {
if (begTime <= t1 && t1 < t2 && t2 <= endTime) {
List<CursorEvent> slicedCursorEvents = EditorBlock.copyEvents(cursorChangedTrajectory, t1, t2);
return new EditorBlock(type, t1, t2, slicedCursorEvents);
} else {
return null;
}
}
public static <E extends IntelliJEvent> List<E> copyEvents(List<E> src, long t1, long t2) {
if (src == null || src.isEmpty()) {
return null;
}
List<E> tmp = new ArrayList<E>();
for (E e : src) {
long t = e.getTime();
if (t >= t1 && t < t2) {
tmp.add(e);
} else if (t >= t2) {
break;
}
}
return tmp.isEmpty() ? null : tmp;
}
public static List<EditorBlock> createEditorBlocks(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<EditorBlock> editorBlocks = new ArrayList<EditorBlock>();
String lastEditor = null;
List<CursorEvent> cursorChangedEvents = null;
for (IntelliJEvent event : events) {
if (!IntelliJEvent.isCursorEvent(event)) {
continue;
}
CursorEvent cursorEvent = IntelliJEvent.toCursorEvent(event);
if (!cursorEvent.isWhenCursorChanged()) {
continue;
}
String editor = cursorEvent.getEditorField();
if (lastEditor == null) {
if (!"UNTYPED".equals(editor)) {
lastEditor = editor;
cursorChangedEvents = new ArrayList<CursorEvent>();
cursorChangedEvents.add(cursorEvent);
}
} else {
if (lastEditor.equals(editor)) {
cursorChangedEvents.add(cursorEvent);
} else {
removeOutliers(cursorChangedEvents);
if (!cursorChangedEvents.isEmpty()) {
editorBlocks.add(new EditorBlock(
lastEditor,
cursorChangedEvents.get(0).getTime(),
cursorChangedEvents.get(cursorChangedEvents.size() - 1).getTime(),
cursorChangedEvents));
}
if (!"UNTYPED".equals(editor)) {
lastEditor = editor;
cursorChangedEvents = new ArrayList<CursorEvent>();
cursorChangedEvents.add(cursorEvent);
} else {
lastEditor = null;
cursorChangedEvents = null;
}
}
}
}
if (lastEditor != null) {
removeOutliers(cursorChangedEvents);
if (!cursorChangedEvents.isEmpty()) {
editorBlocks.add(new EditorBlock(
lastEditor,
cursorChangedEvents.get(0).getTime(),
cursorChangedEvents.get(cursorChangedEvents.size() - 1).getTime(),
cursorChangedEvents));
}
}
return editorBlocks.isEmpty() ? null : editorBlocks;
}
/**
* @see EditorBlock#removeOutliers(List, int, int)
* @param events
*/
public static void removeOutliers(List<CursorEvent> events) {
int maxDis = 5;
int minHop = Math.min(events.size(), Math.max(5, events.size() / 10));
removeOutliers(events, maxDis, minHop);
}
/**
* remove outliers in the cursor changed sequence.
* @param events IntelliJ event sequence
* @param maxDis for any two adjacent cursor changed events, the difference between
* the former's {@code newPos} and the latter's {@code oldPos} must be
* less than the given {@code maxDis}.
* @param minHop the continuous sub cursor changed sequence is valid
* if and only if its hops exceed {@code minHop}.
*/
public static void removeOutliers(List<CursorEvent> events, int maxDis, int minHop) {
if (events == null || events.isEmpty()) {
return;
}
List<List<CursorEvent>> traces = new ArrayList<List<CursorEvent>>();
List<CursorEvent> tmp = null;
for (CursorEvent event : events) {
if (tmp == null) {
tmp = new ArrayList<CursorEvent>();
tmp.add(event);
traces.add(tmp);
} else {
long[] lastNewPos = CursorEvent.getLineAndColumn(tmp.get(tmp.size() - 1).getNewPosField());
long lastNewLine = lastNewPos[0];
long[] currOldPos = CursorEvent.getLineAndColumn(event.getOldPosField());
long currOldLine = currOldPos[0];
if (Math.abs(currOldLine - lastNewLine) < maxDis) {
tmp.add(event);
} else {
tmp = new ArrayList<CursorEvent>();
tmp.add(event);
traces.add(tmp);
}
}
}
for (List<CursorEvent> trace : traces) {
if (trace.size() < minHop) {
events.removeAll(trace);
}
}
}
/**
* sampling {@code events} in a fixed {@code frequency}. The parameter {@code lineParser}
* is an interface whose function is parsing the line in the event.
* @param events file or cursor events
* @param frequency sampling frequency
* @param lineParser instance that implements the {@code LineParser} interface
* @param <E> FileEvent or CursorEvent
* @return samples
*/
public static <E extends IntelliJEvent> long[][] samplingLines(
List<E> events, long frequency, LineParser<E> lineParser) {
if (events == null || events.isEmpty()) {
return null;
}
long begTime = aligning(events.get(0).getTime(), frequency);
long endTime = aligning(events.get(events.size() - 1).getTime(), frequency);
int sampleNum = (int) ((endTime - begTime) / frequency) + 1;
long[][] result = new long[sampleNum][2];
// initialize
result[0][0] = begTime;
result[0][1] = -1;
for (int i = 1; i < sampleNum; i++) {
result[i][0] = result[i - 1][0] + frequency;
result[i][1] = -1;
}
// assignment
for (int i = events.size() - 1; i >= 0; i--) {
E event = events.get(i);
long time = aligning(event.getTime(), frequency);
int index = (int) ((time - begTime) / frequency);
if (result[index][1] == -1) {
result[index][1] = lineParser.parse(event);
}
}
return result;
}
/**
* aligning the time to the sampling point. Following below steps:
* 1) calculating the quotient and remainder of {@code time} divided by {@code frequency};
* 2) return {@code (quotient + 1) * frequency} if {@code remainder >= frequency / 2},
* else return {@code quotient * frequency}.
* @param time time (millisecond)
* @param frequency sampling frequency (millisecond)
* @return aligned time
*/
private static long aligning(long time, long frequency) {
long q = time / frequency;
long r = time % frequency;
long h = frequency / 2;
if (r >= h) {
return (q + 1) * frequency;
} else {
return q * frequency;
}
}
public interface LineParser<E> {
long parse(E e);
}
}

View File

@ -0,0 +1,251 @@
package edu.nju.ics.frontier.bean.block;
import edu.nju.ics.frontier.bean.intellij.CursorEvent;
import edu.nju.ics.frontier.bean.intellij.FileEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.List;
public class FileBlock extends EditorBlock {
private String path;
// private long[][] samples;
private List<FileEvent> fileChangedTrajectory;
// private List<CursorEvent> cursorChangedTrajectory;
public FileBlock(long begTime, long endTime, String path,
// long[][] samples,
List<FileEvent> fileChangedTrajectory,
List<CursorEvent> cursorChangedTrajectory) {
super("File", begTime, endTime, cursorChangedTrajectory);
this.path = path;
// this.samples = samples;
this.fileChangedTrajectory = fileChangedTrajectory;
// this.cursorChangedTrajectory = cursorChangedTrajectory;
}
public String getPath() {
return path;
}
public List<FileEvent> getFileChangedTrajectory() {
return fileChangedTrajectory;
}
public long[][] getSamples() {
return samplingLines(fileChangedTrajectory, cursorChangedTrajectory, 10000);
}
@Override
public FileBlock slice(long t1, long t2) {
if (begTime <= t1 && t1 < t2 && t2 <= endTime) {
List<FileEvent> slicedFileEvents = copyEvents(fileChangedTrajectory, t1, t2);
List<CursorEvent> slicedCursorEvents = copyEvents(cursorChangedTrajectory, t1, t2);
return new FileBlock(t1, t2, path, slicedFileEvents, slicedCursorEvents);
} else {
return null;
}
}
/**
* For the given event sequence, segmenting it into several file blocks by
* splitting the sequence at each file selected point.
* @param events IntelliJ event sequence
* @return list of file blocks
*/
public static List<FileBlock> createFileBlocks(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<FileBlock> fileBlocks = new ArrayList<FileBlock>();
FileEvent splitPoint = null;
List<FileEvent> fileChangedEvents = null;
List<CursorEvent> cursorChangedEvents = null;
List<IntelliJEvent> terminalForFileBlock = null;
for (IntelliJEvent event : events) {
if (IntelliJEvent.isFileEvent(event)) {
FileEvent fileEvent = IntelliJEvent.toFileEvent(event);
if (fileEvent.isWhenSelected()) {
// when file was selected. The new path must be not null and different from the old one.
String oldPath = fileEvent.getOldPathField();
String newPath = fileEvent.getNewPathField();
if (newPath == null || newPath.equals(oldPath)) {
continue;
}
if (splitPoint != null) {
String path = splitPoint.getNewPathField();
long begTime = splitPoint.getTime();
long endTime = fileEvent.getTime();
if (!terminalForFileBlock.isEmpty()) {
long t = terminalForFileBlock.get(0).getTime();
if (t < endTime) {
endTime = t;
}
}
removeOutliers(cursorChangedEvents);
FileBlock fileBlock = new FileBlock(
begTime, endTime, path,
// samplingLines(fileChangedEvents, cursorChangedEvents, 10000),
fileChangedEvents,
cursorChangedEvents);
fileBlocks.add(fileBlock);
}
splitPoint = fileEvent;
fileChangedEvents = new ArrayList<FileEvent>();
cursorChangedEvents = new ArrayList<CursorEvent>();
terminalForFileBlock = new ArrayList<IntelliJEvent>();
} else if (fileEvent.isWhenChanged()) {
// when file changed. The path must be same as the last split point.
if (splitPoint == null) {
continue;
}
if (splitPoint.getNewPathField().equals(fileEvent.getPathField())) {
fileChangedEvents.add(fileEvent);
}
} else if (fileEvent.isWhenClosed()) {
// when file closed. The last file closed event is used to determine the end time of the last file filter.
if (splitPoint == null) {
continue;
}
if (splitPoint.getNewPathField().equals(fileEvent.getPathField())) {
terminalForFileBlock.add(fileEvent);
}
}
} else if (IntelliJEvent.isCursorEvent(event)) {
if (splitPoint == null) {
continue;
}
CursorEvent cursorEvent = IntelliJEvent.toCursorEvent(event);
if (cursorEvent.isWhenCursorChanged()) {
// when cursor changed. The editor must be 'UNTYPED' and the old position does not equals (0,0).
String oldPos = cursorEvent.getOldPosField();
String newPos = cursorEvent.getNewPosField();
if (cursorEvent.isUntypedEditor() && oldPos != null &&
newPos != null && (!"(0,0)".equals(oldPos))) {
cursorChangedEvents.add(cursorEvent);
}
}
}
}
if (splitPoint != null) {
String path = splitPoint.getNewPathField();
long begTime = splitPoint.getTime();
long endTime = events.get(events.size() - 1).getTime();
if (!terminalForFileBlock.isEmpty()) {
long t = terminalForFileBlock.get(0).getTime();
if (t < endTime) {
endTime = t;
}
}
removeOutliers(cursorChangedEvents);
FileBlock fileBlock = new FileBlock(
begTime, endTime, path,
// samplingLines(fileChangedEvents, cursorChangedEvents, 10000),
fileChangedEvents,
cursorChangedEvents);
fileBlocks.add(fileBlock);
}
return fileBlocks.isEmpty() ? null : fileBlocks;
}
/**
* sampling the Lines of Code and Line of Cursor in a fixed frequency.
* @param fileEvents file events
* @param cursorEvents cursor events
* @param frequency sampling frequency
* @return samples
*/
private static long[][] samplingLines(List<FileEvent> fileEvents, List<CursorEvent> cursorEvents, long frequency) {
long[][] linesOfCode = EditorBlock.samplingLines(fileEvents, frequency, new EditorBlock.LineParser<FileEvent>() {
public long parse(FileEvent event) {
return Long.parseLong(event.getLineField());
}
});
long[][] lineOfCursor = EditorBlock.samplingLines(cursorEvents, frequency, new EditorBlock.LineParser<CursorEvent>() {
public long parse(CursorEvent event) {
String pos = event.getNewPosField();
String line = pos.substring(1, pos.indexOf(","));
return Long.parseLong(line);
}
});
long begTime = -1;
long endTime = -1;
if (linesOfCode != null) {
begTime = linesOfCode[0][0];
endTime = linesOfCode[linesOfCode.length - 1][0];
}
if (lineOfCursor != null) {
begTime = (begTime == -1) ? lineOfCursor[0][0] : Math.min(begTime, lineOfCursor[0][0]);
endTime = (endTime == -1) ? lineOfCursor[lineOfCursor.length - 1][0] : Math.max(endTime, lineOfCursor[lineOfCursor.length - 1][0]);
}
if (begTime == -1 || endTime == -1) {
return null;
}
int sampleNum = (int) ((endTime - begTime) / frequency) + 1;
long[][] buffer = new long[sampleNum][3];
// initialize
buffer[0][0] = begTime;
buffer[0][1] = -1;
buffer[0][2] = -1;
for (int i = 1; i < sampleNum; i++) {
buffer[i][0] = buffer[i - 1][0] + frequency;
buffer[i][1] = -1;
buffer[i][2] = -1;
}
// placing
placing(buffer, linesOfCode, 1);
placing(buffer, lineOfCursor, 2);
List<long[]> tmp = new ArrayList<long[]>();
for (int i = 0; i < sampleNum; i++) {
if ((buffer[i][1] != -1) || (buffer[i][2] != -1)) {
tmp.add(buffer[i]);
}
}
long[][] result = new long[tmp.size()][];
for (int i = 0, s = tmp.size(); i < s; i++) {
result[i] = tmp.get(i);
}
return result;
}
/**
* placing the Lines of Code/Cursor into a common array.
* @param dest array to store the value of Lines of Code/Cursor.
* @param src array of Lines of Code/Cursor
* @param column column of {@code dest} to store {@code src}.
*/
private static void placing(long[][] dest, long[][] src, int column) {
if (src == null || src.length <= 0) {
return;
}
int offset = -1;
for (int i = 0, l = dest.length; i < l; i++) {
if (dest[i][0] == src[0][0]) {
offset = i;
break;
}
}
if (offset == -1) {
return;
}
for (int i = 0, l = src.length; i < l; i++) {
dest[i + offset][column] = src[i][1];
}
}
}

View File

@ -0,0 +1,43 @@
package edu.nju.ics.frontier.bean.block;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.List;
public class IdleBlock extends Block {
public IdleBlock(long begTime, long endTime) {
super("Idle", begTime, endTime);
}
@Override
public IdleBlock slice(long t1, long t2) {
if (begTime <= t1 && t1 < t2 && t2 <= endTime) {
return new IdleBlock(t1, t2);
} else {
return null;
}
}
public static List<IdleBlock> createIdleBlocks(List<IntelliJEvent> events, long threshold) {
if (events == null || events.isEmpty()) {
return null;
}
List<IdleBlock> idleBlocks = new ArrayList<IdleBlock>();
IntelliJEvent lastEvent = null;
for (IntelliJEvent currEvent : events) {
if (lastEvent != null) {
long lastTime = lastEvent.getTime();
long currTime = currEvent.getTime();
if ((currTime - lastTime) >= threshold) {
idleBlocks.add(new IdleBlock(lastTime, currTime));
}
}
lastEvent = currEvent;
}
return idleBlocks.isEmpty() ? null : idleBlocks;
}
}

View File

@ -0,0 +1,61 @@
package edu.nju.ics.frontier.bean.block;
import edu.nju.ics.frontier.bean.intellij.AppEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.List;
public class OutsideBlock extends Block {
public OutsideBlock(long begTime, long endTime) {
super("Outside", begTime, endTime);
}
@Override
public OutsideBlock slice(long t1, long t2) {
if (begTime <= t1 && t1 < t2 && t2 <= endTime) {
return new OutsideBlock(t1, t2);
} else {
return null;
}
}
public static List<OutsideBlock> createOutsideBlocks(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<Long> tmp = new ArrayList<Long>();
for (IntelliJEvent event : events) {
if (IntelliJEvent.isAppEvent(event)) {
AppEvent appEvent = IntelliJEvent.toAppEvent(event);
if (appEvent.isWhenDeactivated()) {
if (tmp.size() % 2 == 0) {
tmp.add(appEvent.getTime());
}
} else if (appEvent.isWhenActivated()) {
if (tmp.size() % 2 == 1) {
tmp.add(appEvent.getTime());
}
}
}
}
if (tmp.size() % 2 == 1) {
tmp.remove(tmp.size() - 1);
}
if (tmp.isEmpty()) {
return null;
}
List<OutsideBlock> outsideBlocks = new ArrayList<OutsideBlock>();
for (int i = 0, s = tmp.size() - 1; i < s; i++) {
long begTime = tmp.get(i);
long endTime = tmp.get(i + 1);
outsideBlocks.add(new OutsideBlock(begTime, endTime));
}
return outsideBlocks;
}
}

View File

@ -0,0 +1,260 @@
package edu.nju.ics.frontier.bean.block;
import edu.nju.ics.frontier.bean.intellij.ProjectEvent;
import edu.nju.ics.frontier.bean.intellij.WindowEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.*;
public class ProjectBlock extends Block {
private String projectName;
private String frameName;
public ProjectBlock(long begTime, long endTime, String projectName, String frameName) {
super("Project", begTime, endTime);
this.projectName = projectName;
this.frameName = frameName;
}
public String getProjectName() {
return projectName;
}
public String getFrameName() {
return frameName;
}
@Override
public ProjectBlock slice(long t1, long t2) {
if (begTime <= t1 && t1 < t2 && t2 <= endTime) {
return new ProjectBlock(t1, t2, projectName, frameName);
} else {
return null;
}
}
@Override
public String toString() {
return String.format("{begTime:%d,endTime:%d,projectName:%s,frameName:%s}",
begTime, endTime, projectName, frameName);
}
public static List<ProjectBlock> createProjectBlocks(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
// find out all project events. For such opened event with unknown 'frame' field,
// just try to temperately scan the subsequent window events and select out the
// opened event with a clear 'frame' field, further assigning the window's 'frame'
// field to the project event.
// Noting that an IllegalArgumentException will be thrown out if there is no suitable
// opened window event found in limited step.
List<ProjectEvent> projectEvents = new ArrayList<ProjectEvent>();
List<WindowEvent> windowEvents = new ArrayList<WindowEvent>();
for (int i = 0, s = events.size(); i < s; i++) {
IntelliJEvent event = events.get(i);
if (IntelliJEvent.isProjectEvent(event)) {
ProjectEvent projectEvent = IntelliJEvent.toProjectEvent(event);
if (projectEvent.isWhenOpened() && projectEvent.getFrameField() == null) {
boolean isFrameAttached = false;
for (int j = i + 1; j < s; j++) {
IntelliJEvent e = events.get(j);
if (!IntelliJEvent.isWindowEvent(e)) {
continue;
}
WindowEvent windowEvent = IntelliJEvent.toWindowEvent(e);
if (!windowEvent.isWhenWindowOpened()) {
continue;
}
String frame = windowEvent.getThisWindowName();
if (frame != null && frame.startsWith("frame")) {
projectEvent.addExtraData("frame", frame);
isFrameAttached = true;
break;
}
}
if (!isFrameAttached) {
throw new IllegalArgumentException("project " + projectEvent + " does not attach any frame!");
}
}
projectEvents.add(projectEvent);
} else if (IntelliJEvent.isWindowEvent(event)) {
WindowEvent windowEvent = IntelliJEvent.toWindowEvent(event);
if (windowEvent.isWhenWindowActivated()) {
String selfName = windowEvent.getThisWindowName();
if (selfName != null && selfName.startsWith("frame")) {
windowEvents.add(windowEvent);
}
}
}
}
if (projectEvents.isEmpty() || windowEvents.isEmpty()) {
return null;
}
// try to transform window events to project events. More specifically,
// for such window event whose 'this' field matches the 'frame[0-9]+,[A-Z]+' regex,
// and occurred when window activated:
// 1) trying to find the nearest opened project event with the same 'frame' field.
// If found, jump to 3);
// 2) trying to find the nearest closed project event with the same 'frame' field.
// If not found, jump to 5);
// 3) transforming the window event to the project event in following rule:
// a) the project's 'when' field is same as the window's;
// b) the project's 'name' field is same as the one that found in 1) or 2);
// c) the project's 'frame' field is same as the one that found in 1) or 2).
// 4) Jumping to 1) if there are remain window events, else go to 6);
// 5) throwing out an IllegalArgumentException to warn there is a lonely window event;
// 6) terminating.
List<ProjectEvent> transforms = new ArrayList<ProjectEvent>();
for (WindowEvent windowEvent : windowEvents) {
long winTime = windowEvent.getTime();
String winFrame = windowEvent.getThisWindowName();
ProjectEvent nearestOpened = null;
ProjectEvent nearestClosed = null;
for (ProjectEvent projectEvent : projectEvents) {
String proFrame = projectEvent.getFrameField();
if (!proFrame.equals(winFrame)) {
continue;
}
long proTime = projectEvent.getTime();
if (proTime <= winTime) {
if (projectEvent.isWhenOpened()) {
nearestOpened = projectEvent;
} else {
nearestClosed = projectEvent;
}
} else {
if (projectEvent.isWhenOpened()) {
if (nearestOpened == null) {
nearestOpened = projectEvent;
break;
}
} else {
if (nearestClosed == null) {
nearestClosed = projectEvent;
break;
}
}
}
}
ProjectEvent match = (nearestOpened != null) ? nearestOpened : nearestClosed;
if (match != null) {
ProjectEvent transform = new ProjectEvent(
winTime,
match.getType(),
windowEvent.isWhenWindowActivated() ? "activated" : "deactivated",
match.getData());
transforms.add(transform);
} else {
Map<String, String> data = new HashMap<String, String>();
data.put("name", ("unknown_" + winFrame));
data.put("frame", winFrame);
ProjectEvent transform = new ProjectEvent(
winTime,
"Project",
windowEvent.isWhenWindowActivated() ? "activated" : "deactivated",
data);
transforms.add(transform);
// throw new IllegalArgumentException("the lonely window event " + windowEvent + ".");
}
}
if (transforms.isEmpty()) {
return null;
}
// removing the successive similar activated project events.
List<ProjectEvent> prepared = new ArrayList<ProjectEvent>();
ProjectEvent lastEvent = null;
for (int i = 0, s = transforms.size(); i < s; i++) {
ProjectEvent currEvent = transforms.get(i);
if (lastEvent == null) {
lastEvent = currEvent;
prepared.add(currEvent);
} else {
String lastName = lastEvent.getNameField();
String currName = currEvent.getNameField();
if (!lastName.equals(currName)) {
lastEvent = currEvent;
prepared.add(currEvent);
}
}
}
if (prepared.isEmpty()) {
return null;
}
for (ProjectEvent projectEvent : projectEvents) {
if (projectEvent.isWhenClosed()) {
prepared.add(projectEvent);
}
}
Collections.sort(prepared, new Comparator<ProjectEvent>() {
public int compare(ProjectEvent o1, ProjectEvent o2) {
long t1 = o1.getTime();
long t2 = o2.getTime();
return (int) (t1 - t2);
}
});
// constructing project blocks based on the transformed activated project events.
List<ProjectBlock> projectBlocks = new ArrayList<ProjectBlock>();
ProjectEvent splitPoint = null;
List<IntelliJEvent> terminalForProjectBlock = null;
for (ProjectEvent projectEvent : prepared) {
if (projectEvent.isWhenActivated()) {
if (splitPoint != null) {
long begTime = splitPoint.getTime();
long endTime = projectEvent.getTime();
if (!terminalForProjectBlock.isEmpty()) {
long time = terminalForProjectBlock.get(0).getTime();
if (time < endTime) {
endTime = time;
}
}
ProjectBlock projectBlock = new ProjectBlock(begTime, endTime,
splitPoint.getNameField(), splitPoint.getFrameField());
projectBlocks.add(projectBlock);
}
splitPoint = projectEvent;
terminalForProjectBlock = new ArrayList<IntelliJEvent>();
} else if (projectEvent.isWhenClosed()) {
if (splitPoint == null) {
continue;
}
if (!splitPoint.getNameField().equals(projectEvent.getNameField())) {
continue;
}
if (!splitPoint.getFrameField().equals(projectEvent.getFrameField())) {
continue;
}
terminalForProjectBlock.add(projectEvent);
}
}
if (splitPoint != null) {
long begTime = splitPoint.getTime();
long endTime = events.get(events.size() - 1).getTime();
if (!terminalForProjectBlock.isEmpty()) {
long time = terminalForProjectBlock.get(0).getTime();
if (time < endTime) {
endTime = time;
}
}
ProjectBlock projectBlock = new ProjectBlock(begTime, endTime,
splitPoint.getNameField(), splitPoint.getFrameField());
projectBlocks.add(projectBlock);
}
return projectBlocks.isEmpty() ? null : projectBlocks;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class ActionEvent extends IntelliJEvent {
public ActionEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public ActionEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenPerformed() {
return "performed".equals(when);
}
public String getActionField() {
if (data == null) {
return null;
} else {
return data.get("action");
}
}
public String getIdField() {
if (data == null) {
return null;
} else {
return data.get("id");
}
}
public String getClassField() {
if (data == null) {
return null;
} else {
return data.get("class");
}
}
public String getCategoryField() {
if (data == null) {
return null;
} else {
return data.get("category");
}
}
}

View File

@ -0,0 +1,29 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class AppEvent extends IntelliJEvent {
public AppEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public AppEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenOpened() {
return "opened".equals(when);
}
public boolean isWhenClosed() {
return "closed".equals(when);
}
public boolean isWhenActivated() {
return "activated".equals(when);
}
public boolean isWhenDeactivated() {
return "deactivated".equals(when);
}
}

View File

@ -0,0 +1,65 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class CompileEvent extends IntelliJEvent {
public CompileEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public CompileEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenFinished() {
return "finished".equals(when);
}
public boolean isWhenAutoFinished() {
return "auto_finished".equals(when);
}
public boolean isWhenFileGenerated() {
return "file_generated".equals(when);
}
public String getAbortedField() {
if (data == null) {
return null;
} else {
return data.get("aborted");
}
}
public String getErrorsField() {
if (data == null) {
return null;
} else {
return data.get("errors");
}
}
public String getWarningsField() {
if (data == null) {
return null;
} else {
return data.get("warnings");
}
}
public String getRootField() {
if (data == null) {
return null;
} else {
return data.get("root");
}
}
public String getPathField() {
if (data == null) {
return null;
} else {
return data.get("path");
}
}
}

View File

@ -0,0 +1,119 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class CursorEvent extends IntelliJEvent {
public CursorEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public CursorEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenCursorChanged() {
return "cursor_changed".equals(when);
}
public boolean isWhenCursorAdded() {
return "cursor_added".equals(when);
}
public boolean isWhenCursorRemoved() {
return "cursor_removed".equals(when);
}
public boolean isWhenSelectChanged() {
return "select_changed".equals(when);
}
public String getEditorField() {
if (data == null) {
return null;
} else {
return data.get("editor");
}
}
public String getOldPosField() {
if (data == null) {
return null;
} else {
return data.get("oldPos");
}
}
public String getNewPosField() {
if (data == null) {
return null;
} else {
return data.get("newPos");
}
}
public String getOldField() {
if (data == null) {
return null;
} else {
return data.get("old");
}
}
public String getNewField() {
if (data == null) {
return null;
} else {
return data.get("new");
}
}
public boolean isUntypedEditor() {
return "UNTYPED".equals(getEditorField());
}
public boolean isConsoleEditor() {
return "CONSOLE".equals(getEditorField());
}
public boolean isPreviewEditor() {
String editor = getEditorField();
return (editor != null) && editor.startsWith("PREVIEW");
}
public boolean isDiffEditor() {
return "DIFF".equals(getEditorField());
}
public static boolean isInSameEditor(CursorEvent event1, CursorEvent event2) {
if (event1 == null || event2 == null) {
return false;
}
if (event1.isUntypedEditor()) {
return event2.isUntypedEditor();
} else if (event1.isConsoleEditor()) {
return event2.isConsoleEditor();
} else if (event1.isPreviewEditor()) {
return event2.isPreviewEditor();
} else if (event1.isDiffEditor()) {
return event2.isDiffEditor();
} else {
return false;
}
}
public static long[] getLineAndColumn(String pos) {
int commaIndex = pos.indexOf(",");
String lineStr = pos.substring(1, commaIndex);
String columnStr = pos.substring(commaIndex + 1, pos.length() - 1);
long line = -1;
long column = -1;
try {
line = Long.parseLong(lineStr);
column = Long.parseLong(columnStr);
} catch (NumberFormatException e) {
e.printStackTrace();
}
return new long[]{line, column};
}
}

View File

@ -0,0 +1,129 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class DebugEvent extends IntelliJEvent {
public DebugEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public DebugEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenContextChanged() {
return "context_changed".equals(when);
}
public boolean isWhenBreakpointAdded() {
return "breakpoint_added".equals(when);
}
public boolean isWhenBreakpointRemoved() {
return "breakpoint_removed".equals(when);
}
public boolean isWhenBreakpointChanged() {
return "breakpoint_changed".equals(when);
}
public String getProjectField() {
if (data == null) {
return null;
} else {
return data.get("project");
}
}
public String getSessionField() {
if (data == null) {
return null;
} else {
return data.get("segment");
}
}
public String getEventField() {
if (data == null) {
return null;
} else {
return data.get("event");
}
}
public String getStateField() {
if (data == null) {
return null;
} else {
return data.get("state");
}
}
public boolean isWaitingAttachState() {
return "WAITING_ATTACH".equals(getStateField());
}
public boolean isRunningState() {
return "RUNNING".equals(getStateField());
}
public boolean isPausedState() {
return "PAUSED".equals(getStateField());
}
public boolean isWaitEvaluationState() {
return "WAIT_EVALUATION".equals(getStateField());
}
public boolean isStoppedState() {
return "STOPPED".equals(getStateField());
}
public boolean isDisposedState() {
return "DISPOSED".equals(getStateField());
}
public boolean isContextEvent() {
return "CONTEXT".equals(getEventField());
}
public boolean isStartWaitAttachEvent() {
return "START_WAIT_ATTACH".equals(getEventField());
}
public boolean isAttachedEvent() {
return "ATTACHED".equals(getEventField());
}
public boolean isThreadsRefreshEvent() {
return "THREADS_REFRESH".equals(getEventField());
}
public boolean isResumeEvent() {
return "RESUME".equals(getEventField());
}
public boolean isStepEvent() {
return "STEP".equals(getEventField());
}
public boolean isPauseEvent() {
return "PAUSE".equals(getEventField());
}
public boolean isRefreshEvent() {
return "REFRESH".equals(getEventField());
}
public boolean isRefreshWithStackEvent() {
return "REFRESH_WITH_STACK".equals(getEventField());
}
public boolean isDetachedEvent() {
return "DETACHED".equals(getEventField());
}
public boolean isDisposeEvent() {
return "DISPOSE".equals(getEventField());
}
}

View File

@ -0,0 +1,69 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class ExecuteEvent extends IntelliJEvent {
public ExecuteEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public ExecuteEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenScheduled() {
return "scheduled".equals(when);
}
public boolean isWhenStarting() {
return "starting".equals(when);
}
public boolean isWhenCancelled() {
return "cancelled".equals(when);
}
public boolean isWhenStarted() {
return "started".equals(when);
}
public boolean isWhenTerminating() {
return "terminating".equals(when);
}
public boolean isWhenTerminated() {
return "terminated".equals(when);
}
public boolean isWhenActiveTargetChanged() {
return "active_target_changed".equals(when);
}
public String getIdField() {
if (data == null) {
return null;
} else {
return data.get("id");
}
}
public String getNameField() {
if (data == null) {
return null;
} else {
return data.get("name");
}
}
public boolean isRun() {
return "Run".equals(getIdField());
}
public boolean isDebug() {
return "Debug".equals(getIdField());
}
public boolean isCoverage() {
return "Coverage".equals(getIdField());
}
}

View File

@ -0,0 +1,165 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class FileEvent extends IntelliJEvent {
public FileEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public FileEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenPropertyChanged() {
return "property_changed".equals(when);
}
public boolean isWhenContentChanged() {
return "content_changed".equals(when);
}
public boolean isWhenCreated() {
return "created".equals(when);
}
public boolean isWhenDeleted() {
return "deleted".equals(when);
}
public boolean isWhenMoved() {
return "moved".equals(when);
}
public boolean isWhenCopied() {
return "copied".equals(when);
}
public boolean isWhenOpened() {
return "opened".equals(when);
}
public boolean isWhenClosed() {
return "closed".equals(when);
}
public boolean isWhenSelected() {
return "selected".equals(when);
}
public boolean isWhenChanged() {
return "changed".equals(when);
}
public String getPathField() {
if (data == null) {
return null;
} else {
return data.get("path");
}
}
public String getFileLenField() {
if (data == null) {
return null;
} else {
return data.get("fileLen");
}
}
public String getDocLenField() {
if (data == null) {
return null;
} else {
return data.get("docLen");
}
}
public String getLineField() {
if (data == null) {
return null;
} else {
return data.get("line");
}
}
public String getOldPathField() {
if (data == null) {
return null;
} else {
return data.get("oldPath");
}
}
public String getNewPathField() {
if (data == null) {
return null;
} else {
return data.get("newPath");
}
}
public String getOldDocLenField() {
if (data == null) {
return null;
} else {
return data.get("oldDocLen");
}
}
public String getNewDocLenField() {
if (data == null) {
return null;
} else {
return data.get("newDocLen");
}
}
public String getPropertyField() {
if (data == null) {
return null;
} else {
return data.get("property");
}
}
public String getOldValueField() {
if (data == null) {
return null;
} else {
return data.get("oldValue");
}
}
public String getNewValueField() {
if (data == null) {
return null;
} else {
return data.get("newValue");
}
}
public String getOldParentField() {
if (data == null) {
return null;
} else {
return data.get("oldParent");
}
}
public String getNewParentField() {
if (data == null) {
return null;
} else {
return data.get("newParent");
}
}
public String getOriginalField() {
if (data == null) {
return null;
} else {
return data.get("original");
}
}
}

View File

@ -0,0 +1,212 @@
package edu.nju.ics.frontier.bean.intellij;
import edu.nju.ics.frontier.util.StringUtil;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class IntelliJEvent implements Comparable<IntelliJEvent> {
public enum Type {
App, Project, File, Action, Key, Mouse, Window, Cursor, Compile, Execute, Debug,
}
protected long time;
protected String type;
protected String when;
protected Map<String, String> data;
public IntelliJEvent(long time, String type, String when, Map<String, String> data) {
this.time = time;
this.type = type;
this.when = when;
this.data = data;
}
public long getTime() {
return time;
}
public String getType() {
return type;
}
public String getWhen() {
return when;
}
public Map<String, String> getData() {
return data;
}
public void setData(Map<String, String> data) {
this.data = data;
}
public boolean hasData() {
return (!(data == null || data.isEmpty()));
}
public void addExtraData(String k, String v) {
if (k != null && v != null) {
if (data == null) {
data = new HashMap<String, String>();
}
data.put(k, v);
}
}
public String getExtraData(String k) {
if (data != null && k != null) {
return data.get(k);
} else {
return null;
}
}
@Override
public String toString() {
return String.format("{time:%d(%s),type:%s,when:%s,data:%s}",
time, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(time)),
type, when, StringUtil.toString(data));
}
public static boolean isAppEvent(IntelliJEvent event) {
return Type.App.name().equals(event.type);
}
public static boolean isProjectEvent(IntelliJEvent event) {
return Type.Project.name().equals(event.type);
}
public static boolean isFileEvent(IntelliJEvent event) {
return Type.File.name().equals(event.type);
}
public static boolean isActionEvent(IntelliJEvent event) {
return Type.Action.name().equals(event.type);
}
public static boolean isKeyEvent(IntelliJEvent event) {
return Type.Key.name().equals(event.type);
}
public static boolean isMouseEvent(IntelliJEvent event) {
return Type.Mouse.name().equals(event.type);
}
public static boolean isWindowEvent(IntelliJEvent event) {
return Type.Window.name().equals(event.type);
}
public static boolean isCursorEvent(IntelliJEvent event) {
return Type.Cursor.name().equals(event.type);
}
public static boolean isCompileEvent(IntelliJEvent event) {
return Type.Compile.name().equals(event.type);
}
public static boolean isExecuteEvent(IntelliJEvent event) {
return Type.Execute.name().equals(event.type);
}
public static boolean isDebugEvent(IntelliJEvent event) {
return Type.Debug.name().equals(event.type);
}
public static AppEvent toAppEvent(IntelliJEvent event) {
if (event == null || (!isAppEvent(event))) {
return null;
} else {
return new AppEvent(event);
}
}
public static ProjectEvent toProjectEvent(IntelliJEvent event) {
if (event == null || (!isProjectEvent(event))) {
return null;
} else {
return new ProjectEvent(event);
}
}
public static FileEvent toFileEvent(IntelliJEvent event) {
if (event == null || (!isFileEvent(event))) {
return null;
} else {
return new FileEvent(event);
}
}
public static ActionEvent toActionEvent(IntelliJEvent event) {
if (event == null || (!isActionEvent(event))) {
return null;
} else {
return new ActionEvent(event);
}
}
public static KeyEvent toKeyEvent(IntelliJEvent event) {
if (event == null || (!isKeyEvent(event))) {
return null;
} else {
return new KeyEvent(event);
}
}
public static MouseEvent toMouseEvent(IntelliJEvent event) {
if (event == null || (!isMouseEvent(event))) {
return null;
} else {
return new MouseEvent(event);
}
}
public static WindowEvent toWindowEvent(IntelliJEvent event) {
if (event == null || (!isWindowEvent(event))) {
return null;
} else {
return new WindowEvent(event);
}
}
public static CursorEvent toCursorEvent(IntelliJEvent event) {
if (event == null || (!isCursorEvent(event))) {
return null;
} else {
return new CursorEvent(event);
}
}
public static CompileEvent toCompileEvent(IntelliJEvent event) {
if (event == null || (!isCompileEvent(event))) {
return null;
} else {
return new CompileEvent(event);
}
}
public static ExecuteEvent toExecuteEvent(IntelliJEvent event) {
if (event == null || (!isExecuteEvent(event))) {
return null;
} else {
return new ExecuteEvent(event);
}
}
public static DebugEvent toDebugEvent(IntelliJEvent event) {
if (event == null || (!isDebugEvent(event))) {
return null;
} else {
return new DebugEvent(event);
}
}
public int compareTo(IntelliJEvent o) {
long t1 = this.time;
long t2 = o.time;
return (int) (t1 - t2);
}
}

View File

@ -0,0 +1,237 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class KeyEvent extends IntelliJEvent {
public KeyEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public KeyEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenKeyPressed() {
return "KEY_PRESSED".equals(when);
}
public boolean isWhenKeyReleased() {
return "KEY_RELEASED".equals(when);
}
public boolean isWhenKeyTyped() {
return "KEY_TYPED".equals(when);
}
public String getTextField() {
if (data == null) {
return null;
} else {
return data.get("text");
}
}
public String getModifiersField() {
if (data == null) {
return null;
} else {
return data.get("modifiers");
}
}
public boolean isLetter() {
return "Letter".equals(getTextField());
}
public boolean isNumber() {
return "Number".equals(getTextField());
}
public boolean isPunctuation() {
return "Punctuation".equals(getTextField());
}
public boolean isEnter() {
return "Enter".equals(getTextField());
}
public boolean isEscape() {
return "Escape".equals(getTextField());
}
public boolean isTab() {
return "Tab".equals(getTextField());
}
public boolean isSpace() {
return "Space".equals(getTextField());
}
public boolean isBackspace() {
return "Backspace".equals(getTextField());
}
public boolean isInsert() {
return "Insert".equals(getTextField());
}
public boolean isDelete() {
return "Delete".equals(getTextField());
}
public boolean isDirection() {
return isLeft() || isRight() || isUp() || isDown() ||
isHome() || isEnd() || isPageUp() || isPageDown();
}
public boolean isLeft() {
return "Left".equals(getTextField());
}
public boolean isRight() {
return "Right".equals(getTextField());
}
public boolean isUp() {
return "Up".equals(getTextField());
}
public boolean isDown() {
return "Down".equals(getTextField());
}
public boolean isHome() {
return "Home".equals(getTextField());
}
public boolean isEnd() {
return "End".equals(getTextField());
}
public boolean isPageUp() {
return "Page Up".equals(getTextField());
}
public boolean isPageDown() {
return "Page Down".equals(getTextField());
}
public boolean isModifier() {
return isControl() || isAlt() || isShift();
}
public boolean isControl() {
return "Control".equals(getTextField());
}
public boolean isAlt() {
return "Alt".equals(getTextField());
}
public boolean isShift() {
return "Shift".equals(getTextField());
}
public boolean isFunction() {
return isF1() || isF2() || isF3() || isF4() ||
isF5() || isF6() || isF7() || isF8() ||
isF9() || isF10() || isF11() || isF12();
}
public boolean isF1() {
return "F1".equals(getTextField());
}
public boolean isF2() {
return "F2".equals(getTextField());
}
public boolean isF3() {
return "F3".equals(getTextField());
}
public boolean isF4() {
return "F4".equals(getTextField());
}
public boolean isF5() {
return "F5".equals(getTextField());
}
public boolean isF6() {
return "F6".equals(getTextField());
}
public boolean isF7() {
return "F7".equals(getTextField());
}
public boolean isF8() {
return "F8".equals(getTextField());
}
public boolean isF9() {
return "F9".equals(getTextField());
}
public boolean isF10() {
return "F10".equals(getTextField());
}
public boolean isF11() {
return "F11".equals(getTextField());
}
public boolean isF12() {
return "F12".equals(getTextField());
}
public boolean isCapsLock() {
return "Caps Lock".equals(getTextField());
}
public boolean isNumLock() {
return "Num Lock".equals(getTextField());
}
public boolean isScrollLock() {
return "Scroll Lock".equals(getTextField());
}
public boolean isPause() {
return "Pause".equals(getTextField());
}
public boolean isPrintScreen() {
return "Print Screen".equals(getTextField());
}
public boolean isWindows() {
return "Windows".equals(getTextField());
}
public boolean isContextMenu() {
return "Context Menu".equals(getTextField());
}
public boolean isNone() {
return getTextField() == null;
}
public boolean isPureOrShiftLetter() {
return isLetter() && isNoneOrShiftModifier();
}
public boolean isPureOrShiftNumber() {
return isNumber() && isNoneOrShiftModifier();
}
public boolean isPureOrShiftPunctuation() {
return isPunctuation() && isNoneOrShiftModifier();
}
private boolean isNoneOrShiftModifier() {
String modifiers = getModifiersField();
return (modifiers == null || "Shift".equals(modifiers));
}
}

View File

@ -0,0 +1,103 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class MouseEvent extends IntelliJEvent {
public MouseEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public MouseEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenMousePressed() {
return "MOUSE_PRESSED".equals(when);
}
public boolean isWhenMouseReleased() {
return "MOUSE_RELEASED".equals(when);
}
public boolean isWhenMouseClicked() {
return "MOUSE_CLICKED".equals(when);
}
public boolean isWhenMouseEntered() {
return "MOUSE_ENTERED".equals(when);
}
public boolean isWhenMouseExited() {
return "MOUSE_EXITED".equals(when);
}
public boolean isWhenMouseMoved() {
return "MOUSE_MOVED".equals(when);
}
public boolean isWhenMouseDragged() {
return "MOUSE_DRAGGED".equals(when);
}
public boolean isWhenMouseWheel() {
return "MOUSE_WHEEL".equals(when);
}
public String getButtonField() {
if (data == null) {
return null;
} else {
return data.get("button");
}
}
public String getClickCountField() {
if (data == null) {
return null;
} else {
return data.get("clickCount");
}
}
public String getCoordinatesField() {
if (data == null) {
return null;
} else {
return data.get("coordinates");
}
}
public String getModifiersField() {
if (data == null) {
return null;
} else {
return data.get("modifiers");
}
}
public int[] getAbsoluteCoordinate() {
String coordinates = getCoordinatesField();
if (coordinates == null) {
return null;
}
String point = coordinates.substring(0, coordinates.indexOf("/"));
point = point.substring(1, point.length() - 1);
String[] xy = point.split(",");
int x = Integer.parseInt(xy[0]);
int y = Integer.parseInt(xy[1]);
return new int[]{x, y};
}
public int[] getRelativeCoordinate() {
String coordinates = getCoordinatesField();
if (coordinates == null) {
return null;
}
String point = coordinates.substring(coordinates.indexOf("/") + 1);
point = point.substring(1, point.length() - 1);
String[] xy = point.split(",");
int x = Integer.parseInt(xy[0]);
int y = Integer.parseInt(xy[1]);
return new int[]{x, y};
}
}

View File

@ -0,0 +1,45 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class ProjectEvent extends IntelliJEvent {
public ProjectEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public ProjectEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenOpened() {
return "opened".equals(when);
}
public boolean isWhenClosed() {
return "closed".equals(when);
}
public boolean isWhenActivated() {
return "activated".equals(when);
}
public boolean isWhenDeactivated() {
return "deactivated".equals(when);
}
public String getNameField() {
if (data == null) {
return null;
} else {
return data.get("name");
}
}
public String getFrameField() {
if (data == null) {
return null;
} else {
return data.get("frame");
}
}
}

View File

@ -0,0 +1,101 @@
package edu.nju.ics.frontier.bean.intellij;
import java.util.Map;
public class WindowEvent extends IntelliJEvent {
public WindowEvent(long time, String type, String when, Map<String, String> data) {
super(time, type, when, data);
}
public WindowEvent(IntelliJEvent event) {
super(event.time, event.type, event.when, event.data);
}
public boolean isWhenWindowOpened() {
return "WINDOW_OPENED".equals(when);
}
public boolean isWhenWindowClosing() {
return "WINDOW_CLOSING".equals(when);
}
public boolean isWhenWindowClosed() {
return "WINDOW_CLOSED".equals(when);
}
public boolean isWhenWindowIconified() {
return "WINDOW_ICONIFIED".equals(when);
}
public boolean isWhenWindowDeiconified() {
return "WINDOW_DEICONIFIED".equals(when);
}
public boolean isWhenWindowActivated() {
return "WINDOW_ACTIVATED".equals(when);
}
public boolean isWhenWindowDeactivated() {
return "WINDOW_DEACTIVATED".equals(when);
}
public boolean isWhenWindowGainedFocus() {
return "WINDOW_GAINED_FOCUS".equals(when);
}
public boolean isWhenWindowLostFocus() {
return "WINDOW_LOST_FOCUS".equals(when);
}
public boolean isWhenWindowStateChanged() {
return "WINDOW_STATE_CHANGED".equals(when);
}
public String getThisField() {
if (data == null) {
return null;
} else {
return data.get("this");
}
}
public String getOppositeField() {
if (data == null) {
return null;
} else {
return data.get("opposite");
}
}
public String getThisWindowName() {
String s = getThisField();
return (s == null) ? null : s.split(",")[0];
}
public String getOppositeWindowName() {
String s = getOppositeField();
return (s == null) ? null : s.split(",")[0];
}
public String getThisWindowType() {
return name2type(getThisWindowName());
}
public String getOppositeWindowType() {
return name2type(getOppositeWindowName());
}
public static String name2type(String winName) {
if (winName == null) {
return null;
}
int index;
for (index = 0; index < winName.length(); index++) {
char c = winName.charAt(index);
if ('0' <= c && c <= '9') {
break;
}
}
return winName.substring(0, index);
}
}

View File

@ -0,0 +1,35 @@
package edu.nju.ics.frontier.bean.label;
public abstract class Action {
public enum Type {
SELECT, SUBMIT, REFUSE
}
protected long time;
protected Type type;
public Action(long time, Type type) {
this.time = time;
this.type = type;
}
public long getTime() {
return time;
}
public Type getType() {
return type;
}
public boolean isSelectAction() {
return type == Type.SELECT;
}
public boolean isSubmitAction() {
return type == Type.SUBMIT;
}
public boolean isRefuseAction() {
return type == Type.REFUSE;
}
}

View File

@ -0,0 +1,55 @@
package edu.nju.ics.frontier.bean.label;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.Set;
public class ActionTypeConverter implements JsonSerializer<Action>, JsonDeserializer<Action> {
public JsonElement serialize(Action action, Type type, JsonSerializationContext jsonSerializationContext) {
Class clazz = null;
Action.Type actionType = action.getType();
switch (actionType) {
case SELECT:
clazz = SelectAction.class;
break;
case REFUSE:
clazz = RefuseAction.class;
break;
case SUBMIT:
clazz = SubmitAction.class;
break;
default:
break;
}
if (clazz != null) {
return jsonSerializationContext.serialize(action, clazz);
} else {
return null;
}
}
public Action deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
Class clazz = null;
Set<String> keySet = jsonElement.getAsJsonObject().keySet();
if (keySet != null && keySet.contains("type")) {
String actionTypeName = jsonElement.getAsJsonObject().get("type").getAsString();
if (Action.Type.SELECT.name().equals(actionTypeName)) {
clazz = SelectAction.class;
} else if (Action.Type.REFUSE.name().equals(actionTypeName)) {
clazz = RefuseAction.class;
} else if (Action.Type.SUBMIT.name().equals(actionTypeName)) {
clazz = SubmitAction.class;
}
}
if (clazz != null) {
return jsonDeserializationContext.deserialize(jsonElement, clazz);
} else {
return null;
}
}
}

View File

@ -0,0 +1,178 @@
package edu.nju.ics.frontier.bean.label;
import edu.nju.ics.frontier.util.StringUtil;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Label {
private long begTime;
private long endTime;
private List<Action> actions;
public enum Type {
CLOSE,
CLOSE_WITHOUT_SELECT,
CLOSE_WITH_SELECT,
REFUSE,
REFUSE_WITHOUT_SELECT,
REFUSE_WITH_SELECT,
SUBMIT,
}
public Label(long begTime, long endTime, List<Action> actions) {
this.begTime = begTime;
this.endTime = endTime;
this.actions = actions;
}
public long getBegTime() {
return begTime;
}
public long getEndTime() {
return endTime;
}
public List<Action> getActions() {
return actions;
}
public boolean hasAction() {
return (!actions.isEmpty());
}
public boolean containSelectAction(SelectOrSubmitAction.Dimension dimension) {
List<SelectAction> actions = getSelectActions(dimension);
return actions != null;
}
public boolean containSelectAction() {
if (actions == null || actions.isEmpty()) {
return false;
}
for (Action action : actions) {
if (action.isSelectAction()) {
return true;
}
}
return false;
}
public boolean containSubmitAction() {
if (actions == null || actions.isEmpty()) {
return false;
}
for (Action action : actions) {
if (action.isSubmitAction()) {
return true;
}
}
return false;
}
public boolean containRefuseAction() {
if (actions == null || actions.isEmpty()) {
return false;
}
for (Action action : actions) {
if (action.isRefuseAction()) {
return true;
}
}
return false;
}
public List<SelectAction> getSelectActions(SelectOrSubmitAction.Dimension dimension) {
if (!containSelectAction()) {
return null;
}
List<SelectAction> selectActions = new ArrayList<SelectAction>();
for (Action action : actions) {
if (action.isSelectAction()) {
SelectAction selectAction = (SelectAction) action;
if (selectAction.getDimension() == dimension) {
selectActions.add(selectAction);
}
}
}
return selectActions.isEmpty() ? null : selectActions;
}
public SubmitAction getSubmitAction(SelectOrSubmitAction.Dimension dimension) {
if (!containSubmitAction()) {
return null;
}
for (Action action : actions) {
if (action.isSubmitAction()) {
SubmitAction submitAction = (SubmitAction) action;
if (submitAction.getDimension() == dimension) {
return submitAction;
}
}
}
return null;
}
public RefuseAction getRefuseAction() {
if (!containRefuseAction()) {
return null;
}
for (Action action : actions) {
if (action.isRefuseAction()) {
return ((RefuseAction) action);
}
}
return null;
}
public int getLabelScore(SelectOrSubmitAction.Dimension dimension) {
List<SelectAction> actions = getSelectActions(dimension);
return (actions == null) ? -1 : actions.get(actions.size() - 1).getScore();
}
public boolean isMultiTimesSelected(SelectOrSubmitAction.Dimension dimension) {
List<SelectAction> selectActions = getSelectActions(dimension);
return (selectActions != null && selectActions.size() > 1);
}
public Type getSimpleType() {
boolean isRefused = containRefuseAction();
boolean isSubmitted = containSubmitAction();
if (isRefused) {
return Type.REFUSE;
} else if (isSubmitted) {
return Type.SUBMIT;
} else {
return Type.CLOSE;
}
}
public Type getCanonicalType() {
boolean isEngagementSelected = containSelectAction(SelectOrSubmitAction.Dimension.ENGAGEMENT);
boolean isProductivitySelected = containSelectAction(SelectOrSubmitAction.Dimension.PRODUCTIVITY);
boolean isSelected = (isEngagementSelected && isProductivitySelected);
boolean isRefused = containRefuseAction();
boolean isSubmitted = containSubmitAction();
if (isRefused) {
return isSelected ? Type.REFUSE_WITH_SELECT : Type.REFUSE_WITHOUT_SELECT;
} else if (isSubmitted) {
return Type.SUBMIT;
} else {
return isSelected ? Type.CLOSE_WITH_SELECT : Type.CLOSE_WITHOUT_SELECT;
}
}
@Override
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return String.format("{begTime:%d(%s),endTime:%d(%s),actions:%s}",
begTime, sdf.format(new Date(begTime)),
endTime, sdf.format(new Date(endTime)),
StringUtil.toString(actions));
}
}

View File

@ -0,0 +1,43 @@
package edu.nju.ics.frontier.bean.label;
import java.text.SimpleDateFormat;
import java.util.Date;
public class RefuseAction extends Action {
public enum OutOfIDEActivity {
MEETING, REST, TALK, OTHER
}
private OutOfIDEActivity activity;
public RefuseAction(long time, OutOfIDEActivity activity) {
super(time, Type.REFUSE);
this.activity = activity;
}
public OutOfIDEActivity getActivity() {
return activity;
}
public boolean isMeetingActivity() {
return activity == OutOfIDEActivity.MEETING;
}
public boolean isRestActivity() {
return activity == OutOfIDEActivity.REST;
}
public boolean isTalkActivity() {
return activity == OutOfIDEActivity.TALK;
}
public boolean isOtherActivity() {
return activity == OutOfIDEActivity.OTHER;
}
@Override
public String toString() {
return String.format("{time:%d(%s),type:%s,activity:%s}",
time, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(time)), type.name(), activity.name());
}
}

View File

@ -0,0 +1,7 @@
package edu.nju.ics.frontier.bean.label;
public class SelectAction extends SelectOrSubmitAction {
public SelectAction(long time, Dimension dimension, int index, int score) {
super(time, Type.SELECT, dimension, index, score);
}
}

View File

@ -0,0 +1,47 @@
package edu.nju.ics.frontier.bean.label;
import java.text.SimpleDateFormat;
import java.util.Date;
public abstract class SelectOrSubmitAction extends Action {
public enum Dimension {
ENGAGEMENT, PRODUCTIVITY
}
protected Dimension dimension;
protected int index;
protected int score;
public SelectOrSubmitAction(long time, Type type, Dimension dimension, int index, int score) {
super(time, type);
this.dimension = dimension;
this.index = index;
this.score = score;
}
public Dimension getDimension() {
return dimension;
}
public int getIndex() {
return index;
}
public int getScore() {
return score;
}
public boolean isEngagementDimension() {
return dimension == Dimension.ENGAGEMENT;
}
public boolean isProductivityDimension() {
return dimension == Dimension.PRODUCTIVITY;
}
@Override
public String toString() {
return String.format("{time:%d(%s),type:%s,dimension:%s,index:%d,score:%d}",
time, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(time)), type.name(), dimension.name(), index, score);
}
}

View File

@ -0,0 +1,7 @@
package edu.nju.ics.frontier.bean.label;
public class SubmitAction extends SelectOrSubmitAction {
public SubmitAction(long time, Dimension dimension, int index, int score) {
super(time, Type.SUBMIT, dimension, index, score);
}
}

View File

@ -0,0 +1,40 @@
package edu.nju.ics.frontier.bean.session;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import java.util.List;
public class Session {
private final List<IntelliJEvent> events;
private final List<Label> labels;
public Session(List<IntelliJEvent> events, List<Label> labels) {
this.events = events;
this.labels = labels;
}
public long getBegTime() {
if (events == null || events.isEmpty()) {
return -1;
} else {
return events.get(0).getTime();
}
}
public long getEndTime() {
if (events == null ||events.isEmpty()) {
return -1;
} else {
return events.get(events.size() - 1).getTime();
}
}
public List<IntelliJEvent> getEvents() {
return events;
}
public List<Label> getLabels() {
return labels;
}
}

View File

@ -0,0 +1,47 @@
package edu.nju.ics.frontier.bean.slice;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.StringUtil;
import java.util.List;
public class Slice {
private List<IntelliJEvent> events;
private Label label;
public Slice(List<IntelliJEvent> events, Label label) {
this.events = events;
this.label = label;
}
public List<IntelliJEvent> getEvents() {
return events;
}
public Label getLabel() {
return label;
}
public long getBegTime() {
if (events == null || events.isEmpty()) {
return -1;
} else {
return events.get(0).getTime();
}
}
public long getEndTime() {
if (events == null || events.isEmpty()) {
return -1;
} else {
return events.get(events.size() - 1).getTime();
}
}
@Override
public String toString() {
return String.format("{events:%s,label:%s}",
StringUtil.toString(events), StringUtil.toString(label));
}
}

View File

@ -0,0 +1,85 @@
package edu.nju.ics.frontier.cleaning;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.learning.TimeDatabase;
import edu.nju.ics.frontier.learning.TimePoint;
import edu.nju.ics.frontier.learning.TimeSeries;
import edu.nju.ics.frontier.util.Assertion;
import org.apache.commons.math3.stat.StatUtils;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.stat.descriptive.rank.Max;
import org.apache.commons.math3.stat.descriptive.rank.Median;
import org.apache.commons.math3.stat.descriptive.rank.Min;
import java.util.ArrayList;
import java.util.List;
public class CleaningMain {
public static void main(String[] args) {
String csvPath = "C:\\Users\\zzw\\Desktop\\fujitsu\\time_aligned_instances.csv";
TimeDatabase db = new TimeDatabase();
db.load(csvPath);
db.positiveNeutralNegative();
List<double[]> badFeatures = new ArrayList<double[]>();
OkTextReader reader = new OkTextReader();
reader.open("C:\\Users\\zzw\\Desktop\\fujitsu\\bad_data.csv");
String line;
while ((line = reader.readLine()) != null) {
String[] info = line.split(",");
Assertion.assertEqual(info.length, 2);
String user = info[0];
long timestamp = Long.parseLong(info[1]);
TimePoint tp = db.findTimeSeriesByUser(user).findClosestTimePoint(timestamp, 0, true, false, false);
Assertion.assertNotNull(tp);
badFeatures.add(tp.getFeatures());
tp.setLabel(-1);
Assertion.assertTrue(tp.isUnlabeled());
}
reader.close();
List<double[]> goodFeatures = new ArrayList<double[]>();
for (TimeSeries ts : db.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
goodFeatures.add(tp.getFeatures());
}
}
}
String[] featureNames = db.getFeatureNames();
double[][] goodData = transpose(goodFeatures);
double[][] badData = transpose(badFeatures);
Assertion.assertEqual(featureNames.length, goodData.length);
Assertion.assertEqual(goodData.length, badData.length);
for (int i = 0; i < goodData.length; i++) {
System.out.printf("%30s: %110s\n%30s %110s\n",
featureNames[i],
boxPlot(goodData[i], "good"),
"",
boxPlot(badData[i], "bad"));
}
}
private static double[][] transpose(List<double[]> list) {
double[][] result = new double[list.get(0).length][list.size()];
for (int j = 0; j < result[0].length; j++) {
double[] item = list.get(j);
for (int i = 0; i < result.length; i++) {
result[i][j] = item[i];
}
}
return result;
}
private static String boxPlot(double[] vector, String tag) {
double min = new Min().evaluate(vector);
double max = new Max().evaluate(vector);
double q1 = StatUtils.percentile(vector, 25);
double median = new Median().evaluate(vector);
double q3 = StatUtils.percentile(vector, 75);
double mean = new Mean().evaluate(vector);
return String.format("%s:{avg:%f,min:%f,max:%f,q1:%f,q2:%f,q3:%f}",
tag, mean, min, max, q1, median, q3);
}
}

View File

@ -0,0 +1,38 @@
package edu.nju.ics.frontier.configuration;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Configuration {
public static final long WINDOW_SIZE_IN_MINUTE = 30;
public static final double OVERLAP = 0.5;
public static final boolean STANDARDIZE = false;
public static final boolean NORMALIZE = true;
public static final long WINDOW_SIZE_IN_MILLISECOND = WINDOW_SIZE_IN_MINUTE * 60 * 1000;
public static final int N = 8;
public static final double BASE = 0.99;
public static final double SAME_TEAM_WEIGHT = 1.0;
public static final double DIFF_TEAM_WEIGHT = 0.0;
public static final boolean IS_INTRA_ENABLED = true;
public static final boolean IS_INTER_ENABLED = true;
public static final int EPOCH = 10;
public static final int BATCH = 1;
public static final double ERASE_RATE = 0.1;
public static final boolean IS_DEBUG_MODE = false;
public static final String DATA_ROOT = "C:\\Users\\zzw\\Desktop\\fujitsu\\data\\data";
// public static final PrintStream out = System.out;
public static final OkTextWriter out = new OkTextWriter();
static {
String datetime = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
out.open("C:\\Users\\zzw\\Desktop\\fujitsu\\log\\" + datetime + ".txt");
}
public static void update(String name) {
out.close();
String datetime = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
out.open("C:\\Users\\zzw\\Desktop\\fujitsu\\log\\" + datetime + "_" + name + ".txt");
}
}

View File

@ -0,0 +1,112 @@
package edu.nju.ics.frontier.deep_learning;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.FileUtil;
import java.util.ArrayList;
import java.util.List;
public class DataPreparation {
public static void main(String[] args) {
String[] head;
List<String[]> tmp = new ArrayList<String[]>();
OkTextReader reader = new OkTextReader();
reader.open("C:\\Users\\zzw\\Desktop\\fujitsu\\time_aligned_instances.csv");
head = reader.readLine().split(",");
String line;
while ((line = reader.readLine()) != null) {
String[] info = line.split(",");
tmp.add(info);
}
reader.close();
String[][] body = new String[tmp.size()][];
for (int i = 0; i < body.length; i++) {
body[i] = tmp.get(i);
Assertion.assertEqual(head.length, body[i].length);
}
// create a zero-filled array for padding
String[] padding = new String[head.length];
for (int i = 0; i < padding.length; i++) {
padding[i] = "0.0";
}
List<String[][]> sampleTimeStepFeatureList = new ArrayList<String[][]>();
boolean isOverSampling = true;
int timeSteps = 3;
int forePoint = getIndexOfNextLabeledExample(body, timeSteps - 1);
while (forePoint != -1) {
int backPoint = forePoint - timeSteps + 1;
int foreSessionId = Integer.parseInt(body[forePoint][7]);
String[][] timeStepFeature = new String[timeSteps][];
for (int i = 0; i < timeSteps; i++) {
int sessionId = Integer.parseInt(body[backPoint + i][7]);
if (sessionId == foreSessionId) {
timeStepFeature[i] = body[backPoint + i];
} else {
timeStepFeature[i] = padding;
}
}
sampleTimeStepFeatureList.add(timeStepFeature);
// update forePoint
if (isOverSampling) {
forePoint = getIndexOfNextLabeledExample(body, forePoint + 1);
} else {
forePoint = getIndexOfNextLabeledExample(body, forePoint + timeSteps);
}
}
// generate time sequence dataset
System.out.println("generate features...");
OkTextWriter writer = new OkTextWriter();
String root = "C:\\Users\\zzw\\Desktop\\fujitsu\\deep_learning";
for (int featureIndex = 8; featureIndex < head.length - 2; featureIndex++) {
System.out.printf(" %2d: %s\n", featureIndex - 7, head[featureIndex]);
writer.open(FileUtil.combine(root, "features", String.format("feature%02d.csv", featureIndex - 8)));
for (int sampleIndex = 0; sampleIndex < sampleTimeStepFeatureList.size(); sampleIndex++) {
String[][] sample = sampleTimeStepFeatureList.get(sampleIndex);
for (int timeStepIndex = 0; timeStepIndex < timeSteps; timeStepIndex++) {
writer.print(sample[timeStepIndex][featureIndex]);
writer.print(timeStepIndex < timeSteps - 1 ? "," : "\n");
}
}
writer.close();
}
System.out.println("generate labels...");
writer.open(FileUtil.combine(root, "labels.csv"));
for (int sampleIndex = 0; sampleIndex < sampleTimeStepFeatureList.size(); sampleIndex++) {
String s = sampleTimeStepFeatureList.get(sampleIndex)[timeSteps - 1][head.length - 2];
int label = Integer.parseInt(s);
if (label < 3) {
writer.println("1");
} else if (label == 3) {
writer.println("2");
} else {
writer.println("3");
}
}
writer.close();
System.out.println("done");
}
private static int getIndexOfNextLabeledExample(String[][] body, int offset) {
if (offset < 0 || offset >= body.length) {
return -1;
}
int colIndex = body[0].length - 2; // internalScore
for (int rowIndex = offset; rowIndex < body.length; rowIndex++) {
int label = Integer.parseInt(body[rowIndex][colIndex]);
if (label != -1) {
return rowIndex;
}
}
return -1;
}
}

View File

@ -0,0 +1,147 @@
package edu.nju.ics.frontier.deep_learning;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.recognition.feature.MeasureUtil;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.*;
public class FoldSplitting {
public static void main(String[] args) {
String root = "C:\\Users\\zzw\\Desktop\\fujitsu\\deep_learning";
List<List<String>> features = new ArrayList<List<String>>();
File[] featureFiles = FileUtil.listSubFiles(new File(FileUtil.combine(root, "features")), "feature", ".csv");
for (File featureFile : featureFiles) {
features.add(loadData(featureFile.getAbsolutePath()));
}
List<String> labels = loadData(FileUtil.combine(root, "labels.csv"));
int[][][] trainAndTestFolds = splitTrainAndTestByStratifiedKFold(labels, 10);
for (int foldIndex = 0; foldIndex < 10; foldIndex++) {
String foldRoot = FileUtil.combine(root, "fold" + foldIndex);
System.out.printf(" > fold %d\n", foldIndex);
int[] trainIndices = trainAndTestFolds[foldIndex][0];
int[] testIndices = trainAndTestFolds[foldIndex][1];
for (int featureIndex = 0; featureIndex < features.size(); featureIndex++) {
List<String> feature = features.get(featureIndex);
// X_train
saveData(FileUtil.combine(foldRoot, "train", "features", String.format("feature%02d.csv", featureIndex)), feature, trainIndices);
// X_test
saveData(FileUtil.combine(foldRoot, "test", "features", String.format("feature%02d.csv", featureIndex)), feature, testIndices);
}
// y_train
saveData(FileUtil.combine(foldRoot, "train", "labels.csv"), labels, trainIndices);
// y_test
saveData(FileUtil.combine(foldRoot, "test", "labels.csv"), labels, testIndices);
}
}
private static List<String> loadData(String filePath) {
List<String> result = new ArrayList<String>();
OkTextReader reader = new OkTextReader();
reader.open(filePath);
String line;
while ((line = reader.readLine()) != null) {
result.add(line);
}
reader.close();
return result;
}
private static void saveData(String filePath, List<String> data, int[] indices) {
OkTextWriter writer = new OkTextWriter();
writer.open(filePath);
for (int index : indices) {
writer.println(data.get(index));
}
writer.close();
}
private static int[][][] splitTrainAndTestByStratifiedKFold(List<String> labels, int fold) {
// count number of labels with specific nominal class
Map<String, List<Integer>> labelToIndex = new HashMap<String, List<Integer>>();
for (int i = 0; i < labels.size(); i++) {
MeasureUtil.updateListMap(labelToIndex, labels.get(i), i);
}
List<String> nominalClasses = new ArrayList<String>(labelToIndex.keySet());
Collections.sort(nominalClasses);
System.out.print("#classes:");
for (String nominalClass : nominalClasses) {
System.out.printf(" #(%s)=%3d,", nominalClass, labelToIndex.get(nominalClass).size());
}
System.out.println();
// split training and test sets
int[][][] result = new int[fold][][];
for (String nominalClass : nominalClasses) {
List<Integer> indices = labelToIndex.get(nominalClass);
int length = indices.size();
int size = length / fold;
for (int i = 0; i < fold; i++) {
int testBegIndex = size * i;
int testEndIndex = (i < fold - 1) ? (testBegIndex + size) : length;
// generate training set
int[] trainIndices = new int[length - (testEndIndex - testBegIndex)];
int trainIndex = 0;
for (int j = 0; j < testBegIndex; j++) {
trainIndices[trainIndex++] = indices.get(j);
}
for (int j = testEndIndex; j < length; j++) {
trainIndices[trainIndex++] = indices.get(j);
}
Assertion.assertTrue(trainIndex == trainIndices.length);
// generate test set
int[] testIndices = new int[testEndIndex - testBegIndex];
int testIndex = 0;
for (int j = testBegIndex; j < testEndIndex; j++) {
testIndices[testIndex++] = indices.get(j);
}
Assertion.assertTrue(testIndex == testIndices.length);
// joint
Assertion.assertEqual(length, trainIndex + testIndex);
int[][] oldTrainAndTestIndices = result[i];
if (oldTrainAndTestIndices == null) {
result[i] = new int[][]{trainIndices, testIndices};
} else {
int[] oldTrainIndices = oldTrainAndTestIndices[0];
int[] newTrainIndices = arrayJoin(oldTrainIndices, trainIndices);
int[] oldTestIndices = oldTrainAndTestIndices[1];
int[] newTestIndices = arrayJoin(oldTestIndices, testIndices);
result[i] = new int[][]{newTrainIndices, newTestIndices};
}
}
}
// sort
for (int[][] trainAndTestIndices : result) {
int[] trainIndices = trainAndTestIndices[0];
int[] testIndices = trainAndTestIndices[1];
Arrays.sort(trainIndices);
Arrays.sort(testIndices);
}
return result;
}
private static int[] arrayJoin(int[] array1, int[] array2) {
int[] result = new int[array1.length + array2.length];
for (int i = 0; i < array1.length; i++) {
result[i] = array1[i];
}
for (int i = 0; i < array2.length; i++) {
result[array1.length + i] = array2[i];
}
return result;
}
}

View File

@ -0,0 +1,34 @@
package edu.nju.ics.frontier.learning;
public abstract class BatchSemiSupervisedLearningStrategy extends SemiSupervisedLearningStrategy {
protected int epoch;
protected BatchRate batchRate;
public BatchSemiSupervisedLearningStrategy(String name, int epoch, BatchRate batchRate) {
super(name);
this.epoch = epoch;
this.batchRate = batchRate;
}
public int getEpoch() {
return epoch;
}
public BatchRate getBatchRate() {
return batchRate;
}
@Override
protected boolean isStopBeforeUpdate(TimeDatabase trainDb, int epoch) {
return epoch >= this.epoch;
}
@Override
protected boolean isStopAfterUpdate(TimeDatabase trainDb, int epoch) {
return false;
}
public interface BatchRate {
public abstract int getBatch(int epoch);
}
}

View File

@ -0,0 +1,193 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class DataSource {
public static TimeDatabase initDatabase() {
String csvPath = "C:\\Users\\zzw\\Desktop\\fujitsu\\time_aligned_instances.csv";
TimeDatabase db = new TimeDatabase();
// load dataset
db.load(csvPath);
// transform labels
db.positiveNeutralNegative();
return db.align();
}
public static int[][][] splitTrainAndTestByKFold(int length, int fold) {
int[][][] result = new int[fold][][];
int size = length / fold;
for (int i = 0; i < fold; i++) {
int testBegIndex = size * i;
int testEndIndex = (i < fold - 1) ? (testBegIndex + size) : length;
// generate training set
int[] trainIndices = new int[length - (testEndIndex - testBegIndex)];
int trainIndex = 0;
for (int j = 0; j < testBegIndex; j++) {
trainIndices[trainIndex++] = j;
}
for (int j = testEndIndex; j < length; j++) {
trainIndices[trainIndex++] = j;
}
Assertion.assertTrue(trainIndex == trainIndices.length);
// generate test set
int[] testIndices = new int[testEndIndex - testBegIndex];
int testIndex = 0;
for (int j = testBegIndex; j < testEndIndex; j++) {
testIndices[testIndex++] = j;
}
Assertion.assertTrue(testIndex == testIndices.length);
// reserve
Assertion.assertEqual(length, trainIndex + testIndex);
result[i] = new int[][]{trainIndices, testIndices};
}
return result;
}
public static int[][][] splitTrainValidAndTestByKFold(int length, int fold, double validRate) {
Assertion.assertTrue(validRate >= 0.0 && validRate < 1.0);
int[][][] result = new int[fold][][];
int size = length / fold;
for (int i = 0; i < fold; i++) {
int testBegIndex = size * i;
int testEndIndex = (i < fold - 1) ? (testBegIndex + size) : length;
// generate training and validation sets
int[] trainAndValidIndices = new int[length - (testEndIndex - testBegIndex)];
int trainAndValidIndex = 0;
for (int j = 0; j < testBegIndex; j++) {
trainAndValidIndices[trainAndValidIndex++] = j;
}
for (int j = testEndIndex; j < length; j++) {
trainAndValidIndices[trainAndValidIndex++] = j;
}
Assertion.assertTrue(trainAndValidIndex == trainAndValidIndices.length);
// generate test set
int[] testIndices = new int[testEndIndex - testBegIndex];
int testIndex = 0;
for (int j = testBegIndex; j < testEndIndex; j++) {
testIndices[testIndex++] = j;
}
Assertion.assertTrue(testIndex == testIndices.length);
Assertion.assertEqual(length, trainAndValidIndex + testIndex);
if (validRate > 0.0) {
// divide training and validation sets
int validLength = (int) (trainAndValidIndices.length * validRate);
Assertion.assertPositive(validLength);
int trainLength = trainAndValidIndices.length - validLength;
Assertion.assertPositive(trainLength);
int[] trainIndices = new int[trainLength];
int[] validIndices = new int[validLength];
System.arraycopy(trainAndValidIndices, 0, trainIndices, 0, trainLength);
System.arraycopy(trainAndValidIndices, trainLength, validIndices, 0, validLength);
result[i] = new int[][]{trainIndices, validIndices, testIndices};
} else {
result[i] = new int[][]{trainAndValidIndices, null, testIndices};
}
}
return result;
}
public static TimeDatabase[] splitTrainingAndValidationSets(TimeDatabase trainAndValidDb, double validRate) {
if (validRate == 0.0) {
return new TimeDatabase[]{trainAndValidDb, null};
}
// get the number of labeled data per time point,
// and the number of labeled data of the total database
int numTps = trainAndValidDb.getNumberOfTimePoints();
int[] labeledCountPerTp = new int[numTps];
int labeledCountAllTp = 0;
for (int i = 0; i < numTps; i++) {
int labeledCount = 0;
for (TimeSeries ts : trainAndValidDb.getTimeSeries()) {
TimePoint tp = ts.getTimePoints().get(i);
if (tp.isLabeled()) {
labeledCount++;
}
}
labeledCountPerTp[i] = labeledCount;
labeledCountAllTp += labeledCount;
}
// compute the number of labeled data of the validation set
int labeledCountOfValid = (int) (validRate * labeledCountAllTp);
// get the range of accumulation closest to the number of labeled data of validation set
int[] validRange = rangeOfAccumulationClosestToThreshold(labeledCountPerTp, labeledCountOfValid);
// get the time point indices of training set and that of validation set
int[] validIndices = new int[validRange[1] - validRange[0] + 1];
int[] trainIndices = new int[numTps - validIndices.length];
int validIndex = 0;
for (int i = validRange[0]; i <= validRange[1]; i++) {
validIndices[validIndex++] = i;
}
Assertion.assertEqual(validIndices.length, validIndex);
int trainIndex = 0;
for (int i = 0; i < validRange[0]; i++) {
trainIndices[trainIndex++] = i;
}
for (int i = validRange[1] + 1; i < numTps; i++) {
trainIndices[trainIndex++] = i;
}
Assertion.assertEqual(trainIndices.length, trainIndex);
// split training set and validation set
TimeDatabase trainDb = trainAndValidDb.slice(trainIndices);
TimeDatabase validDb = trainAndValidDb.slice(validIndices);
return new TimeDatabase[]{trainDb, validDb};
}
private static int[] rangeOfAccumulationClosestToThreshold(int[] values, int threshold) {
List<int[]> ranges = new ArrayList<int[]>();
for (int i = 0; i < values.length; i++) {
int[] range = rangeOfAccumulationClosestToThreshold(values, i, threshold);
if (range != null) {
ranges.add(range);
if (range[2] == 0) {
break;
}
} else {
break;
}
}
Assertion.assertFalse(ranges.isEmpty());
Collections.sort(ranges, new Comparator<int[]>() {
public int compare(int[] o1, int[] o2) {
int absDelta1 = o1[2];
int absDelta2 = o2[2];
return absDelta1 - absDelta2;
}
});
int[] closest = ranges.get(0);
return new int[]{closest[0], closest[1]};
}
private static int[] rangeOfAccumulationClosestToThreshold(int[] values, int offset, int threshold) {
Assertion.assertTrue(offset >= 0 && offset < values.length);
int accumulation = 0;
for (int i = offset; i < values.length; i++) {
int oldAccumulation = accumulation;
int newAccumulation = accumulation + values[i];
if (newAccumulation > threshold) {
int oldAbsDelta = Math.abs(oldAccumulation - threshold);
int newAbsDelta = Math.abs(newAccumulation - threshold);
if (oldAbsDelta <= newAbsDelta) {
return new int[]{offset, i - 1, oldAbsDelta};
} else {
return new int[]{offset, i, newAbsDelta};
}
} else if (newAccumulation < threshold) {
accumulation = newAccumulation;
} else {
return new int[]{offset, i, 0};
}
}
return null;
}
}

View File

@ -0,0 +1,907 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Assertion;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Utils;
import java.util.Arrays;
import java.util.List;
public class Evaluation {
private int m_NumClasses;
private String[] m_ClassNames;
private List<int[]> m_TruePredLabelPairs;
private double[][] m_ConfusionMatrix;
private double m_Correct;
private double m_Incorrect;
private double m_WithClass;
public Evaluation(int[] classes, List<int[]> truePredLabelPairs) {
m_NumClasses = classes.length;
m_ClassNames = initClassNames(classes);
m_TruePredLabelPairs = truePredLabelPairs;
m_ConfusionMatrix = getConfusionMatrix(classes, truePredLabelPairs);
for (int i = 0; i < m_ConfusionMatrix.length; i++) {
for (int j = 0; j < m_ConfusionMatrix[i].length; j++) {
if (i == j) {
m_Correct += m_ConfusionMatrix[i][j];
} else {
m_Incorrect += m_ConfusionMatrix[i][j];
}
}
}
m_WithClass = m_Correct + m_Incorrect;
}
public final List<int[]> getTruePredLabelPairs() {
return m_TruePredLabelPairs;
}
/**
* Gets the number of instances incorrectly classified (that is, for which an
* incorrect prediction was made). (Actually the sum of the weights of these
* instances)
*
* @return the number of incorrectly classified instances
*/
public final double incorrect() {
return m_Incorrect;
}
/**
* Gets the percentage of instances incorrectly classified (that is, for which
* an incorrect prediction was made).
*
* @return the percent of incorrectly classified instances (between 0 and 100)
*/
public final double pctIncorrect() {
return 100 * m_Incorrect / m_WithClass;
}
/**
* Gets the number of instances correctly classified (that is, for which a
* correct prediction was made). (Actually the sum of the weights of these
* instances)
*
* @return the number of correctly classified instances
*/
public final double correct() {
return m_Correct;
}
/**
* Gets the percentage of instances correctly classified (that is, for which a
* correct prediction was made).
*
* @return the percent of correctly classified instances (between 0 and 100)
*/
public final double pctCorrect() {
return 100 * m_Correct / m_WithClass;
}
/**
* Calculates the weighted (by class size) true positive rate.
*
* @return the weighted true positive rate.
*/
public double weightedTruePositiveRate() {
double[] classCounts = new double[m_NumClasses];
double classCountSum = 0;
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
classCounts[i] += m_ConfusionMatrix[i][j];
}
classCountSum += classCounts[i];
}
double truePosTotal = 0;
for (int i = 0; i < m_NumClasses; i++) {
double temp = truePositiveRate(i);
if (classCounts[i] > 0) { // If temp is NaN, we want the sum to also be NaN if count > 0
truePosTotal += (temp * classCounts[i]);
}
}
return truePosTotal / classCountSum;
}
/**
* Calculate the true positive rate with respect to a particular class. This
* is defined as
* <p/>
*
* <pre>
* correctly classified positives
* ------------------------------
* total positives
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the true positive rate
*/
public double truePositiveRate(int classIndex) {
double correct = 0, total = 0;
for (int j = 0; j < m_NumClasses; j++) {
if (j == classIndex) {
correct += m_ConfusionMatrix[classIndex][j];
}
total += m_ConfusionMatrix[classIndex][j];
}
return correct / total;
}
/**
* Calculates the weighted (by class size) false positive rate.
*
* @return the weighted false positive rate.
*/
public double weightedFalsePositiveRate() {
double[] classCounts = new double[m_NumClasses];
double classCountSum = 0;
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
classCounts[i] += m_ConfusionMatrix[i][j];
}
classCountSum += classCounts[i];
}
double falsePosTotal = 0;
for (int i = 0; i < m_NumClasses; i++) {
double temp = falsePositiveRate(i);
if (classCounts[i] > 0) { // If temp is NaN, we want the sum to also be NaN if count > 0
falsePosTotal += (temp * classCounts[i]);
}
}
return falsePosTotal / classCountSum;
}
/**
* Calculate the false positive rate with respect to a particular class. This
* is defined as
* <p/>
*
* <pre>
* incorrectly classified negatives
* --------------------------------
* total negatives
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the false positive rate
*/
public double falsePositiveRate(int classIndex) {
double incorrect = 0, total = 0;
for (int i = 0; i < m_NumClasses; i++) {
if (i != classIndex) {
for (int j = 0; j < m_NumClasses; j++) {
if (j == classIndex) {
incorrect += m_ConfusionMatrix[i][j];
}
total += m_ConfusionMatrix[i][j];
}
}
}
return incorrect / total;
}
/**
* Calculates the weighted (by class size) precision.
*
* @return the weighted precision.
*/
public double weightedPrecision() {
double[] classCounts = new double[m_NumClasses];
double classCountSum = 0;
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
classCounts[i] += m_ConfusionMatrix[i][j];
}
classCountSum += classCounts[i];
}
double precisionTotal = 0;
for (int i = 0; i < m_NumClasses; i++) {
double temp = precision(i);
if (classCounts[i] > 0) { // If temp is NaN, we want the sum to also be NaN if count > 0
precisionTotal += (temp * classCounts[i]);
}
}
return precisionTotal / classCountSum;
}
/**
* Calculate the precision with respect to a particular class. This is defined
* as
* <p/>
*
* <pre>
* correctly classified positives
* ------------------------------
* total predicted as positive
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the precision
*/
public double precision(int classIndex) {
double correct = 0, total = 0;
for (int i = 0; i < m_NumClasses; i++) {
if (i == classIndex) {
correct += m_ConfusionMatrix[i][classIndex];
}
total += m_ConfusionMatrix[i][classIndex];
}
return correct / total;
}
/**
* Calculates the weighted (by class size) recall.
*
* @return the weighted recall.
*/
public double weightedRecall() {
return weightedTruePositiveRate();
}
/**
* Calculates the macro weighted (by class size) average F-Measure.
*
* @return the weighted F-Measure.
*/
public double weightedFMeasure() {
double[] classCounts = new double[m_NumClasses];
double classCountSum = 0;
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
classCounts[i] += m_ConfusionMatrix[i][j];
}
classCountSum += classCounts[i];
}
double fMeasureTotal = 0;
for (int i = 0; i < m_NumClasses; i++) {
double temp = fMeasure(i);
if (classCounts[i] > 0) { // If temp is NaN, we want the sum to also be NaN if count > 0
fMeasureTotal += (temp * classCounts[i]);
}
}
return fMeasureTotal / classCountSum;
}
/**
* Calculate the F-Measure with respect to a particular class. This is defined
* as
* <p/>
*
* <pre>
* 2 * recall * precision
* ----------------------
* recall + precision
* </pre>
*
* Returns zero when both precision and recall are zero
*
* @param classIndex the index of the class to consider as "positive"
* @return the F-Measure
*/
public double fMeasure(int classIndex) {
double precision = precision(classIndex);
double recall = recall(classIndex);
if ((precision == 0) && (recall == 0)) {
return 0;
}
return 2 * precision * recall / (precision + recall);
}
/**
* Calculate the recall with respect to a particular class. This is defined as
* <p/>
*
* <pre>
* correctly classified positives
* ------------------------------
* total positives
* </pre>
* <p/>
* (Which is also the same as the truePositiveRate.)
*
* @param classIndex the index of the class to consider as "positive"
* @return the recall
*/
public double recall(int classIndex) {
return truePositiveRate(classIndex);
}
/**
* Calculates the weighted (by class size) matthews correlation coefficient.
*
* @return the weighted matthews correlation coefficient.
*/
public double weightedMatthewsCorrelation() {
double[] classCounts = new double[m_NumClasses];
double classCountSum = 0;
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
classCounts[i] += m_ConfusionMatrix[i][j];
}
classCountSum += classCounts[i];
}
double mccTotal = 0;
for (int i = 0; i < m_NumClasses; i++) {
double temp = matthewsCorrelationCoefficient(i);
if (classCounts[i] > 0) { // If temp is NaN, we want the sum to also be NaN if count > 0
mccTotal += (temp * classCounts[i]);
}
}
return mccTotal / classCountSum;
}
/**
* Calculates the matthews correlation coefficient (sometimes called phi
* coefficient) for the supplied class
*
* @param classIndex the index of the class to compute the matthews
* correlation coefficient for
*
* @return the mathews correlation coefficient
*/
public double matthewsCorrelationCoefficient(int classIndex) {
double numTP = numTruePositives(classIndex);
double numTN = numTrueNegatives(classIndex);
double numFP = numFalsePositives(classIndex);
double numFN = numFalseNegatives(classIndex);
double n = (numTP * numTN) - (numFP * numFN);
double d =
(numTP + numFP) * (numTP + numFN) * (numTN + numFP) * (numTN + numFN);
d = Math.sqrt(d);
return n / d;
}
/**
* Calculate the number of true positives with respect to a particular class.
* This is defined as
* <p/>
*
* <pre>
* correctly classified positives
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the true positive rate
*/
public double numTruePositives(int classIndex) {
double correct = 0;
for (int j = 0; j < m_NumClasses; j++) {
if (j == classIndex) {
correct += m_ConfusionMatrix[classIndex][j];
}
}
return correct;
}
/**
* Calculate the number of true negatives with respect to a particular class.
* This is defined as
* <p/>
*
* <pre>
* correctly classified negatives
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the true positive rate
*/
public double numTrueNegatives(int classIndex) {
double correct = 0;
for (int i = 0; i < m_NumClasses; i++) {
if (i != classIndex) {
for (int j = 0; j < m_NumClasses; j++) {
if (j != classIndex) {
correct += m_ConfusionMatrix[i][j];
}
}
}
}
return correct;
}
/**
* Calculate number of false positives with respect to a particular class.
* This is defined as
* <p/>
*
* <pre>
* incorrectly classified negatives
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the false positive rate
*/
public double numFalsePositives(int classIndex) {
double incorrect = 0;
for (int i = 0; i < m_NumClasses; i++) {
if (i != classIndex) {
for (int j = 0; j < m_NumClasses; j++) {
if (j == classIndex) {
incorrect += m_ConfusionMatrix[i][j];
}
}
}
}
return incorrect;
}
/**
* Calculate number of false negatives with respect to a particular class.
* This is defined as
* <p/>
*
* <pre>
* incorrectly classified positives
* </pre>
*
* @param classIndex the index of the class to consider as "positive"
* @return the false positive rate
*/
public double numFalseNegatives(int classIndex) {
double incorrect = 0;
for (int i = 0; i < m_NumClasses; i++) {
if (i == classIndex) {
for (int j = 0; j < m_NumClasses; j++) {
if (j != classIndex) {
incorrect += m_ConfusionMatrix[i][j];
}
}
}
}
return incorrect;
}
/**
* Returns value of kappa statistic if class is nominal.
*
* @return the value of the kappa statistic
*/
public final double kappa() {
double[] sumRows = new double[m_ConfusionMatrix.length];
double[] sumColumns = new double[m_ConfusionMatrix.length];
double sumOfWeights = 0;
for (int i = 0; i < m_ConfusionMatrix.length; i++) {
for (int j = 0; j < m_ConfusionMatrix.length; j++) {
sumRows[i] += m_ConfusionMatrix[i][j];
sumColumns[j] += m_ConfusionMatrix[i][j];
sumOfWeights += m_ConfusionMatrix[i][j];
}
}
double correct = 0, chanceAgreement = 0;
for (int i = 0; i < m_ConfusionMatrix.length; i++) {
chanceAgreement += (sumRows[i] * sumColumns[i]);
correct += m_ConfusionMatrix[i][i];
}
chanceAgreement /= (sumOfWeights * sumOfWeights);
correct /= sumOfWeights;
if (chanceAgreement < 1) {
return (correct - chanceAgreement) / (1 - chanceAgreement);
} else {
return 1;
}
}
/**
* Outputs the performance statistics in summary form. Lists number (and
* percentage) of instances classified correctly, incorrectly and
* unclassified. Outputs the total number of instances classified, and the
* number of instances (if any) that had no class value provided.
*
* @param title the title for the statistics
* @return the summary as a String
*/
public String toSummaryString(String title) {
StringBuffer text = new StringBuffer();
text.append(title + "\n");
text.append("Correctly Classified Instances ");
text.append(Utils.doubleToString(correct(), 12, 4) + " "
+ Utils.doubleToString(pctCorrect(), 12, 4) + " %\n");
text.append("Incorrectly Classified Instances ");
text.append(Utils.doubleToString(incorrect(), 12, 4) + " "
+ Utils.doubleToString(pctIncorrect(), 12, 4) + " %\n");
text.append("Kappa statistic ");
text.append(Utils.doubleToString(kappa(), 12, 4) + "\n");
text.append("Total Number of Instances ");
text.append(Utils.doubleToString(m_WithClass, 12, 4) + "\n");
return text.toString();
}
/**
* Outputs the performance statistics as a classification confusion matrix.
* For each class value, shows the distribution of predicted class values.
*
* @param title the title for the confusion matrix
* @return the confusion matrix as a String
* @throws Exception if the class is numeric
*/
public String toMatrixString(String title) {
StringBuffer text = new StringBuffer();
char[] IDChars =
{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
int IDWidth;
boolean fractional = false;
// Find the maximum value in the matrix
// and check for fractional display requirement
double maxval = 0;
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
double current = m_ConfusionMatrix[i][j];
if (current < 0) {
current *= -10;
}
if (current > maxval) {
maxval = current;
}
double fract = current - Math.rint(current);
if (!fractional && ((Math.log(fract) / Math.log(10)) >= -2)) {
fractional = true;
}
}
}
IDWidth =
1 + Math.max(
(int) (Math.log(maxval) / Math.log(10) + (fractional ? 3 : 0)),
(int) (Math.log(m_NumClasses) / Math.log(IDChars.length)));
text.append(title).append("\n");
for (int i = 0; i < m_NumClasses; i++) {
if (fractional) {
text.append(" ").append(num2ShortID(i, IDChars, IDWidth - 3))
.append(" ");
} else {
text.append(" ").append(num2ShortID(i, IDChars, IDWidth));
}
}
text.append(" <-- classified as\n");
for (int i = 0; i < m_NumClasses; i++) {
for (int j = 0; j < m_NumClasses; j++) {
text.append(" ").append(
Utils.doubleToString(m_ConfusionMatrix[i][j], IDWidth,
(fractional ? 2 : 0)));
}
text.append(" | ").append(num2ShortID(i, IDChars, IDWidth)).append(" = ")
.append(m_ClassNames[i]).append("\n");
}
return text.toString();
}
/**
* Generates a breakdown of the accuracy for each class, incorporating various
* information-retrieval statistics, such as true/false positive rate,
* precision/recall/F-Measure. Should be useful for ROC curves,
* recall/precision curves.
*
* @param title the title to prepend the stats string with
* @return the statistics presented as a string
* @throws Exception if class is not nominal
*/
public String toClassDetailsString(String title) {
boolean displayTP = true;
boolean displayFP = true;
boolean displayP = true;
boolean displayR = true;
boolean displayFM = true;
boolean displayMCC = true;
StringBuffer text =
new StringBuffer(title + "\n "
+ "TP Rate " + "FP Rate "
+ "Precision " + "Recall "
+ "F-Measure " + "MCC ");
text.append("Class\n");
for (int i = 0; i < m_NumClasses; i++) {
text.append(" ");
if (displayTP) {
double tpr = truePositiveRate(i);
if (Utils.isMissingValue(tpr)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", tpr));
}
}
if (displayFP) {
double fpr = falsePositiveRate(i);
if (Utils.isMissingValue(fpr)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", fpr));
}
}
if (displayP) {
double p = precision(i);
if (Utils.isMissingValue(p)) {
text.append("? ");
} else {
text.append(String.format("%-11.3f", precision(i)));
}
}
if (displayR) {
double r = recall(i);
if (Utils.isMissingValue(r)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", recall(i)));
}
}
if (displayFM) {
double fm = fMeasure(i);
if (Utils.isMissingValue(fm)) {
text.append("? ");
} else {
text.append(String.format("%-11.3f", fMeasure(i)));
}
}
if (displayMCC) {
double mat = matthewsCorrelationCoefficient(i);
if (Utils.isMissingValue(mat)) {
text.append("? ");
} else {
text.append(String
.format("%-9.3f", matthewsCorrelationCoefficient(i)));
}
}
text.append(m_ClassNames[i]).append('\n');
}
text.append("Weighted Avg. ");
if (displayTP) {
double wtpr = weightedTruePositiveRate();
if (Utils.isMissingValue(wtpr)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", wtpr));
}
}
if (displayFP) {
double wfpr = weightedFalsePositiveRate();
if (Utils.isMissingValue(wfpr)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", wfpr));
}
}
if (displayP) {
double wp = weightedPrecision();
if (Utils.isMissingValue(wp)) {
text.append("? ");
} else {
text.append(String.format("%-11.3f", wp));
}
}
if (displayR) {
double wr = weightedRecall();
if (Utils.isMissingValue(wr)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", wr));
}
}
if (displayFM) {
double wf = weightedFMeasure();
if (Utils.isMissingValue(wf)) {
text.append("? ");
} else {
text.append(String.format("%-11.3f", wf));
}
}
if (displayMCC) {
double wmc = weightedMatthewsCorrelation();
if (Utils.isMissingValue(wmc)) {
text.append("? ");
} else {
text.append(String.format("%-9.3f", wmc));
}
}
text.append("\n");
return text.toString();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
for (int rowIndex = 0; rowIndex < m_ConfusionMatrix.length; rowIndex++) {
builder.append("[");
for (int colIndex = 0; colIndex < m_ConfusionMatrix[rowIndex].length; colIndex++) {
int value = (int) (m_ConfusionMatrix[rowIndex][colIndex]);
builder.append(value);
if (colIndex < m_ConfusionMatrix[rowIndex].length - 1) {
builder.append(",");
}
}
builder.append("]");
}
builder.append("]");
return String.format("{Accuracy:%.3f,Precision:%.3f,Recall:%.3f,F1 Score:%.3f,Matrix:%s}",
pctCorrect() / 100.0, weightedPrecision(), weightedRecall(), weightedFMeasure(), builder.toString());
}
/**
* Method for generating indices for the confusion matrix.
*
* @param num integer to format
* @param IDChars the characters to use
* @param IDWidth the width of the entry
* @return the formatted integer as a string
*/
private String num2ShortID(int num, char[] IDChars, int IDWidth) {
char ID[] = new char[IDWidth];
int i;
for (i = IDWidth - 1; i >= 0; i--) {
ID[i] = IDChars[num % IDChars.length];
num = num / IDChars.length - 1;
if (num < 0) {
break;
}
}
for (i--; i >= 0; i--) {
ID[i] = ' ';
}
return new String(ID);
}
public static double[][] initConfusionMatrix(int classNum) {
double[][] confusionMatrix = new double[classNum][classNum];
for (double[] row : confusionMatrix) {
Arrays.fill(row, 0);
}
return confusionMatrix;
}
public static void matrixAddMatrix(double[][] dest, double[][] src) {
Assertion.assertEqual(dest.length, src.length);
for (int i = 0; i < dest.length; i++) {
Assertion.assertEqual(dest[i].length, src[i].length);
for (int j = 0; j < dest[i].length; j++) {
dest[i][j] += src[i][j];
}
}
}
public static String[] initClassNames(int[] classes) {
String[] classNames = new String[classes.length];
for (int i = 0; i < classes.length; i++) {
classNames[i] = String.valueOf(classes[i]);
}
return classNames;
}
public static double[][] getConfusionMatrix(int[] classes, List<Integer> trueLabels, List<Integer> predLabels) {
double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
for (int i = 0; i < trueLabels.size(); i++) {
int trueLabel = trueLabels.get(i);
int predLabel = predLabels.get(i);
confusionMatrix[trueLabel][predLabel]++;
}
return confusionMatrix;
}
public static double[][] getConfusionMatrix(int[] classes, List<int[]> truePredLabelPairs) {
double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
for (int[] truePredLabelPair : truePredLabelPairs) {
int trueLabel = truePredLabelPair[0];
int predLabel = truePredLabelPair[1];
int trueLabelIndex = TimeDatabase.argWhere(classes, trueLabel);
int predLabelIndex = TimeDatabase.argWhere(classes, predLabel);
confusionMatrix[trueLabelIndex][predLabelIndex]++;
}
return confusionMatrix;
}
public static Evaluation evaluate(String title, int[] classes, List<int[]> truePredLabelPairs, boolean isPrint) {
Evaluation evaluation = new Evaluation(classes, truePredLabelPairs);
if (isPrint) {
Configuration.out.println("================================================================================");
Configuration.out.print(evaluation.toSummaryString("[" + title + "]"));
Configuration.out.print(evaluation.toMatrixString(""));
Configuration.out.print(evaluation.toClassDetailsString(""));
Configuration.out.println("================================================================================");
}
return evaluation;
}
public static double[] softmax(double[] probDist) {
double[] tmp = new double[probDist.length];
for (int i = 0; i < probDist.length; i++) {
tmp[i] = Math.exp(probDist[i]);
}
return KStepTransitionMatrix.normalize(tmp);
}
public static void describe(Instances instances, String tag) {
int[] classCount = TimeDatabase.zeros(instances.numClasses());
for (int i = 0; i < instances.numInstances(); i++) {
Instance instance = instances.instance(i);
classCount[(int) instance.classValue()]++;
}
for (int i = 0; i < classCount.length; i++) {
Configuration.out.printf("#(%d)=%4d ", i, classCount[i]);
}
Configuration.out.printf("<- %s\n", tag);
}
public static double entropy(double[] probDist) {
double result = 0.0;
for (double prob : probDist) {
result -= (prob * Math.log(prob));
}
return result;
}
public static int min(int[] array) {
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
}
}
return min;
}
public static int argMin(int[] array) {
int min = array[0];
int minIndex = 0;
for (int i = 1; i < array.length; i++) {
if (array[i] < min) {
min = array[i];
minIndex = i;
}
}
return minIndex;
}
public static double distance(double[][] m1, double[][] m2) {
double result = 0.0;
Assertion.assertEqual(m1.length, m2.length);
for (int row = 0; row < m1.length; row++) {
Assertion.assertEqual(m1[row].length, m2[row].length);
for (int col = 0; col < m1[row].length; col++) {
double v1 = m1[row][col];
double v2 = m2[row][col];
result += Math.abs(v2 - v1);
}
}
return result;
}
}

View File

@ -0,0 +1,444 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Assertion;
import weka.classifiers.Classifier;
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.trees.J48;
import weka.classifiers.trees.RandomForest;
import weka.core.Instance;
import weka.core.Instances;
import java.math.BigDecimal;
import java.util.*;
public class HOCMEMM extends Model {
private int n; // order of Markov chain model
private long offset; // time interval of the time point (i.e., instance)
private double[] lambdas; // weights of each order, used in high-order Markov chain model
private double sameTeamWeight; // weight for users from the same team
private double diffTeamWeight; // weight for users from different teams
private boolean isIntraEnabled; // whether the intra-personal correlation is enabling
private boolean isInterEnabled; // whether the inter-personal correlation is enabling
private int seed = 1; // initial seed for the meta classifier
public static class MyLearningEpochListener implements SemiSupervisedLearningStrategy.LearningEpochListener {
public void learnerInitialized(
Classifier classifier, Model model, ModelPerformanceSampler sampler,
LearningStrategy strategy, TimeDatabase trainDb, TimeDatabase validDb) throws Exception {
String identifier = String.format("%s+%s+init", model.getName(), strategy.getName());
Configuration.out.println(identifier + "{");
// information of both the training and the validation sets
Configuration.out.println(trainDb.simpleDescribe("train"));
Configuration.out.println(validDb == null ? "valid{null}" : validDb.simpleDescribe("valid"));
// k-step transition matrix
Map<String, KStepTransitionMatrix> ktms = trainDb.getKStepTransitionMatrix(Configuration.N, Configuration.WINDOW_SIZE_IN_MILLISECOND);
List<String> keys1 = new ArrayList<String>(ktms.keySet());
Collections.sort(keys1);
for (String key : keys1) {
Configuration.out.println(ktms.get(key).describeIn1D());
}
// valid
if (validDb != null) {
Map<String, List<int[]>> truePredLabelPairsMap = model.predict(classifier, trainDb, validDb, sampler);
List<String> keys2 = new ArrayList<String>(truePredLabelPairsMap.keySet());
Collections.sort(keys2);
for (String key : keys2) {
Evaluation evaluation = new Evaluation(validDb.getClasses(), truePredLabelPairsMap.get(key));
Configuration.out.println(key + evaluation.toString());
}
validDb.cleanCache();
}
Configuration.out.println("}");
}
public void learnerRetrained(int epoch, Classifier classifier, Model model, ModelPerformanceSampler sampler, LearningStrategy strategy, TimeDatabase oldTrainDb, TimeDatabase newTrainDb, TimeDatabase validDb) throws Exception {
String identifier = String.format("%s+%s+%d", model.getName(), strategy.getName(), epoch);
Configuration.out.println(identifier + "{");
// information of both the training and the validation sets
Configuration.out.println(newTrainDb.simpleDescribe("train"));
Configuration.out.println(validDb == null ? "valid{null}" : validDb.simpleDescribe("valid"));
// distance
double distance = SemiSupervisedLearningStrategy.distance(
oldTrainDb.getKStepTransitionMatrix(Configuration.N, Configuration.WINDOW_SIZE_IN_MILLISECOND),
newTrainDb.getKStepTransitionMatrix(Configuration.N, Configuration.WINDOW_SIZE_IN_MILLISECOND), false, "");
if (distance == 0.0) {
Configuration.out.println("retain");
} else {
Configuration.out.println("change:" + new BigDecimal(distance).toString());
}
// k-step transition matrix
Map<String, KStepTransitionMatrix> ktms = newTrainDb.getKStepTransitionMatrix(Configuration.N, Configuration.WINDOW_SIZE_IN_MILLISECOND);
List<String> keys1 = new ArrayList<String>(ktms.keySet());
Collections.sort(keys1);
for (String key : keys1) {
Configuration.out.println(ktms.get(key).describeIn1D());
}
// valid
if (validDb != null) {
Map<String, List<int[]>> truePredLabelPairsMap = model.predict(classifier, newTrainDb, validDb, sampler);
List<String> keys2 = new ArrayList<String>(truePredLabelPairsMap.keySet());
Collections.sort(keys2);
for (String key : keys2) {
Evaluation evaluation = new Evaluation(validDb.getClasses(), truePredLabelPairsMap.get(key));
Configuration.out.println(key + evaluation.toString());
}
validDb.cleanCache();
}
Configuration.out.println("}");
}
}
public static class MyModelPerformanceSampler extends Model.DefaultModelPerformanceSampler {
public static final String OBSER_TAG = "obser";
public static final String INTRA_TAG = "intra";
public static final String INTER_TAG = "inter";
public MyModelPerformanceSampler(Model model, boolean isPrintInfo) {
super(model, isPrintInfo);
}
@SuppressWarnings("unchecked")
@Override
public List<int[]> collectTrueAndPredLabels(String tag, TimeDatabase testDb, Object... args) {
if (OBSER_TAG.equals(tag)) {
return afterObserPredictionDone(tag, testDb);
}
if (INTRA_TAG.equals(tag)) {
Map<String, KStepTransitionMatrix> tcm = (Map<String, KStepTransitionMatrix>) args[0];
return afterIntraPredictionDone(tag, testDb, tcm);
}
if (INTER_TAG.equals(tag)) {
Map<String, KStepTransitionMatrix> scm = (Map<String, KStepTransitionMatrix>) args[0];
return afterInterPredictionDone(tag, testDb, scm);
}
if (FINAL_TAG.equals(tag)) {
return afterFinalPredictionDone(tag, testDb);
}
throw new IllegalArgumentException(String.format("Tag '%s' is not defined for sampling %s's performance.", tag, this.model.getName()));
}
public List<int[]> afterObserPredictionDone(String tag, TimeDatabase testDb) {
int[] classes = testDb.getClasses();
// double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
List<int[]> truePredLabelPairs = new ArrayList<int[]>();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int trueLabel = tp.getLabel();
Assertion.assertNotNegative(trueLabel);
double[] obserProbDist = tp.getObserProbDist();
Assertion.assertNotNull(obserProbDist);
int predLabel = classes[KStepTransitionMatrix.argMax(obserProbDist)];
// confusionMatrix[trueLabel][predLabel]++;
truePredLabelPairs.add(new int[]{trueLabel, predLabel});
}
}
}
if (this.isPrintInfo) {
Evaluation.evaluate(String.format("%s:%s", this.model.getName(), tag), classes, truePredLabelPairs, true);
}
// return confusionMatrix;
return truePredLabelPairs;
}
public List<int[]> afterIntraPredictionDone(String tag, TimeDatabase testDb, Map<String, KStepTransitionMatrix> tcm) {
int[] classes = testDb.getClasses();
// double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
List<int[]> truePredLabelPairs = new ArrayList<int[]>();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int trueLabel = tp.getLabel();
Assertion.assertNotNegative(trueLabel);
double[] obserProbDist = tp.getObserProbDist();
Assertion.assertNotNull(obserProbDist);
double[] intraProbDist = tp.getIntraProbDist();
Assertion.assertNotNull(intraProbDist);
double[] compoProbDist = KStepTransitionMatrix.zeros(classes.length);
KStepTransitionMatrix.vectorAddVector(compoProbDist, obserProbDist);
KStepTransitionMatrix.vectorAddVector(compoProbDist, intraProbDist);
compoProbDist = Evaluation.softmax(compoProbDist);
int predLabel = classes[KStepTransitionMatrix.argMax(compoProbDist)];
// confusionMatrix[trueLabel][predLabel]++;
truePredLabelPairs.add(new int[]{trueLabel, predLabel});
}
}
}
if (this.isPrintInfo) {
Evaluation.evaluate(String.format("%s:%s", this.model.getName(), tag), classes, truePredLabelPairs, true);
for (KStepTransitionMatrix matrix : tcm.values()) {
Configuration.out.print(matrix.toString());
}
}
// return confusionMatrix;
return truePredLabelPairs;
}
public List<int[]> afterInterPredictionDone(String tag, TimeDatabase testDb, Map<String, KStepTransitionMatrix> scm) {
int[] classes = testDb.getClasses();
// double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
List<int[]> truePredLabelPairs = new ArrayList<int[]>();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int trueLabel = tp.getLabel();
Assertion.assertNotNegative(trueLabel);
double[] obserProbDist = tp.getObserProbDist();
Assertion.assertNotNull(obserProbDist);
double[] interProbDist = tp.getInterProbDist();
Assertion.assertNotNull(interProbDist);
double[] compoProbDist = KStepTransitionMatrix.zeros(classes.length);
KStepTransitionMatrix.vectorAddVector(compoProbDist, obserProbDist);
KStepTransitionMatrix.vectorAddVector(compoProbDist, interProbDist);
compoProbDist = Evaluation.softmax(compoProbDist);
int predLabel = classes[KStepTransitionMatrix.argMax(compoProbDist)];
// confusionMatrix[trueLabel][predLabel]++;
truePredLabelPairs.add(new int[]{trueLabel, predLabel});
}
}
}
if (this.isPrintInfo) {
Evaluation.evaluate(String.format("%s:%s", this.model.getName(), tag), classes, truePredLabelPairs, true);
for (KStepTransitionMatrix matrix : scm.values()) {
Configuration.out.print(matrix.toString());
}
}
// return confusionMatrix;
return truePredLabelPairs;
}
public List<int[]> afterFinalPredictionDone(String tag, TimeDatabase testDb) {
int[] classes = testDb.getClasses();
// double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
List<int[]> truePredLabelPairs = new ArrayList<int[]>();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int trueLabel = tp.getLabel();
Assertion.assertNotNegative(trueLabel);
double[] finalProbDist = tp.getFinalProbDist();
Assertion.assertNotNull(finalProbDist);
int predLabel = classes[KStepTransitionMatrix.argMax(finalProbDist)];
// confusionMatrix[trueLabel][predLabel]++;
truePredLabelPairs.add(new int[]{trueLabel, predLabel});
}
}
}
if (this.isPrintInfo) {
Evaluation.evaluate(String.format("%s:%s", this.model.getName(), tag), classes, truePredLabelPairs, true);
}
// return confusionMatrix;
return truePredLabelPairs;
}
}
public HOCMEMM(String name, int n, long offset, double base, double sameTeamWeight, double diffTeamWeight, boolean isIntraEnabled, boolean isInterEnabled, int seed) {
super(name);
this.n = n;
this.offset = offset;
this.lambdas = new double[this.n];
this.lambdas[0] = base;
for (int i = 1; i < n; i++) {
lambdas[i] = lambdas[i - 1] * base;
}
this.sameTeamWeight = sameTeamWeight;
this.diffTeamWeight = diffTeamWeight;
this.isIntraEnabled = isIntraEnabled;
this.isInterEnabled = isInterEnabled;
this.seed = seed;
}
public HOCMEMM(String name, int n, long offset, double base, double sameTeamWeight, double diffTeamWeight, boolean isIntraEnabled, boolean isInterEnabled) {
this(name, n, offset, base, sameTeamWeight, diffTeamWeight, isIntraEnabled, isInterEnabled, 1);
}
@Override
public Classifier fit(TimeDatabase trainDb) throws Exception {
Instances instances = trainDb.getLabeledInstances();
if (instances == null) {
throw new NullPointerException("No labeled data in the training set!");
}
RandomForest metaClassifier = new RandomForest();
metaClassifier.setSeed(this.seed);
metaClassifier.buildClassifier(instances);
return metaClassifier;
}
@Override
public Map<String, List<int[]>> predict(Classifier classifier, TimeDatabase trainDb, TimeDatabase testDb, ModelPerformanceSampler sampler) throws Exception {
Map<String, List<int[]>> confusionMatrices = new HashMap<String, List<int[]>>();
predictObserProbDist(classifier, testDb);
confusionMatrices.put(MyModelPerformanceSampler.OBSER_TAG,
sampler.collectTrueAndPredLabels(MyModelPerformanceSampler.OBSER_TAG, testDb));
if (isIntraEnabled) {
Map<String, KStepTransitionMatrix> tcm = predictIntraProbDist(trainDb, testDb);
confusionMatrices.put(MyModelPerformanceSampler.INTRA_TAG,
sampler.collectTrueAndPredLabels(MyModelPerformanceSampler.INTRA_TAG, testDb, tcm));
}
if (isInterEnabled) {
Map<String, KStepTransitionMatrix> scm = predictInterProbDist(trainDb, testDb);
confusionMatrices.put(MyModelPerformanceSampler.INTER_TAG,
sampler.collectTrueAndPredLabels(MyModelPerformanceSampler.INTER_TAG, testDb, scm));
}
predictFinalProbDist(testDb);
confusionMatrices.put(MyModelPerformanceSampler.FINAL_TAG,
sampler.collectTrueAndPredLabels(MyModelPerformanceSampler.FINAL_TAG, testDb));
return confusionMatrices;
}
private void predictObserProbDist(Classifier classifier, TimeDatabase testDb) throws Exception {
// to tackle the issue: weka.core.UnassignedDatasetException: Instance doesn't have access to a dataset!
testDb.getLabeledAndUnlabeledInstances();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (!tp.isEmpty()) {
Instance instance = tp.getInstance();
Assertion.assertNotNull(instance);
double[] probDist = classifier.distributionForInstance(instance);
tp.setObserProbDist(probDist);
}
}
}
}
private Map<String, KStepTransitionMatrix> predictIntraProbDist(TimeDatabase trainDb, TimeDatabase testDb) {
// estimate intra-personal k-step transition matrix from the training set
Map<String, KStepTransitionMatrix> tcm = trainDb.getTemporalCorrelationMatrix(n, offset);
String[] users = testDb.getUsers();
int classNum = testDb.getClasses().length;
for (int toTpIndex = 0; toTpIndex < testDb.getNumberOfTimePoints(); toTpIndex++) {
for (String user : users) {
List<TimePoint> tps = testDb.findTimeSeriesByUser(user).getTimePoints();
TimePoint toTp = tps.get(toTpIndex);
if (toTp.isEmpty()) {
continue;
}
double[][][] Q_hat = tcm.get(user + "->" + user).getQ_hat();
double[] intraProbDist = KStepTransitionMatrix.zeros(classNum);
adjustPredictionInOneKStepLoop(toTp, toTpIndex, tps, Q_hat, intraProbDist);
toTp.setIntraProbDist(intraProbDist);
}
}
return tcm;
}
private Map<String, KStepTransitionMatrix> predictInterProbDist(TimeDatabase trainDb, TimeDatabase testDb) {
// estimate inter-personal k-step transition matrix from the training set
Map<String, KStepTransitionMatrix> scm = trainDb.getSpatialCorrelationMatrix(n, offset);
String[] users = testDb.getUsers();
int classNum = testDb.getClasses().length;
for (int toTpIndex = 0; toTpIndex < testDb.getNumberOfTimePoints(); toTpIndex++) {
for (String toUser : users) {
TimePoint toTp = testDb.findTimeSeriesByUser(toUser).getTimePoints().get(toTpIndex);
if (toTp.isEmpty()) {
continue;
}
String toTeam = testDb.findTimeSeriesByUser(toUser).getTeam();
double[] interProbDist = KStepTransitionMatrix.zeros(classNum);
for (String fromUser : users) {
if (fromUser.equals(toUser)) {
continue;
}
String fromTeam = testDb.findTimeSeriesByUser(fromUser).getTeam();
List<TimePoint> fromTps = testDb.findTimeSeriesByUser(fromUser).getTimePoints();
double[][][] Q_hat = scm.get(fromUser + "->" + toUser).getQ_hat();
double[] probDist = KStepTransitionMatrix.zeros(classNum);
adjustPredictionInOneKStepLoop(toTp, toTpIndex, fromTps, Q_hat, probDist);
if (toTeam.equals(fromTeam)) {
// in same team
probDist = KStepTransitionMatrix.constantTimesVector(sameTeamWeight, probDist);
} else {
// in different team
probDist = KStepTransitionMatrix.constantTimesVector(diffTeamWeight, probDist);
}
KStepTransitionMatrix.vectorAddVector(interProbDist, probDist);
}
toTp.setInterProbDist(interProbDist);
}
}
return scm;
}
/**
* one k-step loop (k == n here):
* toTpIndex - n, ..., toTpIndex - 1, toTpIndex
* fromUser: * ... *
* | ... |
* ----------------------------------
* |
* toUser: *
* @param toTp which toTp to update
* @param toTpIndex index of toTp
* @param fromTps fromTps used to update toTp
* @param Q_hat correlation matrix from fromUser to toUser
* @param probDist placeholder of the k-step loop's result
*/
private void adjustPredictionInOneKStepLoop(TimePoint toTp, int toTpIndex, List<TimePoint> fromTps, double[][][] Q_hat, double[] probDist) {
int fromTpIndexBeg = toTpIndex - 1;
int fromTpIndexEnd = toTpIndex - n;
for (int fromTpIndex = fromTpIndexBeg, step = 0; fromTpIndex >= 0 && fromTpIndex >= fromTpIndexEnd; fromTpIndex--, step++) {
TimePoint fromTp = fromTps.get(fromTpIndex);
if (fromTp.isEmpty()) {
continue;
}
long deltaTime = toTp.getTimestamp() - fromTp.getTimestamp();
Assertion.assertPositive(deltaTime);
if (deltaTime > offset * (step + 1)) {
continue;
}
// lambda * Q_i * X_i
double lambda_i = lambdas[step];
double[][] Q_i = Q_hat[step];
double[] fromProbDist = fromTp.getObserProbDist();
Assertion.assertNotNull(fromProbDist);
double[] QX_i = KStepTransitionMatrix.matrixTimesVector(Q_i, fromProbDist);
double[] lambdaQX_i = KStepTransitionMatrix.constantTimesVector(lambda_i, QX_i);
Assertion.assertAllNotNegative(lambdaQX_i);
KStepTransitionMatrix.vectorAddVector(probDist, lambdaQX_i);
}
}
private void predictFinalProbDist(TimeDatabase db) {
for (TimeSeries ts : db.getTimeSeries()) {
List<TimePoint> tps = ts.getTimePoints();
for (TimePoint tp : tps) {
if (!tp.isEmpty()) {
double[] obserProbDist = tp.getObserProbDist();
double[] intraProbDist = tp.getIntraProbDist();
double[] interProbDist = tp.getInterProbDist();
double[] finalProbDist = KStepTransitionMatrix.zeros(db.getClasses().length);
// add observation probability distribution
Assertion.assertNotNull(obserProbDist);
KStepTransitionMatrix.vectorAddVector(finalProbDist, obserProbDist);
// add intra-personal probability distribution
if (isIntraEnabled) {
Assertion.assertNotNull(intraProbDist);
KStepTransitionMatrix.vectorAddVector(finalProbDist, intraProbDist);
}
// add inter-personal probability distribution
if (isInterEnabled) {
Assertion.assertNotNull(interProbDist);
KStepTransitionMatrix.vectorAddVector(finalProbDist, interProbDist);
}
// restore the final probability distribution
tp.setFinalProbDist(Evaluation.softmax(finalProbDist));
}
}
}
}
}

View File

@ -0,0 +1,258 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import org.apache.commons.math3.util.MathArrays;
public class KStepTransitionMatrix {
private String fromUser;
private String toUser;
private double[][][] Q_hat;
public KStepTransitionMatrix(String fromUser, String toUser, double[][][] Q_hat) {
this.fromUser = fromUser;
this.toUser = toUser;
this.Q_hat = Q_hat;
}
public String getFromUser() {
return fromUser;
}
public String getToUser() {
return toUser;
}
public double[][][] getQ_hat() {
return Q_hat;
}
public static double[] zeros(int length) {
double[] result = new double[length];
for (int i = 0; i < result.length; i++) {
result[i] = 0;
}
return result;
}
public static double[] matrixTimesVector(double[][] matrix, double[] vector) {
// linear combination is indeed the combination of column vectors,
// and the row of the matrix is actually the column vector.
Assertion.assertEqual(matrix.length, vector.length);
double[] result = zeros(matrix[0].length);
for (int i = 0; i < vector.length; i++) {
double[] tmp = constantTimesVector(vector[i], matrix[i]);
vectorAddVector(result, tmp);
}
return result;
}
public static void vectorAddVector(double[] dest, double[] src) {
Assertion.assertTrue(dest.length == src.length);
for (int i = 0; i < dest.length; i++) {
dest[i] += src[i];
}
}
public static double[] constantTimesVector(double constant, double[] vector) {
double[] result = new double[vector.length];
for (int i = 0; i < result.length; i++) {
result[i] = constant * vector[i];
}
return result;
}
public static double sumVector(double[] vector) {
double sum = 0;
for (double value : vector) {
sum += value;
}
return sum;
}
public static double[] normalize(double[] vector) {
double sum = sumVector(vector);
Assertion.assertNotEqual(sum, 0.0);
return MathArrays.normalizeArray(vector, 1.0);
}
public static int argMax(double[] vector) {
double maxValue = vector[0];
int maxIndex = 0;
for (int i = 1; i < vector.length; i++) {
if (vector[i] > maxValue) {
maxValue = vector[i];
maxIndex = i;
}
}
return maxIndex;
}
public static boolean isAllZero(double[] vector) {
for (double value : vector) {
if (value != 0) {
return false;
}
}
return true;
}
public static boolean isUniform(double[] vector) {
for (int i = 1; i < vector.length; i++) {
if (vector[i] != vector[0]) {
return false;
}
}
return true;
}
public static double shannonEntropy(double[] vector) {
double[] vector_hat = normalize(vector);
// -\Sigma (p * ln(p))
double result = 0;
for (int i = 0; i < vector_hat.length; i++) {
if (vector_hat[i] == 0.0) {
continue;
}
result -= (vector_hat[i] * Math.log(vector_hat[i]));
}
return result;
}
public static double klDivergence(double[] P, double[] Q) {
Assertion.assertEqual(P.length, Q.length);
double[] P_hat = normalize(P);
double[] Q_hat = normalize(Q);
double result = 0;
for (int i = 0; i < P_hat.length; i++) {
double p = P_hat[i];
double q = Q_hat[i];
if (p == 0.0) {
continue;
}
if (q == 0.0) {
return Double.POSITIVE_INFINITY;
}
result += (p * Math.log(p / q));
}
return result;
}
public static double jsDivergence(double[] P, double[] Q) {
Assertion.assertEqual(P.length, Q.length);
double[] M = new double[P.length];
for (int i = 0; i < M.length; i++) {
M[i] = (P[i] + Q[i]) / 2.0;
}
return 0.5 * klDivergence(P, M) + 0.5 * klDivergence(Q, M);
}
public static double cosineDistance(double[] P, double[] Q) {
P = normalize(P);
Q = normalize(Q);
Assertion.assertEqual(P.length, Q.length);
double innerProduct = 0;
for (int i = 0; i < P.length; i++) {
innerProduct += (P[i] * Q[i]);
}
return innerProduct / (norm(P) * norm(Q));
}
public static double norm(double[] vector) {
double result = 0;
for (double value : vector) {
result += (value * value);
}
return Math.sqrt(result);
}
private static double[] createAndAssign(int length, double[] src) {
double[] result = zeros(length);
for (int i = 0; i < Math.min(length, src.length); i++) {
result[i] = src[i];
}
return result;
}
public String describeIn1D() {
StringBuilder builder = new StringBuilder();
builder.append(this.fromUser).append("->").append(this.toUser).append("{");
for (int orderIndex = 0; orderIndex < this.Q_hat.length; orderIndex++) {
builder.append(orderIndex).append("[");
for (int rowIndex = 0; rowIndex < this.Q_hat[orderIndex].length; rowIndex++) {
builder.append("[");
for (int colIndex = 0; colIndex < this.Q_hat[orderIndex][rowIndex].length; colIndex++) {
double value = this.Q_hat[orderIndex][rowIndex][colIndex];
Assertion.assertTrue(value >= 0.0 && value <= 1.0);
builder.append(String.format("%.3f", this.Q_hat[orderIndex][rowIndex][colIndex]));
if (colIndex < this.Q_hat[orderIndex][rowIndex].length - 1) {
builder.append(",");
}
}
builder.append("]");
}
builder.append("]");
if (orderIndex < this.Q_hat.length - 1) {
builder.append(",");
}
}
builder.append("}");
return builder.toString();
}
public String describeIn2D() {
String head = this.fromUser + "->" + this.toUser + "\n";
StringBuilder[] rows = new StringBuilder[this.Q_hat[0].length];
for (int i = 0; i < rows.length; i++) {
rows[i] = new StringBuilder();
}
for (int nIndex = 0; nIndex < this.Q_hat.length; nIndex++) {
for (int rowIndex = 0; rowIndex < this.Q_hat[nIndex].length; rowIndex++) {
StringBuilder builder = rows[rowIndex];
if (this.Q_hat[nIndex][rowIndex][0] > 0) {
builder.append(String.format("[%3.1f", this.Q_hat[nIndex][rowIndex][0]));
} else {
builder.append("[ ");
}
for (int colIndex = 1; colIndex < this.Q_hat[nIndex][rowIndex].length; colIndex++) {
if (this.Q_hat[nIndex][rowIndex][colIndex] > 0) {
builder.append(String.format(",%3.1f", this.Q_hat[nIndex][rowIndex][colIndex]));
} else {
builder.append(", ");
}
}
builder.append("]");
if (nIndex < this.Q_hat.length - 1) {
builder.append(" ");
} else {
builder.append("\n");
}
}
}
StringBuilder matrices = new StringBuilder();
matrices.append(head);
for (StringBuilder row : rows) {
matrices.append(row.toString());
}
return matrices.toString();
}
@Override
public String toString() {
return describeIn2D();
}
public static double distance(KStepTransitionMatrix m1, KStepTransitionMatrix m2) {
double result = 0.0;
double[][][] q1 = m1.Q_hat;
double[][][] q2 = m2.Q_hat;
Assertion.assertEqual(q1.length, q2.length);
for (int i = 0; i < q1.length; i++) {
double[][] matrix1 = q1[i];
double[][] matrix2 = q2[i];
result += Evaluation.distance(matrix1, matrix2);
}
return result;
}
}

View File

@ -0,0 +1,53 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import java.util.ArrayList;
import java.util.Set;
public class LabelAllAtOnceLearningStrategy extends SemiSupervisedLearningStrategy {
private int epoch;
public LabelAllAtOnceLearningStrategy(String name, int epoch) {
super(name);
this.epoch = epoch;
}
@Override
protected int assignPseudoLabels(TimeDatabase trainDb, int epoch) {
// int[] classes = trainDb.getClasses();
// // get indices of all the unlabeled data
// Set<TimePointWithProbDist> unlabeledIndices = SemiSupervisedLearningStrategy.indexOfUnlabeledData(trainDb, classes);
// // assign pseudo labels
// SemiSupervisedLearningStrategy.assignPseudoLabelsByIndices(trainDb, new ArrayList<TimePointWithProbDist>(unlabeledIndices));
// return unlabeledIndices.size();
int[] classes = trainDb.getClasses();
for (TimeSeries ts : trainDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (!tp.isEmpty()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
int label = classes[KStepTransitionMatrix.argMax(probDist)];
tp.setLabel(label);
}
}
}
return 1;
}
@Override
protected boolean isStopBeforeUpdate(TimeDatabase trainDb, int epoch) {
return epoch >= this.epoch;
}
@Override
protected boolean isStopAfterUpdate(TimeDatabase trainDb, int epoch) {
if ((epoch + 1) % 10 == 0) {
System.out.print(epoch + 1);
} else {
System.out.print(".");
}
return false;
}
}

View File

@ -0,0 +1,34 @@
package edu.nju.ics.frontier.learning;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class LabelKMostCertainLearningStrategy extends BatchSemiSupervisedLearningStrategy {
public LabelKMostCertainLearningStrategy(String name, int epoch, BatchRate batchRate) {
super(name, epoch, batchRate);
}
@Override
protected int assignPseudoLabels(TimeDatabase trainDb, int epoch) {
int[] classes = trainDb.getClasses();
// get indices of unlabeled data
Set<TimePointWithProbDist> unlabeledIndices = SemiSupervisedLearningStrategy.indexOfUnlabeledData(trainDb, classes);
if (unlabeledIndices.isEmpty()) {
return 0;
}
// divide data by class
List<List<TimePointWithProbDist>> classIndices = SemiSupervisedLearningStrategy.divideDataByClass(unlabeledIndices, classes);
// compute unlabeled batch under each class
int[] classCounts = SemiSupervisedLearningStrategy.scaleDataUnderEachClass(trainDb, classes, batchRate.getBatch(epoch));
// select out indices of unlabeled data to be added into database
List<TimePointWithProbDist> candidateIndices = new ArrayList<TimePointWithProbDist>();
for (int i = 0; i < classCounts.length; i++) {
candidateIndices.addAll(SemiSupervisedLearningStrategy.selectKMostCertainData(
classIndices.get(i), classCounts[i]));
}
// assign pseudo labels
SemiSupervisedLearningStrategy.assignPseudoLabelsByIndices(trainDb, candidateIndices);
return candidateIndices.size();
}
}

View File

@ -0,0 +1,287 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import java.util.*;
public class LabelNeighborFirstLearningStrategy extends BatchSemiSupervisedLearningStrategy {
public static final int INTRA_DIMENSION = 1;
public static final int INTER_DIMENSION = INTRA_DIMENSION << 1;
public static final int BOTH_DIMENSION = INTRA_DIMENSION + INTER_DIMENSION;
private static final int INTRA_DIMENSION_MASK = INTRA_DIMENSION;
private static final int INTER_DIMENSION_MASK = INTER_DIMENSION;
public static final int BACKWARD = 1;
public static final int FORWARD = BACKWARD << 1;
public static final int BIDIRECTIONAL = BACKWARD + FORWARD;
private static final int BACKWARD_MASK = BACKWARD;
private static final int FORWARD_MASK = FORWARD;
private int dimension;
private int direction;
private int n;
private boolean isCrossTeam;
public LabelNeighborFirstLearningStrategy(String name, int epoch, BatchRate batchRate,
int dimension, int direction, int n, boolean isCrossTeam) {
super(name, epoch, batchRate);
this.dimension = dimension;
this.direction = direction;
this.n = n;
this.isCrossTeam = isCrossTeam;
}
@Override
protected int assignPseudoLabels(TimeDatabase trainDb, int epoch) {
int[] classes = trainDb.getClasses();
// get indices of all unlabeled data
Set<TimePointWithProbDist> unlabeledIndices = SemiSupervisedLearningStrategy.indexOfUnlabeledData(trainDb, classes);
if (unlabeledIndices.isEmpty()) {
return 0;
}
// get number of unlabeled data which will be assigned label soon
int[] classCount = SemiSupervisedLearningStrategy.scaleDataUnderEachClass(trainDb, classes, this.batchRate.getBatch(epoch));
// divide data by class
List<List<TimePointWithProbDist>> classLists = SemiSupervisedLearningStrategy.divideDataByClass(unlabeledIndices, classes);
List<TimePointWithProbDist> candidateIndices = new ArrayList<TimePointWithProbDist>();
for (int i = 0; i < classLists.size(); i++) {
List<TimePointWithProbDist> classList = classLists.get(i);
if (classList.size() > classCount[i]) {
List<TimePointWithNeighbor> unlabeledIndexList = new ArrayList<TimePointWithNeighbor>();
// count number of neighbors of each unlabeled data
for (TimePointWithProbDist unlabeledIndex : classList) {
int[] neighborCount = countNeighbors(trainDb, unlabeledIndex, this.dimension, this.direction, this.n, this.isCrossTeam);
unlabeledIndexList.add(new TimePointWithNeighbor(unlabeledIndex, neighborCount[0], neighborCount[1]));
}
// sort by number of labeled neighbor: more neighbor first
Collections.sort(unlabeledIndexList, new Comparator<TimePointWithNeighbor>() {
public int compare(TimePointWithNeighbor o1, TimePointWithNeighbor o2) {
if (o2.labeledNeighbor == o1.labeledNeighbor) {
return o2.unlabeledNeighbor - o1.unlabeledNeighbor;
} else {
return o2.labeledNeighbor - o1.labeledNeighbor;
}
}
});
// select the first k unlabeled data
for (int j = 0; j < classCount[i]; j++) {
candidateIndices.add(unlabeledIndexList.get(j).tp);
}
} else {
candidateIndices.addAll(classList);
}
}
// assign pseudo labels
SemiSupervisedLearningStrategy.assignPseudoLabelsByIndices(trainDb, candidateIndices);
return candidateIndices.size();
// int[] classes = trainDb.getClasses();
// // find indices of neighbors
// Set<TimePointWithProbDist> unlabeledIndices = indexOfNeighbors(trainDb, classes, dimension, direction, n, isCrossTeam);
// // no unlabeled data
// if (unlabeledIndices.isEmpty()) {
// return 0;
// }
// // assign pseudo labels to all unlabeled neighbors
// SemiSupervisedLearningStrategy.assignPseudoLabelsByIndices(trainDb, new ArrayList<TimePointWithProbDist>(unlabeledIndices));
// return unlabeledIndices.size();
// // determine how many unlabeled data will be assigned pseudo labels soon
// int[] classCount = SemiSupervisedLearningStrategy.scaleDataUnderEachClass(trainDb, classes, this.batchRate.getBatch(epoch));
// // divide data by class
// List<List<TimePointWithProbDist>> classLists = SemiSupervisedLearningStrategy.divideDataByClass(unlabeledIndices, classes);
// // select k most certain data
// List<TimePointWithProbDist> candidateList = new ArrayList<TimePointWithProbDist>();
// for (int i = 0; i < classLists.size(); i++) {
// List<TimePointWithProbDist> classList = SemiSupervisedLearningStrategy.selectKMostCertainData(classLists.get(i), classCount[i]);
// candidateList.addAll(classList);
// }
// // assign pseudo labels
// SemiSupervisedLearningStrategy.assignPseudoLabelsByIndices(trainDb, candidateList);
// return candidateList.size();
}
public static Set<TimePointWithProbDist> indexOfNeighbors(TimeDatabase trainDb, int[] classes, int dimension, int direction, int n, boolean isCrossTeam) {
Set<TimePointWithProbDist> result = new HashSet<TimePointWithProbDist>();
Set<TimePointWithProbDist> labeledIndices = SemiSupervisedLearningStrategy.indexOfLabeledData(trainDb, classes);
if ((dimension & INTRA_DIMENSION_MASK) == INTRA_DIMENSION) {
if ((direction & BACKWARD_MASK) == BACKWARD) {
result.addAll(backwardIndexOfIntraNeighbors(trainDb, labeledIndices, classes, n));
}
if ((direction & FORWARD_MASK) == FORWARD) {
result.addAll(forwardIndexOfIntraNeighbors(trainDb, labeledIndices, classes, n));
}
}
if ((dimension & INTER_DIMENSION_MASK) == INTER_DIMENSION) {
if ((direction & BACKWARD_MASK) == BACKWARD) {
result.addAll(backwardIndexOfInterNeighbors(trainDb, labeledIndices, classes, n, isCrossTeam));
}
if ((direction & FORWARD_MASK) == FORWARD) {
result.addAll(forwardIndexOfInterNeighbors(trainDb, labeledIndices, classes, n, isCrossTeam));
}
}
return result;
}
private static Set<TimePointWithProbDist> backwardIndexOfIntraNeighbors(TimeDatabase trainDb, Set<TimePointWithProbDist> labeledIndices, int[] classes, int n) {
Set<TimePointWithProbDist> result = new HashSet<TimePointWithProbDist>();
for (TimePointWithProbDist labeledIndex : labeledIndices) {
List<TimePoint> tps = trainDb.findTimeSeriesByUser(labeledIndex.user).getTimePoints();
for (int i = Math.max(0, labeledIndex.index - n); i < labeledIndex.index; i++) {
TimePoint tp = tps.get(i);
if (tp.isUnlabeled()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
result.add(new TimePointWithProbDist(labeledIndex.team, labeledIndex.user, i, probDist, classes));
}
}
}
return result;
}
private static Set<TimePointWithProbDist> forwardIndexOfIntraNeighbors(TimeDatabase trainDb, Set<TimePointWithProbDist> labeledIndices, int[] classes, int n) {
Set<TimePointWithProbDist> result = new HashSet<TimePointWithProbDist>();
for (TimePointWithProbDist labeledIndex : labeledIndices) {
List<TimePoint> tps = trainDb.findTimeSeriesByUser(labeledIndex.user).getTimePoints();
for (int i = labeledIndex.index + 1, t = Math.min(tps.size(), labeledIndex.index + 1 + n); i < t; i++) {
TimePoint tp = tps.get(i);
if (tp.isUnlabeled()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
result.add(new TimePointWithProbDist(labeledIndex.team, labeledIndex.user, i, probDist, classes));
}
}
}
return result;
}
private static Set<TimePointWithProbDist> backwardIndexOfInterNeighbors(TimeDatabase trainDb, Set<TimePointWithProbDist> labeledIndices, int[] classes, int n, boolean isCrossTeam) {
Set<TimePointWithProbDist> result = new HashSet<TimePointWithProbDist>();
for (TimePointWithProbDist labeledIndex : labeledIndices) {
for (TimeSeries ts : trainDb.getTimeSeries()) {
if (ts.getUser().equals(labeledIndex.user)) {
continue;
}
if (isCrossTeam && (!ts.getTeam().equals(labeledIndex.team))) {
continue;
}
String team = ts.getTeam();
String user = ts.getUser();
List<TimePoint> tps = ts.getTimePoints();
for (int i = Math.max(0, labeledIndex.index - n); i < labeledIndex.index; i++) {
TimePoint tp = tps.get(i);
if (tp.isUnlabeled()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
result.add(new TimePointWithProbDist(team, user, i, probDist, classes));
}
}
}
}
return result;
}
private static Set<TimePointWithProbDist> forwardIndexOfInterNeighbors(TimeDatabase trainDb, Set<TimePointWithProbDist> labeledIndices, int[] classes, int n, boolean isCrossTeam) {
Set<TimePointWithProbDist> result = new HashSet<TimePointWithProbDist>();
for (TimePointWithProbDist labeledIndex : labeledIndices) {
for (TimeSeries ts : trainDb.getTimeSeries()) {
if (ts.getUser().equals(labeledIndex.user)) {
continue;
}
if (isCrossTeam && (!ts.getTeam().equals(labeledIndex.team))) {
continue;
}
String team = ts.getTeam();
String user = ts.getUser();
List<TimePoint> tps = ts.getTimePoints();
for (int i = labeledIndex.index + 1, t = Math.min(tps.size(), labeledIndex.index + 1 + n); i < t; i++) {
TimePoint tp = tps.get(i);
if (tp.isUnlabeled()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
result.add(new TimePointWithProbDist(team, user, i, probDist, classes));
}
}
}
}
return result;
}
private static class TimePointWithNeighbor {
public final TimePointWithProbDist tp;
public final int labeledNeighbor;
public final int unlabeledNeighbor;
public int entropyRank;
public int labeledNeighborRank;
public int unlabeledNeighborRank;
public TimePointWithNeighbor(TimePointWithProbDist tp, int labeledNeighbor, int unlabeledNeighbor) {
this.tp = tp;
this.labeledNeighbor = labeledNeighbor;
this.unlabeledNeighbor = unlabeledNeighbor;
}
}
public static int[] countNeighbors(TimeDatabase trainDb, TimePointWithProbDist unlabeledIndex,
int dimension, int direction, int n, boolean isCrossTeam) {
int labeledNeighborCount = 0;
int unlabeledNeighborCount = 0;
if ((dimension & INTRA_DIMENSION_MASK) == INTRA_DIMENSION) {
List<TimePoint> tps = trainDb.findTimeSeriesByUser(unlabeledIndex.user).getTimePoints();
if ((direction & BACKWARD_MASK) == BACKWARD) {
for (int i = Math.max(0, unlabeledIndex.index - n); i < unlabeledIndex.index; i++) {
TimePoint tp = tps.get(i);
if (tp.isLabeled()) {
labeledNeighborCount++;
} else if (tp.isUnlabeled()) {
unlabeledNeighborCount++;
}
}
}
if ((direction & FORWARD_MASK) == FORWARD) {
for (int i = unlabeledIndex.index + 1, t = Math.min(trainDb.getNumberOfTimePoints(), unlabeledIndex.index + n + 1); i < t; i++) {
TimePoint tp = tps.get(i);
if (tp.isLabeled()) {
labeledNeighborCount++;
} else if (tp.isUnlabeled()) {
unlabeledNeighborCount++;
}
}
}
}
if ((dimension & INTER_DIMENSION_MASK) == INTER_DIMENSION) {
for (TimeSeries ts : trainDb.getTimeSeries()) {
if (ts.getUser().equals(unlabeledIndex.user)) {
continue;
}
if (!(isCrossTeam || ts.getTeam().equals(unlabeledIndex.team))) {
continue;
}
List<TimePoint> tps = ts.getTimePoints();
if ((direction & BACKWARD_MASK) == BACKWARD) {
for (int i = Math.max(0, unlabeledIndex.index - n); i < unlabeledIndex.index; i++) {
TimePoint tp = tps.get(i);
if (tp.isLabeled()) {
labeledNeighborCount++;
} else if (tp.isUnlabeled()) {
unlabeledNeighborCount++;
}
}
}
if ((direction & FORWARD_MASK) == FORWARD) {
for (int i = unlabeledIndex.index + 1, t = Math.min(trainDb.getNumberOfTimePoints(), unlabeledIndex.index + n + 1); i < t; i++) {
TimePoint tp = tps.get(i);
if (tp.isLabeled()) {
labeledNeighborCount++;
} else if (tp.isUnlabeled()) {
unlabeledNeighborCount++;
}
}
}
}
}
return new int[]{labeledNeighborCount, unlabeledNeighborCount};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
package edu.nju.ics.frontier.learning;
import java.util.List;
import java.util.Map;
public abstract class LearningStrategy {
protected String name;
public LearningStrategy(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract Map<String, List<int[]>> fitAndPredict(Model model, TimeDatabase trainDb, TimeDatabase validDb, TimeDatabase testDb, Model.ModelPerformanceSampler sampler) throws Exception;
}

View File

@ -0,0 +1,120 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Assertion;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
// define models and samplers
Model hocmemm = new HOCMEMM("HOCMEMM",
Configuration.N, Configuration.WINDOW_SIZE_IN_MILLISECOND, Configuration.BASE,
Configuration.SAME_TEAM_WEIGHT, Configuration.DIFF_TEAM_WEIGHT,
Configuration.IS_INTRA_ENABLED, Configuration.IS_INTER_ENABLED);
Model.ModelPerformanceSampler hocmemmSampler = new HOCMEMM.MyModelPerformanceSampler(hocmemm, false);
Model rf = new RFModel("RF");
Model.ModelPerformanceSampler rfSampler = new Model.DefaultModelPerformanceSampler(rf, false);
// define learning strategies
LearningStrategy supervised = new SupervisedLearningStrategy("supervised");
LearningStrategy labelAllAtOnce = new LabelAllAtOnceLearningStrategy("labelAllAtOnce", 100);
LearningStrategy kmc = new LabelKMostCertainLearningStrategy("kmc", 10000, new BatchSemiSupervisedLearningStrategy.BatchRate() {
public int getBatch(int epoch) {
return 300;
}
});
// double[] eraseRates = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
double[] eraseRates = new double[]{/*0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, */0.99};
int start = 0;
int round = start + 1;
for (double eraseRate : eraseRates) {
for (int i = start; i < round; i++) {
Configuration.update(String.format("erase_rate_%.2f", eraseRate));
System.out.printf("[%d] removing %.1f%% labeled data from the training set: ", i, eraseRate * 100.0);
long begTime = System.currentTimeMillis();
learningWithRemovingPartialLabels(
hocmemm,
hocmemmSampler,
new LearningStrategy[]{
// supervised,
labelAllAtOnce,
// kmc,
},
eraseRate,
String.format("%s_erase_%.2f_round_%02d", Configuration.DATA_ROOT, eraseRate, i));
long endTime = System.currentTimeMillis();
long deltaTime = (endTime - begTime) / 1000;
long minute = deltaTime / 60;
long second = deltaTime % 60;
System.out.printf("%2d min %2d sec.\n", minute, second);
}
}
}
private static void learningWithRemovingPartialLabels(
Model model, Model.ModelPerformanceSampler sampler, LearningStrategy[] learningStrategies,
final double eraseRate, String root) throws Exception {
MultiLearningStrategyTrainAndTestSession mss = new MultiLearningStrategyTrainAndTestSession(
"mss", 10, 0.0, false) {
@Override
protected TimeDatabase[] prepareTrainValidAndTestSets(TimeDatabase trainDb, TimeDatabase testDb) {
trainDb.erasePartialLabels(eraseRate);
return new TimeDatabase[]{trainDb, testDb.copy(), testDb};
}
};
mss.setModel(model, sampler);
SemiSupervisedLearningStrategy.LearningEpochListener learningEpochListener = new HOCMEMM.MyLearningEpochListener();
for (LearningStrategy learningStrategy : learningStrategies) {
if (learningStrategy instanceof SemiSupervisedLearningStrategy) {
((SemiSupervisedLearningStrategy) learningStrategy).setLearningEpochListener(learningEpochListener);
}
mss.addLearningStrategies(learningStrategy);
}
Map<String, Evaluation> evaluationMap = mss.crossValidation(root);
// get combinations of model and strategy
Set<String> modelPlusStrategySet = new HashSet<String>();
Set<String> phaseSet = new HashSet<String>();
for (String key : evaluationMap.keySet()) {
modelPlusStrategySet.add(key.substring(0, key.lastIndexOf(":")));
phaseSet.add(key.substring(key.lastIndexOf(":") + 1));
}
List<String> modelPlusStrategyList = new ArrayList<String>(modelPlusStrategySet);
Collections.sort(modelPlusStrategyList);
List<String> phaseList = new ArrayList<String>(phaseSet);
Collections.sort(phaseList);
// compare difference of prediction between every pair of phase
for (String modelPlusStrategy : modelPlusStrategyList) {
for (int fromPhaseIndex = 0, s = phaseList.size(); fromPhaseIndex < s - 1; fromPhaseIndex++) {
String fromPhase = phaseList.get(fromPhaseIndex);
List<int[]> fromTruePredLabelPairs = evaluationMap.get(modelPlusStrategy + ":" + fromPhase).getTruePredLabelPairs();
for (int toPhaseIndex = fromPhaseIndex + 1; toPhaseIndex < s; toPhaseIndex++) {
String toPhase = phaseList.get(toPhaseIndex);
List<int[]> toTruePredLabelPairs = evaluationMap.get(modelPlusStrategy + ":" + toPhase).getTruePredLabelPairs();
Assertion.assertEqual(fromTruePredLabelPairs.size(), toTruePredLabelPairs.size());
int[][] comparisionMatrix = new int[][]{{0, 0}, {0, 0}};
for (int pairIndex = 0; pairIndex < fromTruePredLabelPairs.size(); pairIndex++) {
int[] fromTruePredLabelPair = fromTruePredLabelPairs.get(pairIndex);
int[] toTruePredLabelPair = toTruePredLabelPairs.get(pairIndex);
// check whether two true labels are identical
Assertion.assertEqual(fromTruePredLabelPair[0], toTruePredLabelPair[0]);
// check whether the predicted label is same as the true label
boolean isFromPredLabelTrue = fromTruePredLabelPair[0] == fromTruePredLabelPair[1];
boolean isToPredLabelTrue = toTruePredLabelPair[0] == toTruePredLabelPair[1];
// get index
int fromIndex = isFromPredLabelTrue ? 0 : 1;
int toIndex = isToPredLabelTrue ? 0 : 1;
comparisionMatrix[fromIndex][toIndex]++;
}
Configuration.out.printf("%s: %s vs %s -> [[%4d, %4d], [%4d, %4d]]\n",
modelPlusStrategy, fromPhase, toPhase,
comparisionMatrix[0][0], comparisionMatrix[0][1],
comparisionMatrix[1][0], comparisionMatrix[1][1]);
}
}
}
}
}

View File

@ -0,0 +1,78 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import weka.classifiers.Classifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public abstract class Model {
protected String name;
public Model(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract Classifier fit(TimeDatabase trainDb) throws Exception;
public abstract Map<String, List<int[]>> predict(Classifier classifier, TimeDatabase trainDb, TimeDatabase testDb, ModelPerformanceSampler sampler) throws Exception;
public static abstract class ModelPerformanceSampler {
protected Model model;
protected boolean isPrintInfo;
public ModelPerformanceSampler(Model model, boolean isPrintInfo) {
this.model = model;
this.isPrintInfo = isPrintInfo;
}
public Model getModel() {
return model;
}
public boolean isPrintInfo() {
return isPrintInfo;
}
public abstract List<int[]> collectTrueAndPredLabels(String tag, TimeDatabase testDb, Object... args);
}
public static class DefaultModelPerformanceSampler extends ModelPerformanceSampler {
public static final String FINAL_TAG = "final";
public DefaultModelPerformanceSampler(Model model, boolean isPrintInfo) {
super(model, isPrintInfo);
}
@Override
public List<int[]> collectTrueAndPredLabels(String tag, TimeDatabase testDb, Object... args) {
Assertion.assertEqual("final", tag);
int[] classes = testDb.getClasses();
// double[][] confusionMatrix = Evaluation.initConfusionMatrix(classes.length);
List<int[]> truePredLabelPairs = new ArrayList<int[]>();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int trueLabel = tp.getLabel();
Assertion.assertNotNegative(trueLabel);
double[] finalProbDist = tp.getFinalProbDist();
Assertion.assertNotNull(finalProbDist);
int predLabel = classes[KStepTransitionMatrix.argMax(finalProbDist)];
// confusionMatrix[trueLabel][predLabel]++;
truePredLabelPairs.add(new int[]{trueLabel, predLabel});
}
}
}
if (this.isPrintInfo) {
Evaluation.evaluate(String.format("%s:%s", this.model.getName(), tag), classes, truePredLabelPairs, true);
}
// return confusionMatrix;
return truePredLabelPairs;
}
}
}

View File

@ -0,0 +1,93 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import java.util.*;
public abstract class MultiLearningStrategyTrainAndTestSession extends TrainAndTestSession {
protected Model model;
protected Model.ModelPerformanceSampler sampler;
protected List<LearningStrategy> learningStrategies;
public MultiLearningStrategyTrainAndTestSession(String name, int fold, double validRate, boolean isPrintInfo) {
super(name, fold, validRate, isPrintInfo);
}
public void setModel(Model model, Model.ModelPerformanceSampler sampler) {
this.model = model;
this.sampler = sampler;
}
public void addLearningStrategies(LearningStrategy learningStrategy) {
if (learningStrategy == null) {
return;
}
if (this.learningStrategies == null) {
this.learningStrategies = new ArrayList<LearningStrategy>();
}
this.learningStrategies.add(learningStrategy);
}
@Override
protected Map<String, List<int[]>> validation(int foldIndex, TimeDatabase trainDb, TimeDatabase validDb, TimeDatabase testDb) throws Exception {
Assertion.assertNotNull(this.model);
Assertion.assertNotNull(this.sampler);
Assertion.assertNotNull(this.learningStrategies);
Map<String, List<int[]>> results = new HashMap<String, List<int[]>>();
for (int i = 0; i < this.learningStrategies.size(); i++) {
LearningStrategy learningStrategy = this.learningStrategies.get(i);
Map<String, List<int[]>> confusionMatrices = learningStrategy.fitAndPredict(this.model, trainDb.copy(), (validDb == null) ? null : validDb.copy(), testDb.copy(), this.sampler);
for (Map.Entry<String, List<int[]>> entry : confusionMatrices.entrySet()) {
String key = entry.getKey();
List<int[]> value = entry.getValue();
results.put(String.format("%s:%s:%s", this.model.getName(), learningStrategy.getName(), key), value);
}
if (this.isPrintInfo) {
List<String> keys = new ArrayList<String>(confusionMatrices.keySet());
Collections.sort(keys);
for (String key : keys) {
List<int[]> confusionMatrix = confusionMatrices.get(key);
Evaluation.evaluate(String.format("%s:%s:%s:fold %d", this.model.getName(), learningStrategy.getName(), key, foldIndex), trainDb.getClasses(), confusionMatrix, true);
}
}
}
return results;
}
public static MultiLearningStrategyTrainAndTestSession getDefaultInstance(String name, int fold, double validRate, boolean isPrintInfo) {
return new MultiLearningStrategyTrainAndTestSession(name, fold, validRate, isPrintInfo) {
@Override
protected TimeDatabase[] prepareTrainValidAndTestSets(TimeDatabase trainDb, TimeDatabase testDb) {
TimeDatabase[] tmp = DataSource.splitTrainingAndValidationSets(trainDb, this.validRate);
return new TimeDatabase[]{tmp[0], tmp[1], testDb};
}
};
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(this.name).append("{model:");
if (this.model == null) {
builder.append("null");
} else {
builder.append(this.model.getName());
}
builder.append(",strategies:");
if (this.learningStrategies == null || this.learningStrategies.isEmpty()) {
builder.append("null");
} else {
builder.append("[").append(this.learningStrategies.get(0).getName());
for (int i = 1; i < this.learningStrategies.size(); i++) {
builder.append(",").append(this.learningStrategies.get(i).getName());
}
builder.append("]");
}
builder.append("}");
return builder.toString();
}
}

View File

@ -0,0 +1,97 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import java.util.*;
public abstract class MultiModelTrainAndTestSession extends TrainAndTestSession {
protected List<Model> models;
protected List<Model.ModelPerformanceSampler> samplers;
protected LearningStrategy learningStrategy;
public MultiModelTrainAndTestSession(String name, int fold, double validRate, boolean isPrintInfo) {
super(name, fold, validRate, isPrintInfo);
}
public void addModel(Model model, Model.ModelPerformanceSampler sampler) {
if (model == null || sampler == null) {
return;
}
if (this.models == null) {
this.models = new ArrayList<Model>();
Assertion.assertNull(this.samplers);
this.samplers = new ArrayList<Model.ModelPerformanceSampler>();
}
this.models.add(model);
this.samplers.add(sampler);
}
public void setLearningStrategy(LearningStrategy learningStrategy) {
this.learningStrategy = learningStrategy;
}
@Override
protected Map<String, List<int[]>> validation(int foldIndex, TimeDatabase trainDb, TimeDatabase validDb, TimeDatabase testDb) throws Exception {
Assertion.assertNotNull(this.models);
Assertion.assertNotNull(this.samplers);
Assertion.assertNotNull(this.learningStrategy);
Map<String, List<int[]>> results = new HashMap<String, List<int[]>>();
for (int i = 0; i < this.models.size(); i++) {
Model model = this.models.get(i);
Model.ModelPerformanceSampler sampler = this.samplers.get(i);
Map<String, List<int[]>> confusionMatrices = this.learningStrategy.fitAndPredict(
model, trainDb.copy(), (validDb == null) ? null : validDb.copy(), testDb.copy(), sampler);
for (Map.Entry<String, List<int[]>> entry : confusionMatrices.entrySet()) {
String key = entry.getKey();
List<int[]> value = entry.getValue();
results.put(String.format("%s:%s:%s", model.getName(), this.learningStrategy.getName(), key), value);
}
if (this.isPrintInfo) {
List<String> keys = new ArrayList<String>(confusionMatrices.keySet());
Collections.sort(keys);
for (String key : keys) {
List<int[]> confusionMatrix = confusionMatrices.get(key);
Evaluation.evaluate(String.format("%s:%s:%s:fold %d", model.getName(), this.learningStrategy.getName(), key, foldIndex), trainDb.getClasses(), confusionMatrix, true);
}
}
}
return results;
}
public static MultiModelTrainAndTestSession getDefaultInstance(String name, int fold, double validRate, boolean isPrintInfo) {
return new MultiModelTrainAndTestSession(name, fold, validRate, isPrintInfo) {
@Override
protected TimeDatabase[] prepareTrainValidAndTestSets(TimeDatabase trainDb, TimeDatabase testDb) {
TimeDatabase[] tmp = DataSource.splitTrainingAndValidationSets(trainDb, this.validRate);
return new TimeDatabase[]{tmp[0], tmp[1], testDb};
}
};
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(this.name).append("{models:");
if (this.models == null || this.models.isEmpty()) {
builder.append("null");
} else {
builder.append("[").append(this.models.get(0).getName());
for (int i = 1; i < this.models.size(); i++) {
builder.append(",").append(this.models.get(i).getName());
}
builder.append("]");
}
builder.append(",strategy:");
if (this.learningStrategy == null) {
builder.append("null");
} else {
builder.append(this.learningStrategy.getName());
}
builder.append("}");
return builder.toString();
}
}

View File

@ -0,0 +1,47 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import weka.classifiers.Classifier;
import weka.classifiers.trees.RandomForest;
import weka.core.Instance;
import weka.core.Instances;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RFModel extends Model {
public RFModel(String name) {
super(name);
}
@Override
public Classifier fit(TimeDatabase trainDb) throws Exception {
Instances instances = trainDb.getLabeledInstances();
if (instances == null) {
throw new NullPointerException("No labeled data in the training set!");
}
RandomForest metaClassifier = new RandomForest();
metaClassifier.buildClassifier(instances);
return metaClassifier;
}
@Override
public Map<String, List<int[]>> predict(Classifier classifier, TimeDatabase trainDb, TimeDatabase testDb, ModelPerformanceSampler sampler) throws Exception {
Map<String, List<int[]>> confusionMatrices = new HashMap<String, List<int[]>>();
testDb.getLabeledAndUnlabeledInstances();
for (TimeSeries ts : testDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (!tp.isEmpty()) {
Instance instance = tp.getInstance();
Assertion.assertNotNull(instance);
double[] probDist = classifier.distributionForInstance(instance);
tp.setFinalProbDist(probDist);
}
}
}
confusionMatrices.put("final", sampler.collectTrueAndPredLabels("final", testDb));
return confusionMatrices;
}
}

View File

@ -0,0 +1,292 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Assertion;
import weka.classifiers.Classifier;
import java.math.BigDecimal;
import java.util.*;
public abstract class SemiSupervisedLearningStrategy extends LearningStrategy {
protected LearningEpochListener learningEpochListener;
public SemiSupervisedLearningStrategy(String name) {
super(name);
}
public interface LearningEpochListener {
void learnerInitialized(Classifier classifier, Model model, Model.ModelPerformanceSampler sampler, LearningStrategy strategy, TimeDatabase trainDb, TimeDatabase validDb) throws Exception;
void learnerRetrained(int epoch, Classifier classifier, Model model, Model.ModelPerformanceSampler sampler, LearningStrategy strategy, TimeDatabase oldTrainDb, TimeDatabase newTrainDb, TimeDatabase validDb) throws Exception;
}
public LearningEpochListener getLearningEpochListener() {
return learningEpochListener;
}
public void setLearningEpochListener(LearningEpochListener learningEpochListener) {
this.learningEpochListener = learningEpochListener;
}
@Override
public Map<String, List<int[]>> fitAndPredict(Model model, TimeDatabase trainDb, TimeDatabase validDb, TimeDatabase testDb, Model.ModelPerformanceSampler sampler) throws Exception {
// initial meta classifier
Classifier classifier = model.fit(trainDb);
if (this.learningEpochListener != null) {
this.learningEpochListener.learnerInitialized(classifier, model, sampler, this, trainDb, validDb);
}
// iteratively learning
int i = 0;
while (true) {
// check whether exit is required
if (isStopBeforeUpdate(trainDb, i)) {
break;
}
// predict the probability distributions of both labeled and unlabeled data in training set
model.predict(classifier, trainDb, trainDb, sampler);
// add pseudo labeled data into training set
TimeDatabase oldTrainDb = null;
if (this.learningEpochListener != null) {
oldTrainDb = trainDb.copy();
}
if (assignPseudoLabels(trainDb, i) <= 0) {
break;
}
// clean predicted probability distribution
trainDb.cleanCache();
// retrain meta classifier
classifier = model.fit(trainDb);
if (this.learningEpochListener != null) {
this.learningEpochListener.learnerRetrained(i, classifier, model, sampler, this, oldTrainDb, trainDb, validDb);
}
// check whether exit is required
if (isStopAfterUpdate(trainDb, i)) {
break;
}
// update epoch
i++;
}
return model.predict(classifier, trainDb, testDb, sampler);
}
/**
*
* @param trainDb
* @param epoch
* @return number of pseudo labels assigned
*/
protected abstract int assignPseudoLabels(TimeDatabase trainDb, int epoch);
protected abstract boolean isStopBeforeUpdate(TimeDatabase trainDb, int epoch);
protected abstract boolean isStopAfterUpdate(TimeDatabase trainDb, int epoch);
public static class TimePointWithProbDist implements Comparable<TimePointWithProbDist> {
public final String team;
public final String user;
public final int index;
public final double[] probDist;
public final int label;
public final double entropy;
public TimePointWithProbDist(String team, String user, int index, double[] probDist, int[] classes) {
this.team = team;
this.user = user;
this.index = index;
this.probDist = probDist;
this.label = classes[KStepTransitionMatrix.argMax(probDist)];
this.entropy = Evaluation.entropy(probDist);
}
/**
* the less entropy, the more certainty.
* @param o the instance to compare
* @return negative number if this.entropy is less than o.entropy,
* positive number if this.entropy is greater than o.entropy,
* zero if this.entropy equals to o.entropy.
*/
public int compareTo(TimePointWithProbDist o) {
return Double.compare(this.entropy, o.entropy);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TimePointWithProbDist)) {
return false;
}
TimePointWithProbDist o = (TimePointWithProbDist) obj;
return this.user.equals(o.user) && this.index == o.index;
}
public TimePoint findTimePoint(TimeDatabase db) {
return db.findTimeSeriesByUser(this.user).getTimePoints().get(this.index);
}
}
/**
* Finds indices of all the labeled data in the database.
* @param trainDb
* @param classes
* @return
*/
public static Set<TimePointWithProbDist> indexOfLabeledData(TimeDatabase trainDb, int[] classes) {
Set<TimePointWithProbDist> labeledIndices = new HashSet<TimePointWithProbDist>();
for (TimeSeries ts : trainDb.getTimeSeries()) {
List<TimePoint> tps = ts.getTimePoints();
String team = ts.getTeam();
String user = ts.getUser();
for (int i = 0; i < tps.size(); i++) {
TimePoint tp = tps.get(i);
if (tp.isLabeled()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
labeledIndices.add(new TimePointWithProbDist(team, user, i, probDist, classes));
}
}
}
return labeledIndices;
}
/**
* Finds indices of all the unlabeled data in the database.
* @param trainDb
* @param classes
* @return
*/
public static Set<TimePointWithProbDist> indexOfUnlabeledData(TimeDatabase trainDb, int[] classes) {
Set<TimePointWithProbDist> unlabeledIndices = new HashSet<TimePointWithProbDist>();
for (TimeSeries ts : trainDb.getTimeSeries()) {
List<TimePoint> tps = ts.getTimePoints();
String team = ts.getTeam();
String user = ts.getUser();
for (int i = 0; i < tps.size(); i++) {
TimePoint tp = tps.get(i);
if (tp.isUnlabeled()) {
double[] probDist = tp.getFinalProbDist();
Assertion.assertNotNull(probDist);
unlabeledIndices.add(new TimePointWithProbDist(team, user, i, probDist, classes));
}
}
}
return unlabeledIndices;
}
/**
* Gets the number of data under each class.
* @param trainDb
* @param classes
* @return
*/
public static int[] countDataUnderEachClass(TimeDatabase trainDb, int[] classes) {
// computing proportion of each class
int[] classCount = TimeDatabase.zeros(classes.length);
for (TimeSeries ts : trainDb.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int index = TimeDatabase.argWhere(classes, tp.getLabel());
classCount[index]++;
}
}
}
return classCount;
}
/**
* Gets scaled number of data whose label is as the specific class.
* @param trainDb
* @param classes
* @param batch number of unlabeled added in this epoch
* @return
*/
public static int[] scaleDataUnderEachClass(TimeDatabase trainDb, int[] classes, int batch) {
// compute proportion of each class
int[] classCounts = countDataUnderEachClass(trainDb, classes);
int sum = 0;
for (int classCount : classCounts) {
Assertion.assertPositive(classCount);
sum += classCount;
}
int accumulation = 0;
for (int i = 0; i < classCounts.length - 1; i++) {
classCounts[i] = (int) (((double) (classCounts[i] * batch)) / ((double) sum));
Assertion.assertPositive(classCounts[i]);
accumulation += classCounts[i];
}
classCounts[classCounts.length - 1] = batch - accumulation;
Assertion.assertPositive(classCounts[classCounts.length - 1]);
return classCounts;
}
/**
* Classifies data into different group according to their labels.
* @param indices
* @param classes
* @return
*/
public static List<List<TimePointWithProbDist>> divideDataByClass(Set<TimePointWithProbDist> indices, int[] classes) {
// initial containers
List<List<TimePointWithProbDist>> classLists = new ArrayList<List<TimePointWithProbDist>>();
for (int i = 0; i < classes.length; i++) {
classLists.add(new ArrayList<TimePointWithProbDist>());
}
for (TimePointWithProbDist tp : indices) {
int index = TimeDatabase.argWhere(classes, tp.label);
classLists.get(index).add(tp);
}
return classLists;
}
/**
* Selects the k most certain data. The certainty is measured by
* the entropy of the data's estimated probability distribution.
* @param indices
* @param k
* @return
*/
public static List<TimePointWithProbDist> selectKMostCertainData(List<TimePointWithProbDist> indices, int k) {
if (indices.size() <= k) {
return indices;
} else {
// sort by entropy
Collections.sort(indices);
return indices.subList(0, k);
}
}
/**
* Assigns pseudo labels to unlabeled data.
* @param trainDb
* @param indices
*/
public static void assignPseudoLabelsByIndices(TimeDatabase trainDb, List<TimePointWithProbDist> indices) {
for (TimePointWithProbDist candidate : indices) {
TimePoint tp = candidate.findTimePoint(trainDb);
tp.setLabel(candidate.label);
}
}
/**
* Computing distance between the old set of k-step transition matrices and the new one.
* @param oldKtms
* @param newKtms
* @param isPrintInfo
* @return
*/
public static double distance(Map<String, KStepTransitionMatrix> oldKtms, Map<String, KStepTransitionMatrix> newKtms, boolean isPrintInfo, String name) {
double distance = 0.0;
for (String key : newKtms.keySet()) {
KStepTransitionMatrix oldKtm = oldKtms.get(key);
KStepTransitionMatrix newKtm = newKtms.get(key);
Assertion.assertNotNull(oldKtm);
Assertion.assertNotNull(newKtm);
distance += KStepTransitionMatrix.distance(oldKtm, newKtm);
}
if (isPrintInfo) {
if (distance > 0.0) {
Configuration.out.printf("%s: transition matrices are changed (%s).\n", name, new BigDecimal(distance).toString());
} else {
Configuration.out.println(name + ": transition matrices are unchanged.");
}
}
return distance;
}
}

View File

@ -0,0 +1,18 @@
package edu.nju.ics.frontier.learning;
import weka.classifiers.Classifier;
import java.util.List;
import java.util.Map;
public class SupervisedLearningStrategy extends LearningStrategy {
public SupervisedLearningStrategy(String name) {
super(name);
}
@Override
public Map<String, List<int[]>> fitAndPredict(Model model, TimeDatabase trainDb, TimeDatabase validDb, TimeDatabase testDb, Model.ModelPerformanceSampler sampler) throws Exception {
Classifier classifier = model.fit(trainDb);
return model.predict(classifier, trainDb, testDb, sampler);
}
}

View File

@ -0,0 +1,97 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.common.io.OkTextTransfer;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.util.Assertion;
import java.io.File;
import java.io.FileFilter;
public class Temp {
public static void main(String[] args) {
transformJsonToCsv();
}
private static void copyTestSetToValidSet() {
OkTextTransfer transfer = new OkTextTransfer();
File rootDir = new File("C:\\Users\\zzw\\Desktop\\fujitsu\\data");
File[] eraseAndRoundDirs = rootDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory() &&
pathname.getName().matches("data_erase_0[.][0-9]{2}_round_[0-9]{2}");
}
});
Assertion.assertTrue(eraseAndRoundDirs != null && eraseAndRoundDirs.length > 0);
for (File eraseAndRoundDir : eraseAndRoundDirs) {
File[] foldDirs = eraseAndRoundDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory() && pathname.getName().matches("fold_[0-9]");
}
});
Assertion.assertTrue(foldDirs != null && foldDirs.length > 0);
for (File foldDir : foldDirs) {
File testFile = new File(foldDir.getAbsolutePath() + File.separator + "test.json");
Assertion.assertTrue(testFile.exists());
File validFile = new File(foldDir.getAbsolutePath() + File.separator + "valid.json");
transfer.transfer(testFile, validFile);
System.out.println(validFile.getAbsolutePath());
}
}
}
private static void transformJsonToCsv() {
File rootDir = new File("C:\\Users\\zzw\\Desktop\\fujitsu\\data");
File[] eraseAndRoundDirs = rootDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory() &&
pathname.getName().matches("data_erase_0[.][0-9]{2}_round_[0-9]{2}");
}
});
Assertion.assertTrue(eraseAndRoundDirs != null && eraseAndRoundDirs.length > 0);
for (File eraseAndRoundDir : eraseAndRoundDirs) {
File[] foldDirs = eraseAndRoundDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isDirectory() && pathname.getName().matches("fold_[0-9]");
}
});
Assertion.assertTrue(foldDirs != null && foldDirs.length > 0);
for (File foldDir : foldDirs) {
File trainFile = new File(foldDir.getAbsolutePath() + File.separator + "train.json");
Assertion.assertTrue(trainFile.exists());
transformJsonToCsv(trainFile);
File testFile = new File(foldDir.getAbsolutePath() + File.separator + "test.json");
Assertion.assertTrue(testFile.exists());
transformJsonToCsv(testFile);
// File validFile = new File(foldDir.getAbsolutePath() + File.separator + "valid.json");
// Assertion.assertTrue(validFile.exists());
// transformJsonToCsv(validFile);
}
}
}
private static void transformJsonToCsv(File jsonFile) {
String jsonPath = jsonFile.getAbsolutePath();
String csvPath = jsonPath.substring(0, jsonPath.lastIndexOf(".")) + ".csv";
TimeDatabase db = TimeDatabase.deserialize(jsonPath);
OkTextWriter writer = new OkTextWriter();
writer.open(csvPath);
// csv header
for (String featureName : db.getFeatureNames()) {
writer.print(featureName + ",");
}
writer.println(db.getLabelName());
// csv body
for (TimeSeries ts : db.getTimeSeries()) {
for (TimePoint tp : ts.getTimePoints()) {
if (!tp.isEmpty()) {
for (double feature : tp.getFeatures()) {
writer.print(feature + ",");
}
writer.println(tp.getLabel());
}
}
}
writer.close();
System.out.println(csvPath);
}
}

View File

@ -0,0 +1,786 @@
package edu.nju.ics.frontier.learning;
import com.google.gson.Gson;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.util.Assertion;
import weka.core.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class TimeDatabase {
private String[] users;
private int[] classes;
private String[] featureNames;
private String labelName;
private List<TimeSeries> timeSeries;
public TimeDatabase() {
this.timeSeries = new ArrayList<TimeSeries>();
}
public void load(String csvPath) {
OkTextReader reader = new OkTextReader();
reader.open(csvPath);
// head
String[] head = reader.readLine().split(",");
this.featureNames = new String[head.length - 10];
for (int i = 0, l = this.featureNames.length; i < l; i++) {
this.featureNames[i] = head[i + 8];
}
this.labelName = head[head.length - 2];
// body
Set<Integer> classSet = new HashSet<Integer>();
String line;
while ((line = reader.readLine()) != null) {
// parse data
String[] info = line.split(",");
String team = info[0];
String user = info[1];
// 徐敏敏组, 江超组, 缪忍忍组
if (team.equals("徐敏敏组")) {
continue;
}
long timestamp = Long.parseLong(info[3]);
int sessionId = Integer.parseInt(info[7]);
double[] features = new double[info.length - 10];
for (int i = 0, l = features.length; i < l; i++) {
features[i] = Double.parseDouble(info[i + 8]);
}
// zero-based classes
int label = Integer.parseInt(info[info.length - 2]);
if (label != -1) {
label = label - 1;
}
// add data into database
TimeSeries ts = findTimeSeriesByUser(user);
if (ts == null) {
ts = new TimeSeries(team, user);
timeSeries.add(ts);
}
ts.addTimePoint(new TimePoint(timestamp, sessionId, features, label));
// update classes
if (label != -1) {
classSet.add(label);
}
}
reader.close();
// assign value to users
this.users = new String[this.timeSeries.size()];
for (int i = 0, l = this.users.length; i < l; i++) {
this.users[i] = this.timeSeries.get(i).getUser();
}
// assign value to classes
List<Integer> classList = new ArrayList<Integer>(classSet);
Collections.sort(classList, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
this.classes = new int[classList.size()];
for (int i = 0, l = this.classes.length; i < l; i++) {
this.classes[i] = classList.get(i);
}
}
public Instances defineDataFormat() {
ArrayList<Attribute> attrs = new ArrayList<Attribute>();
// add features
for (String attrName : this.featureNames) {
attrs.add(new Attribute(attrName));
}
// add label
ArrayList<String> classes = new ArrayList<String>();
for (int classValue : this.classes) {
classes.add(String.valueOf(classValue));
}
attrs.add(new Attribute(this.labelName, classes));
return new Instances("work_engagement", attrs, 0);
}
public Instances getLabeledInstances() {
Instances instances = defineDataFormat();
for (TimeSeries ts : this.timeSeries) {
List<TimePoint> tps = ts.getTimePoints();
for (TimePoint tp : tps) {
if (!tp.isLabeled()) {
continue;
}
Instance instance = tp.transferToInstance();
instances.add(instance);
instance.setDataset(instances);
}
}
instances.setClassIndex(instances.numAttributes() - 1);
return instances.isEmpty() ? null : instances;
}
public Instances getLabeledAndUnlabeledInstances() {
Instances instances = defineDataFormat();
for (TimeSeries ts : this.timeSeries) {
List<TimePoint> tps = ts.getTimePoints();
for (TimePoint tp : tps) {
if (tp.isEmpty()) {
continue;
}
Instance instance = tp.transferToInstance();
instances.add(instance);
instance.setDataset(instances);
}
}
instances.setClassIndex(instances.numAttributes() - 1);
return instances.isEmpty() ? null : instances;
}
public void positiveNeutralNegative() {
this.classes = new int[]{0, 1, 2};
if (this.timeSeries.isEmpty()) {
return;
}
for (TimeSeries ts : this.timeSeries) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int label = tp.getLabel();
if (label <= 1) {
tp.setLabel(0);
} else if (label == 2) {
tp.setLabel(1);
} else {
tp.setLabel(2);
}
}
}
}
}
public void positiveNegative() {
this.classes = new int[]{0, 1};
if (this.timeSeries.isEmpty()) {
return;
}
for (TimeSeries ts : this.timeSeries) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
int label = tp.getLabel();
if (label <= 1) {
tp.setLabel(0);
} else if (label == 2) {
tp.setLabel(-1);
} else {
tp.setLabel(1);
}
}
}
}
}
public TimeDatabase copy() {
TimeDatabase newDb = new TimeDatabase();
newDb.users = this.users;
newDb.classes = this.classes;
newDb.featureNames = this.featureNames;
newDb.labelName = this.labelName;
for (TimeSeries ts : this.timeSeries) {
TimeSeries newTs = new TimeSeries(ts.getTeam(), ts.getUser());
for (TimePoint tp : ts.getTimePoints()) {
newTs.addTimePoint(new TimePoint(tp));
}
newDb.timeSeries.add(newTs);
}
return newDb;
}
public TimeDatabase align() {
TimeDatabase newDb = new TimeDatabase();
newDb.users = this.users;
newDb.classes = this.classes;
newDb.featureNames = this.featureNames;
newDb.labelName = this.labelName;
// retrieve all users' timestamps, and sort in ascending order
Set<Long> timestampSet = new HashSet<Long>();
for (TimeSeries ts : this.timeSeries) {
List<TimePoint> tps = ts.getTimePoints();
for (TimePoint tp : tps) {
timestampSet.add(tp.getTimestamp());
}
}
List<Long> timestampList = new ArrayList<Long>(timestampSet);
Collections.sort(timestampList, new Comparator<Long>() {
public int compare(Long o1, Long o2) {
if (o1 < o2) {
return -1;
} else if (o1 > o2) {
return 1;
} else {
return 0;
}
}
});
// align all users' time points by adding empty time points as placeholders
for (TimeSeries ts : this.timeSeries) {
TimeSeries newTs = new TimeSeries(ts.getTeam(), ts.getUser());
for (long timestamp : timestampList) {
TimePoint newTp = ts.findClosestTimePoint(timestamp, 0, true, true, false);
if (newTp == null) {
newTp = new TimePoint(timestamp, -1, null, -1);
}
newTs.addTimePoint(newTp);
}
newDb.timeSeries.add(newTs);
}
return newDb;
}
public boolean fillingEmpty(boolean[] classFlags, long threshold) {
if (this.timeSeries.isEmpty()) {
return false;
}
boolean isDbChanged = false;
for (TimeSeries ts : this.timeSeries) {
for (TimePoint tp : ts.getTimePoints()) {
if (!tp.isEmpty()) {
continue;
}
TimePoint closestTp = ts.findClosestTimePoint(tp.getTimestamp(), threshold, true, true, false);
if (closestTp == null) {
continue;
}
if (closestTp.isLabeled()) {
int label = closestTp.getLabel();
if (classFlags[argWhere(this.classes, label)]) {
tp.setFeatures(closestTp.getFeatures());
tp.setLabel(label);
isDbChanged = true;
}
} else {
tp.setFeatures(closestTp.getFeatures());
isDbChanged = true;
}
}
}
return isDbChanged;
}
public boolean spreadLabels(boolean[] classFlags, long threshold) {
if (this.timeSeries.isEmpty()) {
return false;
}
boolean isDbChanged = false;
for (TimeSeries ts : this.timeSeries) {
for (TimePoint tp : ts.getTimePoints()) {
if (!tp.isUnlabeled()) {
continue;
}
int label = ts.findClosestLabel(tp.getTimestamp(), threshold);
if (label != -1 && classFlags[argWhere(this.classes, label)]) {
tp.setLabel(label);
isDbChanged = true;
}
}
}
return isDbChanged;
}
public TimeDatabase slice(int[] indices) {
TimeDatabase newDb = new TimeDatabase();
newDb.users = this.users;
newDb.classes = this.classes;
newDb.featureNames = this.featureNames;
newDb.labelName = this.labelName;
for (TimeSeries ts : this.timeSeries) {
TimeSeries newTs = new TimeSeries(ts.getTeam(), ts.getUser());
List<TimePoint> tps = ts.getTimePoints();
for (int index : indices) {
newTs.addTimePoint(new TimePoint(tps.get(index)));
}
newDb.timeSeries.add(newTs);
}
return newDb;
}
public String simpleDescribe(String tag) {
StringBuilder builder = new StringBuilder();
int[] classCount = zeros(this.classes.length);
int labeledCount = 0;
int unlabeledCount = 0;
int emptyCount = 0;
for (TimeSeries ts : this.timeSeries) {
for (TimePoint tp : ts.getTimePoints()) {
if (tp.isLabeled()) {
labeledCount++;
int index = argWhere(this.classes, tp.getLabel());
classCount[index]++;
} else if (tp.isUnlabeled()) {
unlabeledCount++;
} else {
emptyCount++;
}
}
}
builder.append(tag).
append("{all:").append(labeledCount + unlabeledCount + emptyCount).
append(",lbl:").append(labeledCount).
append(",unl:").append(unlabeledCount).
append(",emp:").append(emptyCount).
append(",cls[").append(classCount[0]);
for (int i = 1; i < classCount.length; i++) {
builder.append(",").append(classCount[i]);
}
builder.append("]}");
return builder.toString();
}
public String describe(String tag) {
StringBuilder builder = new StringBuilder();
builder.append("id | #all #lbl #unl #emp |");
for (int cls : this.classes) {
builder.append(" #(").append(cls).append(")");
}
builder.append(" | #workday | team#user <- ").append(tag).append("\n");
int allLblNum = 0;
int allUnlNum = 0;
int[] allClsNums = zeros(this.classes.length);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
for (int tsIndex = 0, tsLength = this.timeSeries.size(); tsIndex < tsLength; tsIndex++) {
int lblNum = 0;
int unlNum = 0;
int empNum = 0;
int[] clsNums = zeros(this.classes.length);
Set<String> workdays = new HashSet<String>();
TimeSeries ts = this.timeSeries.get(tsIndex);
String team = ts.getTeam();
String user = ts.getUser();
List<TimePoint> tps = ts.getTimePoints();
for (TimePoint tp : tps) {
switch (tp.getType()) {
case TimePoint.LABELED:
lblNum++;
clsNums[argWhere(this.classes, tp.getLabel())]++;
workdays.add(sdf.format(new Date(tp.getTimestamp())));
break;
case TimePoint.UNLABELED:
unlNum++;
workdays.add(sdf.format(new Date(tp.getTimestamp())));
break;
case TimePoint.EMPTY:
empNum++;
break;
default:
break;
}
}
int allNum = tps.size();
allLblNum += lblNum;
allUnlNum += unlNum;
addVector(allClsNums, clsNums);
int workdayNum = workdays.size();
builder.append(String.format("%2d | %4d %4d %4d %4d |",
(tsIndex + 1), allNum, lblNum, unlNum, empNum));
for (int clsNum : clsNums) {
if (clsNum > 0) {
builder.append(String.format(" %4d", clsNum));
} else {
builder.append(" ");
}
}
builder.append(String.format(" | %8d | %s#%s\n", workdayNum, team, user));
}
builder.append(String.format(" | %4d %4d |", allLblNum, allUnlNum));
for (int allClsNum : allClsNums) {
if (allClsNum > 0) {
builder.append(String.format(" %4d", allClsNum));
} else {
builder.append(" ");
}
}
builder.append(" |");
return builder.toString();
}
public String describe() {
return describe("");
}
public Map<String, KStepTransitionMatrix> getTemporalCorrelationMatrix(int n, long offset) {
Map<String, KStepTransitionMatrix> matricesMap = new HashMap<String, KStepTransitionMatrix>();
for (String user : this.users) {
KStepTransitionMatrix transitionMatrix = getKStepTransitionMatrix(user, user, n, offset);
Assertion.assertNotNull(transitionMatrix);
matricesMap.put(String.format("%s->%s", user, user), transitionMatrix);
}
return matricesMap;
}
public Map<String, KStepTransitionMatrix> getSpatialCorrelationMatrix(int n, long offset) {
Map<String, KStepTransitionMatrix> matricesMap = new HashMap<String, KStepTransitionMatrix>();
for (String fromUser : this.users) {
for (String toUser : this.users) {
if (toUser.equals(fromUser)) {
continue;
}
KStepTransitionMatrix transitionMatrix = getKStepTransitionMatrix(fromUser, toUser, n, offset);
Assertion.assertNotNull(transitionMatrix);
matricesMap.put(String.format("%s->%s", fromUser, toUser), transitionMatrix);
}
}
return matricesMap;
}
public Map<String, KStepTransitionMatrix> getKStepTransitionMatrix(int n, long offset) {
Map<String, KStepTransitionMatrix> matricesMap = new HashMap<String, KStepTransitionMatrix>();
for (String fromUser : this.users) {
for (String toUser : this.users) {
KStepTransitionMatrix transitionMatrix = getKStepTransitionMatrix(fromUser, toUser, n, offset);
Assertion.assertNotNull(transitionMatrix);
matricesMap.put(String.format("%s->%s", fromUser, toUser), transitionMatrix);
}
}
return matricesMap;
}
/**
* get the 1- to n-order state transition matrices from {@code fromUser} to {@code toUser}.
* @param fromUser requester
* @param toUser recipient
* @param n n-order Markov Chain Model.
* @param offset upper limitation of the time difference between 1-step pair of time point.
* @return 1- to n-order state transition matrices from {@code fromUser} to {@code toUser},
* or {@code null} if {@code n} less than 1.
*/
public KStepTransitionMatrix getKStepTransitionMatrix(String fromUser, String toUser, int n, long offset) {
if (fromUser == null || toUser == null || n < 1) {
return null;
}
TimeSeries fromTs = findTimeSeriesByUser(fromUser);
TimeSeries toTs = findTimeSeriesByUser(toUser);
if (fromTs == null || toTs == null) {
return null;
}
List<TimePoint> fromTps = fromTs.getTimePoints();
List<TimePoint> toTps = toTs.getTimePoints();
if (fromTps.size() != toTps.size()) {
return null;
}
// initialize F
int[][][] F = new int[n][this.classes.length][this.classes.length];
for (int i = 0; i < F.length; i++) {
for (int j = 0; j < F[i].length; j++) {
for (int k = 0; k < F[i][j].length; k++) {
F[i][j][k] = 0;
}
}
}
// statistic F
for (int step = 1; step <= n; step++) {
long threshold = offset * step;
for (int i = 0, j = step, l = toTps.size(); j < l; i++, j++) {
TimePoint fromTp = fromTps.get(i);
TimePoint toTp = toTps.get(j);
if (!(fromTp.isLabeled() && toTp.isLabeled())) {
continue;
}
long deltaTime = toTp.getTimestamp() - fromTp.getTimestamp();
Assertion.assertPositive(deltaTime);
if (deltaTime > threshold) {
continue;
}
int fromIndex = argWhere(this.classes, fromTp.getLabel());
int toIndex = argWhere(this.classes, toTp.getLabel());
F[step - 1][fromIndex][toIndex]++;
}
}
// transform F to Q_hat
double[][][] Q_hat = new double[n][this.classes.length][this.classes.length];
for (int i = 0; i < F.length; i++) {
for (int j = 0; j < F[i].length; j++) {
int sum = sumVector(F[i][j]);
if (sum == 0) {
for (int k = 0; k < F[i][j].length; k++) {
Q_hat[i][j][k] = 0;
}
} else {
for (int k = 0; k < F[i][j].length; k++) {
Q_hat[i][j][k] = ((double) F[i][j][k]) / ((double) sum);
}
}
}
}
return new KStepTransitionMatrix(fromUser, toUser, Q_hat);
}
public String[] getUsers() {
return users;
}
public int[] getClasses() {
return classes;
}
public String[] getFeatureNames() {
return featureNames;
}
public String getLabelName() {
return labelName;
}
public List<TimeSeries> getTimeSeries() {
return timeSeries;
}
public int getNumberOfTimeSeries() {
return this.timeSeries.size();
}
public int[] getNumberOfTimePointsOfEachUser() {
if (this.timeSeries.isEmpty()) {
return null;
}
int[] result = new int[this.timeSeries.size()];
for (int i = 0; i < result.length; i++) {
result[i] = this.timeSeries.get(i).getTimePoints().size();
}
return result;
}
public int getNumberOfTimePoints() {
int[] tmp = getNumberOfTimePointsOfEachUser();
if (tmp == null) {
return -1;
}
for (int i = 1; i < tmp.length; i++) {
if (tmp[i] != tmp[0]) {
return -1;
}
}
return tmp[0];
}
public TimeSeries findTimeSeriesByUser(String user) {
if (timeSeries.isEmpty() || user == null) {
return null;
}
for (TimeSeries ts : timeSeries) {
if (ts.getUser().equals(user)) {
return ts;
}
}
return null;
}
public static int[] zeros(int length) {
int[] result = new int[length];
for (int i = 0; i < length; i++) {
result[i] = 0;
}
return result;
}
public static int argWhere(int[] array, int value) {
for (int i = 0, l = array.length; i < l; i++) {
if (array[i] == value) {
return i;
}
}
return -1;
}
private static void addVector(int[] dest, int[] src) {
Assertion.assertTrue(dest.length == src.length);
for (int i = 0, l = dest.length; i < l; i++) {
dest[i] += src[i];
}
}
public static int sumVector(int[] vector) {
int sum = 0;
for (int i = 0, l = vector.length; i < l; i++) {
sum += vector[i];
}
return sum;
}
public static boolean isAllZero(int[] vector) {
for (int value : vector) {
if (value != 0) {
return false;
}
}
return true;
}
public void cleanCache() {
for (TimeSeries ts : this.timeSeries) {
for (TimePoint tp : ts.getTimePoints()) {
tp.cleanCache();
}
}
}
public void erasePartialLabels(double rate) {
Assertion.assertTrue(rate > 0.0 && rate < 1.0);
for (TimeSeries ts : this.timeSeries) {
List<TimePoint> tps = ts.getTimePoints();
for (int cls : this.classes) {
List<Integer> indices = ts.getIndicesWithClass(cls);
if (indices == null) {
continue;
}
int eraseNum = (int) (indices.size() * rate);
if (eraseNum <= 0) {
continue;
}
int[] eraseIndices = randInt(indices, eraseNum);
for (int eraseIndex : eraseIndices) {
TimePoint tp = tps.get(eraseIndex);
Assertion.assertTrue(tp.isLabeled() && tp.getLabel() == cls);
tp.setLabel(-1);
}
}
}
}
/**
* Gets n random integers fall in the interval of [min, max).
* @param min inclusive
* @param max exclusive
* @param length number of random to generate
* @return
*/
public static int[] randInt(int min, int max, int length) {
Assertion.assertTrue(min < max && max - min >= length);
List<Integer> tmp = new ArrayList<Integer>();
for (int i = min; i < max; i++) {
tmp.add(i);
}
int[] result = new int[length];
for (int i = 0; i < length; i++) {
int index = (int) (Math.random() * tmp.size());
result[i] = tmp.remove(index);
}
return result;
}
/**
* Gets n random integers from a specific integer list.
* @param list
* @param length number of random to generate
* @return
*/
public static int[] randInt(List<Integer> list, int length) {
Assertion.assertNotNull(list);
Assertion.assertTrue(list.size() >= length);
int[] indices = randInt(0, list.size(), length);
int[] result = new int[length];
for (int i = 0; i < length; i++) {
result[i] = list.get(indices[i]);
}
return result;
}
public String drawSparseDatabase() {
StringBuilder builder = new StringBuilder();
int tpNum = this.getNumberOfTimePoints();
int notLabeledTpNum = 0;
List<Integer> tmp = new ArrayList<Integer>();
for (int i = 0; i < tpNum; i++) {
boolean isAllNotLabeledInAColumn = true;
for (TimeSeries ts : this.timeSeries) {
if (ts.getTimePoints().get(i).isLabeled()) {
isAllNotLabeledInAColumn = false;
break;
}
}
if (isAllNotLabeledInAColumn) {
notLabeledTpNum++;
} else {
if (notLabeledTpNum > 0) {
tmp.add(-notLabeledTpNum);
}
tmp.add(i);
notLabeledTpNum = 0;
}
}
if (notLabeledTpNum > 0) {
tmp.add(-notLabeledTpNum);
}
// print head
for (int value : tmp) {
if (value < 0) {
builder.append(String.format("[%4d]", -value));
} else {
builder.append("*");
}
}
builder.append("\n");
// print body
for (TimeSeries ts : this.timeSeries) {
List<TimePoint> tps = ts.getTimePoints();
for (int value : tmp) {
if (value < 0) {
builder.append(" ");
} else {
TimePoint tp = tps.get(value);
if (tp.isLabeled()) {
builder.append(tp.getLabel());
} else {
builder.append("|");
}
}
}
builder.append("<-").append(ts.getTeam()).append("#").append(ts.getUser()).append("\n");
}
return builder.toString();
}
public static void serialize(TimeDatabase db, String path) {
Gson gson = new Gson();
String json = gson.toJson(db);
OkTextWriter writer = new OkTextWriter();
writer.open(path);
writer.println(json);
writer.close();
}
public static TimeDatabase deserialize(String path) {
OkTextReader reader = new OkTextReader();
reader.open(path);
String json = reader.readLine();
reader.close();
Gson gson = new Gson();
return gson.fromJson(json, TimeDatabase.class);
}
}

View File

@ -0,0 +1,173 @@
package edu.nju.ics.frontier.learning;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Utils;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class TimePoint {
public static final int LABELED = 0;
public static final int UNLABELED = 1;
public static final int EMPTY = 2;
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private long timestamp;
private int sessionId;
private double[] features;
private int label;
private Instance instance;
/**
* $f_{obser}(o^{v}_{t},s^{v}_{t})$
*/
private double[] obserProbDist;
/**
* $f_{intra}(s^{v}_{t-i},s^{v}_{t})$
*/
private double[] intraProbDist;
/**
* $f_{inter}(s^{u}_{t-i},s^{v}_{t})$
*/
private double[] interProbDist;
private double[] finalProbDist;
public TimePoint(long timestamp, int sessionId, double[] features, int label) {
this.timestamp = timestamp;
this.sessionId = sessionId;
this.features = features;
this.label = label;
}
public TimePoint(TimePoint tp) {
this.timestamp = tp.timestamp;
this.sessionId = tp.sessionId;
this.features = tp.features;
this.label = tp.label;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public int getSessionId() {
return sessionId;
}
public void setSessionId(int sessionId) {
this.sessionId = sessionId;
}
public double[] getFeatures() {
return features;
}
public void setFeatures(double[] features) {
this.features = features;
}
public int getLabel() {
return label;
}
public void setLabel(int label) {
this.label = label;
}
public boolean isLabeled() {
return features != null && label != -1;
}
public boolean isUnlabeled() {
return features != null && label == -1;
}
public boolean isEmpty() {
return features == null;
}
public int getType() {
if (features != null) {
return (label != -1) ? LABELED : UNLABELED;
} else {
return EMPTY;
}
}
public Instance getInstance() {
return instance;
}
public Instance transferToInstance() {
if (this.isEmpty()) {
return null;
}
double[] features = this.getFeatures();
double[] values = new double[features.length + 1];
System.arraycopy(features, 0, values, 0, features.length);
if (this.isLabeled()) {
values[values.length - 1] = this.getLabel();
} else {
values[values.length - 1] = Utils.missingValue();
}
this.instance = new DenseInstance(1.0, values);
return this.instance;
}
public double[] getObserProbDist() {
return obserProbDist;
}
public void setObserProbDist(double[] obserProbDist) {
this.obserProbDist = obserProbDist;
}
public double[] getIntraProbDist() {
return intraProbDist;
}
public void setIntraProbDist(double[] intraProbDist) {
this.intraProbDist = intraProbDist;
}
public double[] getInterProbDist() {
return interProbDist;
}
public void setInterProbDist(double[] interProbDist) {
this.interProbDist = interProbDist;
}
public double[] getFinalProbDist() {
return finalProbDist;
}
public void setFinalProbDist(double[] finalProbDist) {
this.finalProbDist = finalProbDist;
}
public void cleanCache() {
this.instance = null;
this.obserProbDist = null;
this.intraProbDist = null;
this.interProbDist = null;
this.finalProbDist = null;
}
@Override
public String toString() {
return String.format("timestamp:%s,sessionId:%d,features:%s,internalScore:%d",
SDF.format(new Date(timestamp)), sessionId, Arrays.toString(features), label);
}
}

View File

@ -0,0 +1,140 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.util.Assertion;
import java.util.ArrayList;
import java.util.List;
public class TimeSeries {
private String team;
private String user;
private List<TimePoint> timePoints;
public TimeSeries(String team, String user) {
this.team = team;
this.user = user;
this.timePoints = new ArrayList<TimePoint>();
}
public void addTimePoint(TimePoint tp) {
if (tp != null) {
timePoints.add(tp);
}
}
public String getTeam() {
return team;
}
public String getUser() {
return user;
}
public List<TimePoint> getTimePoints() {
return timePoints;
}
public TimePoint findClosestTimePoint(long timestamp, long threshold, boolean labeled, boolean unlabeled, boolean empty) {
if (timePoints.isEmpty()) {
return null;
}
long minDelta = Long.MAX_VALUE;
int minIndex = -1;
for (int index = 0, l = timePoints.size(); index < l; index++) {
TimePoint tp = timePoints.get(index);
if (tp.isLabeled() && (!labeled)) {
continue;
}
if (tp.isUnlabeled() && (!unlabeled)) {
continue;
}
if (tp.isEmpty() && (!empty)) {
continue;
}
long delta = Math.abs(tp.getTimestamp() - timestamp);
if (delta < minDelta) {
minDelta = delta;
minIndex = index;
}
}
return (minDelta <= threshold) ? timePoints.get(minIndex) : null;
}
public int findClosestIndex(long timestamp, long threshold, boolean labeled, boolean unlabeled, boolean empty) {
if (timePoints.isEmpty()) {
return -1;
}
long minDelta = Long.MAX_VALUE;
int minIndex = -1;
for (int index = 0, l = timePoints.size(); index < l; index++) {
TimePoint tp = timePoints.get(index);
if (tp.isLabeled() && (!labeled)) {
continue;
}
if (tp.isUnlabeled() && (!unlabeled)) {
continue;
}
if (tp.isEmpty() && (!empty)) {
continue;
}
long delta = Math.abs(tp.getTimestamp() - timestamp);
if (delta < minDelta) {
minDelta = delta;
minIndex = index;
}
}
return (minDelta <= threshold) ? minIndex : -1;
}
public int findClosestLabel(long timestamp, long threshold) {
if (timePoints.isEmpty()) {
return -1;
}
long minDelta = Long.MAX_VALUE;
int minLabel = -1;
for (int index = 0, l = timePoints.size(); index < l; index++) {
TimePoint tp = timePoints.get(index);
if (tp.isLabeled()) {
long delta = Math.abs(tp.getTimestamp() - timestamp);
if (delta < minDelta) {
minDelta = delta;
minLabel = tp.getLabel();
}
}
}
return (minDelta <= threshold) ? minLabel : -1;
}
public int[] countLabeledDataWithClass(int[] classes) {
int[] result = TimeDatabase.zeros(classes.length);
for (TimePoint tp : this.timePoints) {
if (tp.isLabeled()) {
int label = tp.getLabel();
Assertion.assertNotNegative(label);
result[TimeDatabase.argWhere(classes, label)]++;
}
}
return result;
}
public List<Integer> getIndicesWithClass(int cls) {
List<Integer> result = new ArrayList<Integer>();
for (int i = 0; i < this.timePoints.size(); i++) {
TimePoint tp = this.timePoints.get(i);
if (tp.isLabeled() && tp.getLabel() == cls) {
result.add(i);
}
}
return result.isEmpty() ? null : result;
}
}

View File

@ -0,0 +1,183 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Assertion;
import java.io.File;
import java.util.*;
public abstract class TrainAndTestSession {
protected String name;
protected int fold;
protected double validRate;
protected boolean isPrintInfo;
public TrainAndTestSession(String name, int fold, double validRate, boolean isPrintInfo) {
this.name = name;
this.fold = fold;
this.validRate = validRate;
this.isPrintInfo = isPrintInfo;
}
public String getName() {
return name;
}
public int getFold() {
return fold;
}
public void setFold(int fold) {
this.fold = fold;
}
public double getValidRate() {
return validRate;
}
public void setValidRate(double validRate) {
this.validRate = validRate;
}
public boolean isPrintInfo() {
return isPrintInfo;
}
public void setPrintInfo(boolean printInfo) {
isPrintInfo = printInfo;
}
@Override
public String toString() {
return this.name;
}
public Map<String, Evaluation> crossValidation(String root) throws Exception {
Configuration.out.println(toString());
// attempt to load training set, validation set, and test set from local file system
boolean isLoadDataFromLocalFileSystem;
if (root != null) {
isLoadDataFromLocalFileSystem = true;
for (int foldIndex = 0; foldIndex < this.fold; foldIndex++) {
File trainFile = new File(root + File.separator + "fold_" + foldIndex + File.separator + "train.json");
if (!trainFile.exists()) {
isLoadDataFromLocalFileSystem = false;
break;
}
File testFile = new File(root + File.separator + "fold_" + foldIndex + File.separator + "test.json");
if (!testFile.exists()) {
isLoadDataFromLocalFileSystem = false;
break;
}
}
} else {
isLoadDataFromLocalFileSystem = false;
}
// container of confusion matrices generated in each fold
Map<String, List<int[]>> tmp = new HashMap<String, List<int[]>>();
int[] classes = null;
if (isLoadDataFromLocalFileSystem) {
System.out.println("load data from local file system: " + root);
// cross validation
for (int foldIndex = 0; foldIndex < this.fold; foldIndex++) {
Configuration.out.printf(" > %s:fold %d\n", this.name, foldIndex);
// get training and test sets
File trainFile = new File(root + File.separator + "fold_" + foldIndex + File.separator + "train.json");
TimeDatabase trainDb = TimeDatabase.deserialize(trainFile.getAbsolutePath());
File testFile = new File(root + File.separator + "fold_" + foldIndex + File.separator + "test.json");
TimeDatabase testDb = TimeDatabase.deserialize(testFile.getAbsolutePath());
File validFile = new File(root + File.separator + "fold_" + foldIndex + File.separator + "valid.json");
TimeDatabase validDb = validFile.exists() ? TimeDatabase.deserialize(validFile.getAbsolutePath()) : null;
// initial classes
if (classes == null) {
classes = trainDb.getClasses();
}
// log
if (this.isPrintInfo) {
Configuration.out.println(trainDb.simpleDescribe(String.format("%s:training set", this.name)));
Configuration.out.println(validDb == null ? "null" : validDb.simpleDescribe(String.format("%s:validation set", this.name)));
Configuration.out.println(testDb.simpleDescribe(String.format("%s:test set", this.name)));
}
// do validation
Map<String, List<int[]>> truePredLabelPairsMap = validation(foldIndex, trainDb, validDb, testDb);
for (Map.Entry<String, List<int[]>> entry : truePredLabelPairsMap.entrySet()) {
String key = entry.getKey();
List<int[]> truePredLabelPairs = entry.getValue();
List<int[]> result = tmp.get(key);
if (result == null) {
result = new ArrayList<int[]>();
}
result.addAll(truePredLabelPairs);
tmp.put(key, result);
}
}
} else {
System.out.println("init data from scratch");
// load dataset
TimeDatabase db = DataSource.initDatabase();
// log
if (this.isPrintInfo) {
Configuration.out.println(db.describe(this.name + ":dataset"));
}
// split training and test sets
int tpNum = db.getNumberOfTimePoints();
Assertion.assertPositive(tpNum);
int[][][] trainAndTestFolds = DataSource.splitTrainAndTestByKFold(tpNum, this.fold);
// cross validation
for (int foldIndex = 0; foldIndex < trainAndTestFolds.length; foldIndex++) {
Configuration.out.printf(" > %s:fold %d\n", this.name, foldIndex);
// get training and test sets
int[] trainIndices = trainAndTestFolds[foldIndex][0];
int[] testIndices = trainAndTestFolds[foldIndex][1];
TimeDatabase[] trainValidAndTestDb = prepareTrainValidAndTestSets(db.slice(trainIndices), db.slice(testIndices));
TimeDatabase trainDb = trainValidAndTestDb[0];
TimeDatabase validDb = trainValidAndTestDb[1];
TimeDatabase testDb = trainValidAndTestDb[2];
// initial classes
if (classes == null) {
classes = trainDb.getClasses();
}
// serialize
TimeDatabase.serialize(trainDb, root + File.separator + "fold_" + foldIndex + File.separator + "train.json");
TimeDatabase.serialize(testDb, root + File.separator + "fold_" + foldIndex + File.separator + "test.json");
if (validDb != null) {
TimeDatabase.serialize(validDb, root + File.separator + "fold_" + foldIndex + File.separator + "valid.json");
}
// log
if (this.isPrintInfo) {
Configuration.out.println(trainDb.simpleDescribe(String.format("%s:training set", this.name)));
Configuration.out.println(validDb == null ? "null" : validDb.simpleDescribe(String.format("%s:validation set", this.name)));
Configuration.out.println(testDb.simpleDescribe(String.format("%s:test set", this.name)));
}
// do validation
Map<String, List<int[]>> truePredLabelPairsMap = validation(foldIndex, trainDb, validDb, testDb);
for (Map.Entry<String, List<int[]>> entry : truePredLabelPairsMap.entrySet()) {
String key = entry.getKey();
List<int[]> truePredLabelPairs = entry.getValue();
List<int[]> result = tmp.get(key);
if (result == null) {
result = new ArrayList<int[]>();
}
result.addAll(truePredLabelPairs);
tmp.put(key, result);
}
}
}
List<String> keys = new ArrayList<String>(tmp.keySet());
Collections.sort(keys);
Map<String, Evaluation> result = new HashMap<String, Evaluation>();
for (String key : keys) {
List<int[]> value = tmp.get(key);
Evaluation evaluation = Evaluation.evaluate(key, classes, value, true);
result.put(key, evaluation);
}
return result;
}
protected abstract TimeDatabase[] prepareTrainValidAndTestSets(TimeDatabase trainDb, TimeDatabase testDb);
protected abstract Map<String, List<int[]>> validation(int foldIndex, TimeDatabase trainDb, TimeDatabase validDb, TimeDatabase testDb) throws Exception;
}

View File

@ -0,0 +1,301 @@
package edu.nju.ics.frontier.learning;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Assertion;
import java.io.File;
import java.io.FileFilter;
public class ValidationResultAnalysis {
public static void main(String[] args) {
String coarseRegex = "[0-9]{8}_[0-9]{6}_erase_rate_0[.][0-9]0[.]txt";
String coarseCvFilename = "cv_erase_rate_from_0.1_to_0.9_in_0.1_step.csv";
String coarseTvFilename = "tv_erase_rate_from_0.1_to_0.9_in_0.1_step.csv";
String fineRegex = "[0-9]{8}_[0-9]{6}_erase_rate_0[.][0-9][1-9][.]txt";
String fineCvFilename = "cv_erase_rate_from_0.91_to_0.99_in_0.01_step.csv";
String fineTvFilename = "tv_erase_rate_from_0.91_to_0.99_in_0.01_step.csv";
parseCrossValidationResult(coarseRegex, coarseCvFilename);
parseCrossValidationResult(fineRegex, fineCvFilename);
parseTrainAndValidResult(coarseRegex, coarseTvFilename);
parseTrainAndValidResult(fineRegex, fineTvFilename);
}
private static void parseTrainAndValidResult(final String regex, final String filename) {
String root = "C:\\Users\\zzw\\Desktop\\fujitsu\\log\\";
File[] files = new File(root).listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isFile() &&
pathname.getName().matches(regex);
}
});
Configuration.out.open(root + filename);
for (int i = 0; i < files.length; i++) {
String name = files[i].getName();
double eraseRate = Double.parseDouble(name.substring(name.lastIndexOf("_") + 1, name.lastIndexOf(".")));
// Configuration.out.open(String.format("%stv_erase_rate_%.2f.csv", root, eraseRate));
System.out.printf("[%2d] %.2f %s\n", i + 1, eraseRate, files[i].getName());
parseTrainAndValidResult(files[i].getAbsolutePath(), eraseRate, i == 0);
}
Configuration.out.close();
}
private static void parseTrainAndValidResult(String path, double eraseRate, boolean hasHeader) {
int foldIndex = -1;
String model = null;
String strategy = null;
int epoch = -1;
Dataset trainSet = null;
Dataset validSet = null;
double distance = -1;
Metrics finalMetrics = null;
Metrics interMetrics = null;
Metrics intraMetrics = null;
Metrics obserMetrics = null;
int rowCount = 0;
OkTextReader reader = new OkTextReader();
reader.open(path);
String line;
while ((line = reader.readLine()) != null) {
// parse fold
if (line.startsWith(" >")) {
foldIndex = Integer.parseInt(line.substring(line.length() - 1));
}
// parse model+strategy+epoch{
if (line.endsWith("{")) {
Assertion.assertNotNegative(foldIndex);
String[] info = line.substring(0, line.length() - 1).split("[+]");
model = info[0];
strategy = info[1];
if ("init".equals(info[2])) {
epoch = 0;
} else {
epoch = Integer.parseInt(info[2]) + 1;
}
}
// parse train{all:[0-9]+,lbl:[0-9]+,unl:[0-9]+,emp:[0-9]+,cls\[[0-9]+,[0-9]+,[0-9]+\]}
// parse valid{all:[0-9]+,lbl:[0-9]+,unl:[0-9]+,emp:[0-9]+,cls\[[0-9]+,[0-9]+,[0-9]+\]}
if (line.startsWith("train") || line.startsWith("valid")) {
if (line.equals("valid{null}")) {
continue;
}
int labeledNum = Integer.parseInt(line.substring(line.indexOf("lbl") + 4, line.indexOf("unl") - 1));
int unlabeledNum = Integer.parseInt(line.substring(line.indexOf("unl") + 4, line.indexOf("emp") - 1));
String[] classNumStrs = line.substring(line.indexOf("cls") + 4, line.length() - 2).split(",");
int[] classNums = new int[classNumStrs.length];
for (int i = 0; i < classNums.length; i++) {
classNums[i] = Integer.parseInt(classNumStrs[i]);
}
Dataset dataset = new Dataset(labeledNum, unlabeledNum, classNums);
if (line.startsWith("train")) {
Assertion.assertNotNull(model);
Assertion.assertNotNull(strategy);
Assertion.assertNotNegative(epoch);
trainSet = dataset;
} else {
Assertion.assertNotNull(trainSet);
validSet = dataset;
}
}
// parse change:9363.3297839872539043426513671875 or retain
if (line.startsWith("change") || line.startsWith("retain")) {
if (line.startsWith("change")) {
distance = Double.parseDouble(line.substring(line.indexOf(":") + 1));
} else {
distance = 0.0;
}
}
// parse final{Accuracy:0.155,Precision:0.948,Recall:0.155,F1 Score:0.184,Matrix:[[2,1,67][0,10,14][0,0,3]]}
// parse inter{Accuracy:0.206,Precision:0.831,Recall:0.206,F1 Score:0.239,Matrix:[[7,13,50][0,10,14][0,0,3]]}
// parse intra{Accuracy:0.113,Precision:0.929,Recall:0.113,F1 Score:0.144,Matrix:[[3,1,66][0,5,19][0,0,3]]}
// parse obser{Accuracy:0.227,Precision:0.814,Recall:0.227,F1 Score:0.252,Matrix:[[8,19,43][0,11,13][0,0,3]]}
if (line.startsWith("final") || line.startsWith("inter") || line.startsWith("intra") || line.startsWith("obser")) {
double accuracy = Double.parseDouble(line.substring(line.indexOf("Accuracy:") + 9, line.indexOf("Precision:") - 1));
double precision = Double.parseDouble(line.substring(line.indexOf("Precision:") + 10, line.indexOf("Recall:") - 1));
double recall = Double.parseDouble(line.substring(line.indexOf("Recall:") + 7, line.indexOf("F1 Score:") - 1));
double f1Score = Double.parseDouble(line.substring(line.indexOf("F1 Score:") + 9, line.indexOf("Matrix:") - 1));
Metrics metrics = new Metrics(accuracy, precision, recall, f1Score);
if (line.startsWith("final")) {
finalMetrics = metrics;
} else if (line.startsWith("inter")) {
interMetrics = metrics;
} else if (line.startsWith("intra")) {
intraMetrics = metrics;
} else {
obserMetrics = metrics;
}
}
// parse }
if (line.equals("}")) {
if (hasHeader && rowCount == 0) {
Configuration.out.print("EraseRate,Fold,Models,Strategies,Combinations,Epoch,TrainLabeledNum,TrainUnlabeledNum,TrainClassNum,TrainLabeledPro");
if (validSet != null) {
Configuration.out.print(",ValidLabeledNum,ValidUnlabeledNum,ValidClassNum,ValidLabeledPro");
}
Configuration.out.print(",Distance");
if (obserMetrics != null) {
Configuration.out.print(",obserAccuracy,obserPrecision,obserRecall,obserF1Score");
}
if (intraMetrics != null) {
Configuration.out.print(",intraAccuracy,intraPrecision,intraRecall,intraF1Score");
}
if (interMetrics != null) {
Configuration.out.print(",interAccuracy,interPrecision,interRecall,interF1Score");
}
if (finalMetrics != null) {
Configuration.out.print(",finalAccuracy,finalPrecision,finalRecall,finalF1Score");
}
Configuration.out.println();
}
Configuration.out.printf("%.2f,%d,%s,%s,%s,%d,%s", eraseRate, foldIndex, model, strategy, model + "+" + strategy, epoch, trainSet);
model = null;
strategy = null;
epoch = -1;
trainSet = null;
if (validSet != null) {
Configuration.out.printf(",%s", validSet);
validSet = null;
}
Configuration.out.printf(",%f", distance);
distance = -1;
if (obserMetrics != null) {
Configuration.out.printf(",%s", obserMetrics);
obserMetrics = null;
}
if (intraMetrics != null) {
Configuration.out.printf(",%s", intraMetrics);
intraMetrics = null;
}
if (interMetrics != null) {
Configuration.out.printf(",%s", interMetrics);
interMetrics = null;
}
if (finalMetrics != null) {
Configuration.out.printf(",%s", finalMetrics);
finalMetrics = null;
}
Configuration.out.println();
rowCount++;
}
}
reader.close();
}
private static void parseCrossValidationResult(final String regex, final String filename) {
String root = "C:\\Users\\zzw\\Desktop\\fujitsu\\log\\";
File[] files = new File(root).listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isFile() &&
pathname.getName().matches(regex);
}
});
Configuration.out.open(root + filename);
Configuration.out.println("EraseRate,Models,Strategies,Combinations,Phases,Metrics,Value");
for (int i = 0; i < files.length; i++) {
String name = files[i].getName();
double eraseRate = Double.parseDouble(name.substring(name.lastIndexOf("_") + 1, name.lastIndexOf(".")));
System.out.printf("[%2d] %.2f %s\n", i + 1, eraseRate, files[i].getName());
parseCrossValidationResult(files[i].getAbsolutePath(), eraseRate);
}
Configuration.out.close();
}
private static void parseCrossValidationResult(String path, final double eraseRate) {
String model = null;
String strategy = null;
String phase = null;
double accuracy = -1.0;
double precision = -1.0;
double recall = -1.0;
double f1Score = -1.0;
OkTextReader reader = new OkTextReader();
reader.open(path);
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("[")) {
Assertion.assertNull(model);
Assertion.assertNull(strategy);
Assertion.assertNull(phase);
Assertion.assertEqual(accuracy, -1.0);
Assertion.assertEqual(precision, -1.0);
Assertion.assertEqual(recall, -1.0);
Assertion.assertEqual(f1Score, -1.0);
String[] info = line.substring(1, line.length() - 1).split(":");
model = info[0];
strategy = info[1];
phase = info[2];
} else if (line.startsWith("Correctly")) {
Assertion.assertNotNull(model);
Assertion.assertNotNull(strategy);
Assertion.assertNotNull(phase);
accuracy = Double.parseDouble(line.substring(line.length() - 9, line.length() - 2));
accuracy /= 100.0;
} else if (line.startsWith("Weighted")) {
Assertion.assertNotNegative(accuracy);
precision = Double.parseDouble(line.substring(35, 40));
recall = Double.parseDouble(line.substring(46, 51));
f1Score = Double.parseDouble(line.substring(55, 60));
Configuration.out.printf("%.2f,%s,%s,%s,%s,Accuracy,%.3f\n", eraseRate, model, strategy, model + "+" + strategy, phase, accuracy);
Configuration.out.printf("%.2f,%s,%s,%s,%s,Precision,%.3f\n", eraseRate, model, strategy, model + "+" + strategy, phase, precision);
Configuration.out.printf("%.2f,%s,%s,%s,%s,Recall,%.3f\n", eraseRate, model, strategy, model + "+" + strategy, phase, recall);
Configuration.out.printf("%.2f,%s,%s,%s,%s,F1 Score,%.3f\n", eraseRate, model, strategy, model + "+" + strategy, phase, f1Score);
model = null;
strategy = null;
phase = null;
accuracy = -1.0;
precision = -1.0;
recall = -1.0;
f1Score = -1.0;
}
}
reader.close();
}
private static class Dataset {
public final int labeledNum;
public final int unlabeledNum;
public final int[] classNums;
public final double labeledProportion;
public Dataset(int labeledNum, int unlabeledNum, int[] classNums) {
this.labeledNum = labeledNum;
this.unlabeledNum = unlabeledNum;
this.classNums = classNums;
this.labeledProportion = ((double) this.labeledNum) / ((double) (this.labeledNum + this.unlabeledNum));
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(labeledNum).append(",").append(unlabeledNum).append(",");
builder.append(classNums[0]);
for (int i = 1; i < classNums.length; i++) {
builder.append(":").append(classNums[i]);
}
builder.append(",").append(labeledProportion);
return builder.toString();
}
}
private static class Metrics {
public final double accuracy;
public final double precision;
public final double recall;
public final double f1Score;
public Metrics(double accuracy, double precision, double recall, double f1Score) {
this.accuracy = accuracy;
this.precision = precision;
this.recall = recall;
this.f1Score = f1Score;
}
@Override
public String toString() {
return String.format("%.3f,%.3f,%.3f,%.3f", accuracy, precision, recall, f1Score);
}
}
}

View File

@ -0,0 +1,196 @@
package edu.nju.ics.frontier.motivating_example;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class DataPreparation {
public static void main(String[] args) {
String root = "C:\\Users\\zzw\\Desktop\\fujitsu\\motivating_example\\";
List<String[]> srcData = loadCsvData(root + "representative1.csv");
OkTextWriter writer = new OkTextWriter();
writer.open(root + "representative2.csv");
writer.println("team,user,date,timestamp,timedelta,addedLocSum,deletedLocSum,locGainSum,addedOrDeletedLocSum,label");
int offset = 1;
List<String[]> srcSession = getNextSession(srcData, offset);
while (srcSession != null) {
List<String[]> destSession = downSampling(srcSession, 2, false);
if (destSession != null) {
List<String[]> variables = extractVariables(srcData.get(0), destSession);
saveCsvData(variables, writer);
}
offset += srcSession.size();
srcSession = getNextSession(srcData, offset);
}
writer.close();
}
private static List<String[]> loadCsvData(String path) {
List<String[]> result = new ArrayList<String[]>();
OkTextReader reader = new OkTextReader();
reader.open(path);
String line;
while ((line = reader.readLine()) != null) {
result.add(line.split(","));
}
reader.close();
return result.isEmpty() ? null : result;
}
private static List<String[]> extractVariables(String[] head, List<String[]> session) {
List<String[]> result = new ArrayList<String[]>();
int teamIndex = argWhere(head, "team");
int userIndex = argWhere(head, "user");
int dateIndex = argWhere(head, "date");
int sliceBegTimeIndex = argWhere(head, "sliceBegTime");
int addedLocSumIndex = argWhere(head, "addedLocSum");
int deletedLocSumIndex = argWhere(head, "deletedLocSum");
int locGainSumIndex = argWhere(head, "locGainSum");
int addedOrDeletedLocSumIndex = argWhere(head, "addedOrDeletedLocSum");
int internalScoreIndex = argWhere(head, "internalScore");
for (String[] row : session) {
int internalScore = Integer.parseInt(row[internalScoreIndex]);
// Assertion.assertPositive(internalScore);
int label;
if (internalScore == -1) {
label = 0;
} else if (internalScore < 3) {
label = 1;
} else if (internalScore == 3) {
label = 2;
} else {
label = 3;
}
long timestamp = Long.parseLong(row[sliceBegTimeIndex]);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.HOUR_OF_DAY, 0);
long timedelta = timestamp - calendar.getTimeInMillis();
double second = ((double) timedelta) / 1000.0;
double minute = second / 60.0;
double hour = minute / 60.0;
result.add(new String[]{
row[teamIndex],
row[userIndex],
row[dateIndex],
row[sliceBegTimeIndex],
String.valueOf(hour),
row[addedLocSumIndex],
row[deletedLocSumIndex],
row[locGainSumIndex],
row[addedOrDeletedLocSumIndex],
String.valueOf(label),
});
}
return result.isEmpty() ? null : result;
}
private static void saveCsvData(List<String[]> variables, OkTextWriter writer) {
if (variables != null) {
for (String[] row : variables) {
for (int i = 0; i < row.length - 1; i++) {
writer.print(row[i] + ",");
}
writer.println(row[row.length - 1]);
}
}
}
private static List<String[]> getNextSession(List<String[]> data, int offset) {
if (data == null || data.isEmpty() || offset < 0 || offset >= data.size()) {
return null;
}
List<String[]> result = new ArrayList<String[]>();
String[] row = data.get(offset);
int sessionId = Integer.parseInt(row[7]);
result.add(row);
for (int i = offset + 1; i < data.size(); i++) {
row = data.get(i);
int id = Integer.parseInt(row[7]);
if (sessionId == id) {
result.add(row);
} else {
break;
}
}
return result.isEmpty() ? null : result;
}
private static List<String[]> downSampling(List<String[]> session, int stride, boolean isOnlyLabeled) {
List<String[]> result = new ArrayList<String[]>();
long sliceBegTime = Long.parseLong(session.get(0)[3]);
long thirtyMinute = 30 * 60 * 1000;
int i;
if (sliceBegTime % thirtyMinute == 0) {
i = 0;
} else {
i = 1;
}
for (; i < session.size(); i += stride) {
String[] row = session.get(i);
if (isOnlyLabeled) {
if (Integer.parseInt(row[row.length - 2]) == -1) {
continue;
}
}
result.add(row);
}
// int prevIndex = -1;
// int nextIndex = getIndexOfNextLabeledItem(session, 0);
// while (nextIndex != -1) {
// if (prevIndex == -1) {
// result.add(session.get(nextIndex));
// prevIndex = nextIndex;
// nextIndex = getIndexOfNextLabeledItem(session, nextIndex + 1);
// } else {
// if (nextIndex - prevIndex >= stride) {
// result.add(session.get(nextIndex));
// prevIndex = nextIndex;
// nextIndex = getIndexOfNextLabeledItem(session, nextIndex + 1);
// } else {
// nextIndex = getIndexOfNextLabeledItem(session, nextIndex + 1);
// }
// }
// }
// for (int i = 0; i < session.size(); i += stride) {
// String[] row = session.get(i);
// if (isOnlyLabeled) {
// if (Integer.parseInt(row[row.length - 2]) == -1) {
// continue;
// }
// }
// result.add(row);
// }
return result.isEmpty() ? null : result;
}
private static int getIndexOfNextLabeledItem(List<String[]> session, int offset) {
if (session == null || session.isEmpty() || offset < 0 || offset >= session.size()) {
return -1;
}
for (int i = offset; i < session.size(); i++) {
String[] row = session.get(i);
if (Integer.parseInt(row[row.length - 2]) != -1) {
return i;
}
}
return -1;
}
private static <T> int argWhere(T[] array, T value) {
for (int i = 0; i < array.length; i++) {
if (array[i].equals(value)) {
return i;
}
}
return -1;
}
}

View File

@ -0,0 +1,62 @@
package edu.nju.ics.frontier.parse;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractEventLabelTask implements Executor.Task {
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File dateDir = new File(FileUtil.combine(userDir.getAbsolutePath(), dateStr));
if (!dateDir.exists()) {
return;
}
File[] eventOrLabelFiles = FileUtil.listSubFiles(dateDir, null, ".json");
if (eventOrLabelFiles == null) {
return;
}
List<File> eventFiles = new ArrayList<File>();
List<File> labelFiles = new ArrayList<File>();
for (File eventOrLabelFile : eventOrLabelFiles) {
String fileName = eventOrLabelFile.getName();
if (fileName.startsWith("event")) {
eventFiles.add(eventOrLabelFile);
} else if (fileName.startsWith("label")) {
labelFiles.add(eventOrLabelFile);
}
}
List<IntelliJEvent> events = null;
if (!eventFiles.isEmpty()) {
events = new ArrayList<IntelliJEvent>();
for (File eventFile : eventFiles) {
List<IntelliJEvent> tmp = DataSource.loadEvents(eventFile);
if (tmp != null) {
events.addAll(tmp);
}
}
}
List<Label> labels = null;
if (!labelFiles.isEmpty()) {
labels = new ArrayList<Label>();
for (File labelFile : labelFiles) {
List<Label> tmp = DataSource.loadLabels(labelFile);
if (tmp != null) {
labels.addAll(tmp);
}
}
}
process(srcRoot, destRoot, teamDir, userDir, dateDir, events, labels);
}
protected abstract void process(String srcRoot, String destRoot, File teamDir, File userDir, File dateDir, List<IntelliJEvent> events, List<Label> labels);
}

View File

@ -0,0 +1,59 @@
package edu.nju.ics.frontier.parse;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.intellij.WindowEvent;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CursorContextTask implements Executor.Task {
private Map<String, String> mFrame2Project;
private Map<String, Set<String>> mFrame2Files;
private Map<String, String> mFrame2FocusedFile;
private String mFocusedFrame;
private String mFocusedProject;
private String mFocusedFile;
public CursorContextTask() {
this.mFrame2Project = new HashMap<String, String>();
this.mFrame2Files = new HashMap<String, Set<String>>();
this.mFrame2FocusedFile = new HashMap<String, String>();
this.mFocusedFrame = null;
this.mFocusedProject = null;
this.mFocusedFile = null;
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
for (File dateDir : dateDirs) {
List<IntelliJEvent> events = ParseUtil.loadWorkdayEvents(dateDir);
if (events == null) {
continue;
}
for (IntelliJEvent event : events) {
if (IntelliJEvent.isWindowEvent(event)) {
WindowEvent windowEvent = IntelliJEvent.toWindowEvent(event);
if (windowEvent.isWhenWindowActivated()) {
String winName = windowEvent.getThisWindowName();
if (winName.startsWith("frame")) {
if (!winName.equals(mFocusedFrame)) {
mFocusedFrame = winName;
mFocusedProject = mFrame2Project.get(mFocusedFrame);
mFocusedFile = mFrame2FocusedFile.get(mFocusedFrame);
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,186 @@
package edu.nju.ics.frontier.parse;
import com.google.gson.Gson;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.bean.intellij.CursorEvent;
import edu.nju.ics.frontier.bean.intellij.FileEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.Logger;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class IntelliJEventCleanTask implements Executor.Task {
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File intellijDir = new File(FileUtil.combine(userDir.getAbsolutePath(), "interaction_traces", "intellij"));
if (!intellijDir.exists()) {
return;
}
StringBuilder builder = new StringBuilder();
builder.append(getClass().getName()).append(" {").
append("\n arguments:\n intellijDir=").
append(intellijDir.getAbsolutePath()).
append("\n date=").append(dateStr);
File dateDir = new File(FileUtil.combine(intellijDir.getAbsolutePath(), dateStr));
File[] eventFiles = FileUtil.listSubFiles(dateDir, null, ".json");
if (eventFiles != null) {
for (File eventFile : eventFiles) {
List<IntelliJEvent> data = loadData(eventFile, userDir.getName());
File dest = saveData(eventFile.getParentFile(), data);
eventFile.delete();
builder.append("\n data cleaned:\n src=").
append(eventFile.getAbsolutePath()).
append("\n dest=").
append(dest == null ? "null" : dest.getAbsolutePath());
}
}
builder.append("\n}");
Logger.println(builder.toString());
}
private List<IntelliJEvent> loadData(File file, String user) {
List<IntelliJEvent> result = new ArrayList<IntelliJEvent>();
OkTextReader reader = new OkTextReader();
Gson gson = new Gson();
reader.open(file);
String line;
while ((line = reader.readLine()) != null) {
IntelliJEvent event = gson.fromJson(line, IntelliJEvent.class);
if (isFileContentChangedEvent(event) ||
isAnonymousFileChanged(event) ||
isFileCreatedOrDeletedEvent(event) ||
isCursorAutoChangedInConsoleEvent(event)) {
continue;
}
if (IntelliJEvent.isFileEvent(event)) {
FileEvent fileEvent = IntelliJEvent.toFileEvent(event);
if (fileEvent.isWhenSelected()) {
String oldPath = fileEvent.getOldPathField();
String newPath = fileEvent.getNewPathField();
if (oldPath != null) {
if (!oldPath.equals(newPath)) {
result.add(event);
}
} else if (newPath != null) {
result.add(event);
}
} else {
String path = fileEvent.getPathField();
String project = ProjectFileMapping.getProjectByFile4User(user, path);
if (project != null) {
event.addExtraData("project", project);
result.add(event);
}
}
} else {
result.add(event);
}
}
reader.close();
if (!result.isEmpty()) {
boolean isCursorChangedFollowingFileChangedPattern = true;
for (int i = 0; i < result.size(); i++) {
IntelliJEvent event = result.get(i);
if (!(isFileChangedEvent(event) || isCursorChangedInConsoleEvent(event))) {
isCursorChangedFollowingFileChangedPattern = false;
break;
}
}
if (isCursorChangedFollowingFileChangedPattern) {
result.clear();
}
}
return result.isEmpty() ? null : result;
}
private boolean isFileContentChangedEvent(IntelliJEvent event) {
return "File".equals(event.getType()) && "content_changed".equals(event.getWhen());
}
private boolean isAnonymousFileChanged(IntelliJEvent event) {
return "File".equals(event.getType()) && "changed".equals(event.getWhen()) &&
(event.getExtraData("path") == null);
}
private boolean isFileChangedEvent(IntelliJEvent event) {
return "File".equals(event.getType()) && "changed".equals(event.getWhen());
}
private boolean isCursorAutoChangedInConsoleEvent(IntelliJEvent event) {
if (!IntelliJEvent.isCursorEvent(event)) {
return false;
}
CursorEvent cursorEvent = IntelliJEvent.toCursorEvent(event);
if (!cursorEvent.isWhenCursorChanged()) {
return false;
}
if (!cursorEvent.isConsoleEditor()) {
return false;
}
long[] oldPos = CursorEvent.getLineAndColumn(cursorEvent.getOldPosField());
long[] newPos = CursorEvent.getLineAndColumn(cursorEvent.getNewPosField());
return oldPos[1] == 0 && newPos[1] == 0;
}
private boolean isCursorChangedInConsoleEvent(IntelliJEvent event) {
if (!"Cursor".equals(event.getType())) {
return false;
}
if (!"cursor_changed".equals(event.getWhen())) {
return false;
}
if (!event.hasData()) {
return false;
}
String editor = event.getData().get("editor");
return (editor != null && "CONSOLE".equals(editor));
}
private boolean isFileCreatedOrDeletedEvent(IntelliJEvent event) {
if (!IntelliJEvent.isFileEvent(event)) {
return false;
}
FileEvent fileEvent = IntelliJEvent.toFileEvent(event);
return fileEvent.isWhenCreated() || fileEvent.isWhenDeleted();
}
private File saveData(File dateDir, List<IntelliJEvent> data) {
if (data == null) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String begTimeStr = sdf.format(new Date(data.get(0).getTime()));
String endTimeStr = sdf.format(new Date(data.get(data.size() - 1).getTime()));
String fileName = begTimeStr + "-" + endTimeStr + ".json";
String path = FileUtil.combine(dateDir.getAbsolutePath(), fileName);
OkTextWriter writer = new OkTextWriter();
Gson gson = new Gson();
writer.open(path);
for (int i = 0; i < data.size(); i++) {
String json = gson.toJson(data.get(i));
writer.println(json);
}
writer.close();
return new File(path);
}
}

View File

@ -0,0 +1,132 @@
package edu.nju.ics.frontier.parse;
import com.google.gson.Gson;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.Logger;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class IntelliJEventParseTask implements Executor.Task {
private static final long ONE_DAY = 24 * 60 * 60 * 1000;
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File intellijDir = new File(FileUtil.combine(userDir.getAbsolutePath(), "interaction_traces", "intellij"));
if (!intellijDir.exists()) {
return;
}
File destDir = new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName()));
StringBuilder builder = new StringBuilder();
builder.append(getClass().getName()).append(" {").
append("\n arguments:\n intellijDir=").
append(intellijDir.getAbsolutePath()).
append("\n destDir=").append(destDir.getAbsolutePath()).
append("\n date=").append(dateStr);
List<IntelliJEvent> intellijData = parseDataFromIntellijDirectory(intellijDir, dateStr);
builder.append("\n intellij parsed: #data=").
append((intellijData == null) ? 0 : intellijData.size());
List<List<IntelliJEvent>> dividedData = divideData(intellijData);
builder.append("\n data divided: #day=").
append((dividedData == null) ? 0 : dividedData.size());
saveData(dividedData, destDir);
builder.append("\n data saved\n}");
Logger.println(builder.toString());
}
private List<IntelliJEvent> parseDataFromIntellijDirectory(File intellijDir, String dateStr) {
OkTextReader reader = new OkTextReader();
Gson gson = new Gson();
List<IntelliJEvent> events = new ArrayList<IntelliJEvent>();
File dateDir = new File(intellijDir.getAbsolutePath() + File.separator + dateStr);
File[] eventFiles = FileUtil.listSubFiles(dateDir, null, ".json");
if (eventFiles == null) {
return null;
}
for (File eventFile : eventFiles) {
reader.open(eventFile);
String line;
while ((line = reader.readLine()) != null) {
IntelliJEvent event = gson.fromJson(line, IntelliJEvent.class);
events.add(event);
}
reader.close();
}
return events.isEmpty() ? null : events;
}
private List<List<IntelliJEvent>> divideData(List<IntelliJEvent> mergedData) {
if (mergedData == null) {
return null;
}
List<IntelliJEvent> copiedMergedData = new ArrayList<IntelliJEvent>(mergedData);
List<List<IntelliJEvent>> result = new ArrayList<List<IntelliJEvent>>();
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(copiedMergedData.get(0).getTime());
calendar.set(Calendar.HOUR_OF_DAY, 6);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long endTime = calendar.getTimeInMillis() + ONE_DAY;
List<IntelliJEvent> tmp = new ArrayList<IntelliJEvent>();
while (!copiedMergedData.isEmpty()) {
if (copiedMergedData.get(0).getTime() < endTime) {
tmp.add(copiedMergedData.remove(0));
} else {
if (!tmp.isEmpty()) {
result.add(tmp);
}
// prepare next day
endTime += ONE_DAY;
tmp = new ArrayList<IntelliJEvent>();
}
}
if (!tmp.isEmpty()) {
result.add(tmp);
}
return (result.isEmpty() ? null : result);
}
private void saveData(List<List<IntelliJEvent>> dividedData, File destDir) {
if (dividedData == null) {
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss");
Gson gson = new Gson();
for (int i = 0; i < dividedData.size(); i++) {
List<IntelliJEvent> data = dividedData.get(i);
String dateStr = dateFormat.format(new Date(data.get(0).getTime()));
String begTimeStr = timeFormat.format(new Date(data.get(0).getTime()));
String endTimeStr = timeFormat.format(new Date(data.get(data.size() - 1).getTime()));
String path = destDir.getAbsolutePath() + File.separator + dateStr + File.separator +
"event-" + begTimeStr + "-" + endTimeStr + ".json";
FileUtil.toJson(gson, data, new File(path));
}
}
}

View File

@ -0,0 +1,217 @@
package edu.nju.ics.frontier.parse;
import com.google.gson.Gson;
import edu.nju.ics.frontier.bean.label.*;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.Logger;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
public class LabelParseTask implements Executor.Task {
private static final long ONE_DAY = 24 * 60 * 60 * 1000;
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File destDir = new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName()));
File scaleDir = new File(FileUtil.combine(userDir.getAbsolutePath(), "interaction_traces", "scale"));
if (!scaleDir.exists()) {
return;
}
StringBuilder builder = new StringBuilder();
builder.append(getClass().getName()).append(" {").
append("\n arguments:\n scale=").
append(scaleDir.getAbsolutePath()).
append("\n dest=").append(destDir.getAbsolutePath()).
append("\n date=").append(dateStr);
List<Label> scaleData = parseData(scaleDir, dateStr);
builder.append("\n scale parsed: #data=").
append((scaleData == null) ? 0 : scaleData.size());
Map<String, List<Label>> dividedData = divideData(scaleData);
builder.append("\n data divided: #day=").
append((dividedData == null) ? 0 : dividedData.size());
saveData(dividedData, destDir);
builder.append("\n data saved\n}");
Logger.println(builder.toString());
}
private Label parseLabel(File file) {
long begTime = -1;
long endTime = -1;
List<Action> actions = new ArrayList<Action>();
OkTextReader reader = new OkTextReader();
reader.open(file);
String line;
while ((line = reader.readLine()) != null) {
int commaIndex1 = line.indexOf(",");
int commaIndex2 = line.indexOf(",", commaIndex1 + 1);
long time = Long.parseLong(line.substring(0, commaIndex1));
String action = line.substring(commaIndex1 + 1, commaIndex2);
if ("begin".equals(action)) {
begTime = time;
} else if ("end".equals(action)) {
endTime = time;
} else if ("select".equals(action)) {
AnswerModel model = new Gson().fromJson(
line.substring(commaIndex2 + 1), AnswerModel.class);
SelectOrSubmitAction.Dimension dimension = "engagement".equals(model.title) ?
SelectOrSubmitAction.Dimension.ENGAGEMENT :
SelectOrSubmitAction.Dimension.PRODUCTIVITY;
actions.add(new SelectAction(time, dimension, model.answer, model.optionScore));
} else if ("submit".equals(action)) {
AnswerModel model = new Gson().fromJson(
line.substring(commaIndex2 + 1), AnswerModel.class);
SelectOrSubmitAction.Dimension dimension = "engagement".equals(model.title) ?
SelectOrSubmitAction.Dimension.ENGAGEMENT :
SelectOrSubmitAction.Dimension.PRODUCTIVITY;
actions.add(new SubmitAction(time, dimension, model.answer, model.optionScore));
} else if ("refuse".equals(action)) {
String option = line.substring(commaIndex2 + 1);
RefuseAction.OutOfIDEActivity activity = null;
if ("meeting".equals(option)) {
activity = RefuseAction.OutOfIDEActivity.MEETING;
} else if ("rest".equals(option)) {
activity = RefuseAction.OutOfIDEActivity.REST;
} else if ("talk".equals(option)) {
activity = RefuseAction.OutOfIDEActivity.TALK;
} else if ("other".equals(option)) {
activity = RefuseAction.OutOfIDEActivity.OTHER;
}
if (activity != null) {
actions.add(new RefuseAction(time, activity));
}
}
}
reader.close();
if (begTime == -1) {
return null;
}
if (endTime == -1) {
if (!actions.isEmpty()) {
endTime = actions.get(actions.size() - 1).getTime();
} else {
endTime = begTime;
}
}
return new Label(begTime, endTime, actions);
}
private List<Label> parseData(File scaleDir, String dateStr) {
List<Label> result = new ArrayList<Label>();
File dateDir = new File(FileUtil.combine(scaleDir.getAbsolutePath(), dateStr));
File[] labelFiles = FileUtil.listSubFiles(dateDir, null, ".json");
if (labelFiles == null) {
return null;
}
for (File labelFile : labelFiles) {
Label label = parseLabel(labelFile);
if (label != null) {
result.add(label);
}
}
return result.isEmpty() ? null : result;
}
private Map<String, List<Label>> divideData(List<Label> scaleData) {
if (scaleData == null) {
return null;
}
Map<String, List<Label>> result = new HashMap<String, List<Label>>();
for (Label label : scaleData) {
String k = getLabelDate(label);
if (k == null) {
continue;
}
List<Label> v = result.get(k);
if (v == null) {
v = new ArrayList<Label>();
}
v.add(label);
result.put(k, v);
}
return (result.isEmpty() ? null : result);
}
private void saveData(Map<String, List<Label>> dividedData, File destDir) {
if (dividedData == null) {
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat timeFormat = new SimpleDateFormat("HHmmss");
for (Map.Entry<String, List<Label>> entry : dividedData.entrySet()) {
String dateStr = entry.getKey();
List<Label> labels = entry.getValue();
String begTimeStr = timeFormat.format(new Date(labels.get(0).getBegTime()));
String endTimeStr = timeFormat.format(new Date(labels.get(labels.size() - 1).getEndTime()));
String path = FileUtil.combine(destDir.getAbsolutePath(), dateStr,
"label-" + begTimeStr + "-" + endTimeStr + ".json");
DataSource.saveLabels(labels, new File(path));
}
}
private String getLabelDate(Label label) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(label.getEndTime());
calendar.set(Calendar.HOUR_OF_DAY, 6);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long begTime = calendar.getTimeInMillis();
long endTime = begTime + ONE_DAY;
if (begTime <= label.getBegTime() && label.getEndTime() <= endTime) {
return new SimpleDateFormat("yyyyMMdd").format(new Date(begTime));
} else {
return null;
}
}
public static class AnswerModel {
private String title;
private int answer;
private int optionScore;
public AnswerModel(String title, int answer, int optionScore) {
this.title = title;
this.answer = answer;
this.optionScore = optionScore;
}
public String getTitle() {
return title;
}
public int getAnswer() {
return answer;
}
public int getOptionScore() {
return optionScore;
}
@Override
public String toString() {
return String.format("{title:%s,answer:%d,optionScore:%d}", title, answer, optionScore);
}
}
}

View File

@ -0,0 +1,223 @@
package edu.nju.ics.frontier.parse;
import edu.nju.ics.frontier.bean.intellij.FileEvent;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.intellij.ProjectEvent;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.CollectionUtil;
import edu.nju.ics.frontier.util.Constants;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.*;
public class ParseMain {
public static void main(String[] args) {
cleanIntelliJEvent();
parseIntelliJEvent();
parseLabel();
}
public static void cleanIntelliJEvent() {
String root = "C:\\Users\\zzw\\Desktop\\Research\\dataset\\flow_tracker_plugin\\fujitsu";
Executor.executeToDateDir(root, root, new IntelliJEventCleanTask());
}
public static void parseIntelliJEvent() {
String srcRoot = "C:\\Users\\zzw\\Desktop\\Research\\dataset\\flow_tracker_plugin\\fujitsu";
String destRoot = FileUtil.combine(Constants.FUJITSU_ROOT, "parse");
Executor.executeToDateDir(srcRoot, destRoot, new IntelliJEventParseTask());
}
public static void parseLabel() {
String srcRoot = "C:\\Users\\zzw\\Desktop\\Research\\dataset\\flow_tracker_plugin\\fujitsu";
String destRoot = FileUtil.combine(Constants.FUJITSU_ROOT, "parse");
Executor.executeToDateDir(srcRoot, destRoot, new LabelParseTask());
}
public static void enumerateProjects() {
String srcRoot = FileUtil.combine(Constants.FUJITSU_ROOT, "parse");
Executor.executeToUserDir(srcRoot, "", new Executor.Task() {
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
Set<String> projects = new HashSet<String>();
for (File dateDir : dateDirs) {
List<IntelliJEvent> events = ParseUtil.loadWorkdayEvents(dateDir);
if (events == null) {
continue;
}
for (IntelliJEvent event : events) {
if (!IntelliJEvent.isProjectEvent(event)) {
continue;
}
ProjectEvent projectEvent = IntelliJEvent.toProjectEvent(event);
CollectionUtil.updateSet(projects, projectEvent.getNameField());
}
}
System.out.printf("%s/%s/%d\n%s\n", teamDir.getName(), userDir.getName(), projects.size(), projects);
}
});
}
public static void enumerateFiles() {
String srcRoot = FileUtil.combine(Constants.FUJITSU_ROOT, "parse");
Executor.executeToUserDir(srcRoot, "", new Executor.Task() {
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
Set<String> globalFiles = new HashSet<String>();
Set<String> switchFiles = new HashSet<String>();
Set<String> selectFiles = new HashSet<String>();
List<Set<String>> fileSwitches = new ArrayList<Set<String>>();
for (File dateDir : dateDirs) {
List<IntelliJEvent> events = ParseUtil.loadWorkdayEvents(dateDir);
if (events == null) {
continue;
}
for (IntelliJEvent event : events) {
if (!IntelliJEvent.isFileEvent(event)) {
continue;
}
FileEvent fileEvent = IntelliJEvent.toFileEvent(event);
if (fileEvent.isWhenSelected()) {
String oldPath = fileEvent.getOldPathField();
String newPath = fileEvent.getNewPathField();
CollectionUtil.updateSet(globalFiles, oldPath);
CollectionUtil.updateSet(globalFiles, newPath);
CollectionUtil.updateSet(selectFiles, oldPath);
CollectionUtil.updateSet(selectFiles, newPath);
if (oldPath != null && newPath != null && (!oldPath.equals(newPath))) {
CollectionUtil.updateSet(switchFiles, oldPath);
CollectionUtil.updateSet(switchFiles, newPath);
Set<String> tmp = new HashSet<String>();
tmp.add(oldPath);
tmp.add(newPath);
fileSwitches.add(tmp);
}
} else if (fileEvent.isWhenChanged()) {
String path = fileEvent.getPathField();
CollectionUtil.updateSet(globalFiles, path);
}
}
}
System.out.printf("%s/%s\n", teamDir.getName(), userDir.getName());
// connected components
List<Set<String>> fileConnectedComponents = getConnectedComponents(fileSwitches);
System.out.println("connected components:");
Set<String> usedFiles = new HashSet<String>();
for (Set<String> fileConnectedComponent : fileConnectedComponents) {
for (String item : fileConnectedComponent) {
Assertion.assertTrue(usedFiles.add(item));
}
System.out.println(fileConnectedComponent);
}
// files selected but not switched
System.out.println("files selected but not switched:");
Set<String> selectButNotSwitch = new HashSet<String>();
for (String file : selectFiles) {
if (!usedFiles.contains(file)) {
selectButNotSwitch.add(file);
}
}
System.out.println(selectButNotSwitch);
// files changed but not selected
System.out.println("files changed but not selected:");
Set<String> changedButNotSelect = new HashSet<String>();
for (String file : globalFiles) {
if (!selectFiles.contains(file)) {
changedButNotSelect.add(file);
}
}
System.out.println(changedButNotSelect);
}
});
}
private static List<Set<String>> getConnectedComponents(List<Set<String>> fileSwitches) {
Map<String, Set<Integer>> clusterIdsForEachFile = getClusterIdsForEachFile(fileSwitches);
int[] conflictClusterIds = getConflictClusterIds(clusterIdsForEachFile);
while (conflictClusterIds != null) {
resolveClusterConflict(clusterIdsForEachFile, conflictClusterIds[1], conflictClusterIds[0]);
conflictClusterIds = getConflictClusterIds(clusterIdsForEachFile);
}
Map<Integer, Set<String>> fileClusters = new HashMap<Integer, Set<String>>();
for (Map.Entry<String, Set<Integer>> entry : clusterIdsForEachFile.entrySet()) {
String key = entry.getKey();
Object[] value = entry.getValue().toArray();
Assertion.assertTrue(value.length == 1);
CollectionUtil.updateSetMap(fileClusters, (Integer) value[0], key);
}
List<Set<String>> result = new ArrayList<Set<String>>();
for (Map.Entry<Integer, Set<String>> entry : fileClusters.entrySet()) {
result.add(entry.getValue());
}
return result.isEmpty() ? null : result;
}
private static Map<String, Set<Integer>> getClusterIdsForEachFile(List<Set<String>> fileSwitches) {
if (fileSwitches == null || fileSwitches.isEmpty()) {
return null;
}
Map<String, Set<Integer>> result = new HashMap<String, Set<Integer>>();
for (int i = 0, l = fileSwitches.size(); i < l; i++) {
Set<String> tmp = fileSwitches.get(i);
for (String item : tmp) {
CollectionUtil.updateSetMap(result, item, i);
}
}
return result.isEmpty() ? null : result;
}
private static int[] getConflictClusterIds(Map<String, Set<Integer>> clusterIdsForEachFile) {
if (clusterIdsForEachFile == null) {
return null;
}
for (Map.Entry<String, Set<Integer>> entry : clusterIdsForEachFile.entrySet()) {
Set<Integer> value = entry.getValue();
if (value.size() > 1) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int item : value) {
if (item < min) {
min = item;
}
if (item > max) {
max = item;
}
}
return new int[]{min, max};
}
}
return null;
}
private static void resolveClusterConflict(Map<String, Set<Integer>> clusterIdsForEachFile, int from, int to) {
if (clusterIdsForEachFile == null) {
return;
}
for (Map.Entry<String, Set<Integer>> entry : clusterIdsForEachFile.entrySet()) {
Set<Integer> value = entry.getValue();
if (value.contains(from)) {
value.remove(from);
value.add(to);
}
}
}
}

View File

@ -0,0 +1,42 @@
package edu.nju.ics.frontier.parse;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ParseUtil {
public static List<IntelliJEvent> loadWorkdayEvents(File dateDir) {
File[] eventFiles = FileUtil.listSubFiles(dateDir, "event", ".json");
if (eventFiles == null) {
return null;
}
List<IntelliJEvent> events = new ArrayList<IntelliJEvent>();
for (File eventFile : eventFiles) {
List<IntelliJEvent> tmp = DataSource.loadEvents(eventFile);
if (tmp != null) {
events.addAll(tmp);
}
}
return events.isEmpty() ? null : events;
}
public static List<Label> loadWorkdayLabels(File dateDir) {
File[] labelFiles = FileUtil.listSubFiles(dateDir, "label", ".json");
if (labelFiles == null) {
return null;
}
List<Label> labels = new ArrayList<Label>();
for (File labelFile : labelFiles) {
List<Label> tmp = DataSource.loadLabels(labelFile);
if (tmp != null) {
labels.addAll(tmp);
}
}
return labels.isEmpty() ? null : labels;
}
}

View File

@ -0,0 +1,69 @@
package edu.nju.ics.frontier.parse;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.Constants;
import edu.nju.ics.frontier.util.FileUtil;
import java.util.HashMap;
import java.util.Map;
public class ProjectFileMapping {
private static Map<String, Map<String, String>> file2Project4EachUser = new HashMap<String, Map<String, String>>();
static {
OkTextReader reader = new OkTextReader();
reader.open(FileUtil.combine(Constants.RESOURCE_ROOT, "file_2_project_4_each_user.txt"));
String line;
String user = null;
while ((line = reader.readLine()) != null) {
if (line.contains("/")) {
user = line.split("/")[1];
} else if (line.startsWith("project:")) {
Assertion.assertNotNull(user);
String project = line.substring(line.indexOf(":") + 1, line.indexOf(","));
String[] files = line.substring(line.indexOf("[") + 1, line.indexOf("]")).split(",");
Map<String, String> value = file2Project4EachUser.get(user);
if (value == null) {
value = new HashMap<String, String>();
}
for (String file : files) {
Assertion.assertNull(value.put(file, project));
}
file2Project4EachUser.put(user, value);
}
}
reader.close();
}
public static Map<String, Map<String, String>> getFile2Project4EachUser() {
return file2Project4EachUser;
}
public static Map<String, String> getFile2ProjectByUser(String user) {
if (user == null) {
return null;
}
return file2Project4EachUser.get(user);
}
public static String getProjectByFile4User(String user, String file) {
if (user == null || file == null) {
return null;
}
Map<String, String> file2Project = getFile2ProjectByUser(user);
return file2Project == null ? null : file2Project.get(file);
}
public static void printProjectFileMapping() {
for (Map.Entry<String, Map<String, String>> entry1 : file2Project4EachUser.entrySet()) {
String k1 = entry1.getKey();
Map<String, String> v1 = entry1.getValue();
System.out.printf("------------------------------------------------------------------\n%s\n", k1);
for (Map.Entry<String, String> entry2 : v1.entrySet()) {
System.out.printf("%s->%s\n", entry2.getKey(), entry2.getValue());
}
}
}
}

View File

@ -0,0 +1,103 @@
package edu.nju.ics.frontier.recognition.feature;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math3.stat.descriptive.rank.Max;
import org.apache.commons.math3.stat.descriptive.rank.Min;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class FeatureEngineeringTask implements Executor.Task {
private boolean mStandardize;
private boolean mNormalize;
private OkTextReader mReader;
private OkTextWriter mWriter;
public FeatureEngineeringTask(boolean standardize, boolean normalize) {
mStandardize = standardize;
mNormalize = normalize;
mReader = new OkTextReader();
mWriter = new OkTextWriter();
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File sampleFile = new File(FileUtil.combine(userDir.getAbsolutePath(), "samples.csv"));
if (!sampleFile.exists()) {
return;
}
// load
List<String[]> tmp = new ArrayList<String[]>();
mReader.open(sampleFile);
String line;
while ((line = mReader.readLine()) != null) {
tmp.add(line.split(","));
}
mReader.close();
// process
Object[][] table = new Object[tmp.size()][tmp.get(0).length];
int offset = 0;
// for team, user, date, sliceBegTime, sliceEndTime, sessionBegTime, sessionEndTime and sessionId
for (; offset < 8; offset++) {
for (int i = 0, m = tmp.size(); i < m; i++) {
table[i][offset] = tmp.get(i)[offset];
}
}
// standardization: for features
for (int n = tmp.get(0).length - 2; offset < n; offset++) {
double[] values = new double[tmp.size()];
for (int i = 0, m = values.length; i < m; i++) {
values[i] = Double.parseDouble(tmp.get(i)[offset]);
}
// standardization (Z-score)
if (mStandardize) {
double mean = new Mean().evaluate(values);
double sd = new StandardDeviation().evaluate(values);
for (int i = 0, m = values.length; i < m; i++) {
table[i][offset] = (sd == 0.0) ? 0.0 : (values[i] - mean) / sd;
}
continue;
}
// normalization (0, 1)
if (mNormalize) {
double min = new Min().evaluate(values);
double max = new Max().evaluate(values);
double range = max - min;
for (int i = 0, m = values.length; i < m; i++) {
table[i][offset] = (range == 0.0) ? 0.0 : (values[i] - min) / range;
Assertion.assertNotNegative((Double) table[i][offset]);
}
continue;
}
for (int i = 0, m = values.length; i < m; i++) {
table[i][offset] = values[i];
}
}
// for engagement and productivity labels
for (int n = tmp.get(0).length; offset < n; offset++) {
for (int i = 0, m = tmp.size(); i < m; i++) {
table[i][offset] = Integer.parseInt(tmp.get(i)[offset]);
}
}
// restore
mWriter.open(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName(), "instances.csv"));
for (Object[] row : table) {
for (int j = 0, n = row.length - 1; j < n; j++) {
mWriter.print(row[j] + ",");
}
mWriter.println(row[row.length - 1]);
}
mWriter.close();
System.out.printf("%s/%s -> %d instances\n", teamDir.getName(), userDir.getName(), table.length);
}
}

View File

@ -0,0 +1,819 @@
package edu.nju.ics.frontier.recognition.feature;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import edu.nju.ics.frontier.bean.intellij.*;
import edu.nju.ics.frontier.bean.label.SelectOrSubmitAction;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.recognition.segment.Slice;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.*;
public class FeatureExtractionTask implements Executor.Task {
private Gson mGson;
private OkTextWriter mWriter;
public FeatureExtractionTask() {
mGson = DataSource.getGson();
mWriter = new OkTextWriter();
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
FileUtil.delete(new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName())));
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
int sampleCount = 0;
File sampleFile = new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName(), "samples.csv"));
mWriter.open(sampleFile);
for (File dateDir : dateDirs) {
File sliceFile = new File(FileUtil.combine(dateDir.getAbsolutePath(), "slices.json"));
if (!sliceFile.exists()) {
continue;
}
List<Slice> slices = FileUtil.fromJson(mGson, new TypeToken<ArrayList<Slice>>(){}.getType(), sliceFile);
if (slices == null || slices.isEmpty()) {
continue;
}
for (Slice slice : slices) {
Object[] sample = createSample(slice);
StringBuilder builder = new StringBuilder();
for (Object value : sample) {
builder.append(value).append(",");
}
String row = builder.toString();
row = row.substring(0, row.length() - 1);
mWriter.println(row);
sampleCount++;
}
}
mWriter.close();
System.out.printf("%s/%s -> %d samples\n", teamDir.getName(), userDir.getName(), sampleCount);
}
private Object[] createSample(Slice slice) {
String team = slice.getTeam();
String user = slice.getUser();
String date = slice.getDate();
long sliceBegTime = slice.getSliceBegTime();
long sliceEndTime = slice.getSliceEndTime();
long sessionBegTime = slice.getSessionBegTime();
long sessionEndTime = slice.getSessionEndTime();
int sessionId = slice.getSessionId();
List<IntelliJEvent> events = slice.getEvents();
Label label = slice.getLabel();
long begTime = sliceBegTime;
long endTime = sliceEndTime;
if (sliceBegTime < sessionBegTime) {
begTime = sessionBegTime;
endTime = begTime + (sliceEndTime - sliceBegTime);
}
if (sliceEndTime > sessionEndTime) {
endTime = sessionEndTime;
begTime = endTime - (sliceEndTime - sliceBegTime);
}
Assertion.assertTrue(sessionBegTime <= begTime && endTime <= sessionEndTime);
// TODO: feature extraction
// for team, user, date, sliceBegTime, sliceEndTime, sessionBegTime, sessionEndTime,
// sessionId, engagement label, and productivity label
int featureCount = 10;
double[] part1 = aggregateAppEvents(events, begTime, endTime);
featureCount += part1.length;
double[] part2 = aggregateWindowFileCursorEvents(events);
featureCount += part2.length;
double[] part3 = aggregateExecuteEvents(events);
featureCount += part3.length;
double[] part4 = aggregateActionEvents(events);
featureCount += part4.length;
double[] part5 = aggregateKeyMouseEvents(events, begTime, endTime);
featureCount += part5.length;
Object[] result = new Object[featureCount];
int offset = 0;
result[offset++] = team;
result[offset++] = user;
result[offset++] = date;
result[offset++] = sliceBegTime;
result[offset++] = sliceEndTime;
result[offset++] = sessionBegTime;
result[offset++] = sessionEndTime;
result[offset++] = sessionId;
offset += copyValues(result, part1, offset);
offset += copyValues(result, part2, offset);
offset += copyValues(result, part3, offset);
offset += copyValues(result, part4, offset);
offset += copyValues(result, part5, offset);
result[offset++] = label == null ? -1 : label.getLabelScore(SelectOrSubmitAction.Dimension.ENGAGEMENT);
result[offset] = label == null ? -1 : label.getLabelScore(SelectOrSubmitAction.Dimension.PRODUCTIVITY);
return result;
}
private double[] aggregateAppEvents(List<IntelliJEvent> events, long begTime, long endTime) {
List<AppEvent> appEvents = MeasureUtil.getAppEventSequence(events);
int[] enterAndExitIdeCount = MeasureUtil.getEnterAndExitIdeCount(appEvents);
long[] inAndOutsideIdeTime = MeasureUtil.getInAndOutsideIdeTime(appEvents, begTime, endTime);
long inactiveTime = MeasureUtil.getInactiveTimeInIDE(events, appEvents, begTime, endTime, 300000);
double oneMinute = 60 * 1000;
Assertion.assertEqual(inAndOutsideIdeTime[0] + inAndOutsideIdeTime[1], endTime - begTime);
long timeInterval = Math.round(safeDivision(endTime - begTime, oneMinute));
int enterIdeCount = enterAndExitIdeCount[0];
int exitIdeCount = enterAndExitIdeCount[1];
long inIdeTime = Math.round(safeDivision(inAndOutsideIdeTime[0], oneMinute));
long outsideIdeTime = timeInterval - inIdeTime;
long inIdeInactiveTime = Math.round(safeDivision(inactiveTime, oneMinute));
long inIdeActiveTime = inIdeTime - inIdeInactiveTime;
// ratios of time spent in and outside the IDE
double inIdeTimeRatio = safeDivision(inIdeTime, timeInterval);
double outsideIdeTimeRatio = safeDivision(outsideIdeTime, timeInterval);
// ratios of active time interval in the IDE related to duration in IDE
// and duration of slice, respectively
double inIdeActiveTimeRatio = safeDivision(inIdeActiveTime, inIdeTime);
double activeTimeRatio = safeDivision(inIdeActiveTime, timeInterval);
Assertion.assertTrue(activeTimeRatio <= inIdeActiveTimeRatio);
double inIdeInactiveTimeRatio = safeDivision(inIdeInactiveTime, inIdeTime);
double inactiveTimeRatio = safeDivision(outsideIdeTime + inIdeInactiveTime, timeInterval);
return new double[]{
// number of switches from outside to IDE
// Assertion.assertNotNegative(enterIdeCount),
// number of switches from IDE to outside
Assertion.assertNotNegative(exitIdeCount),
// minutes spent in the IDE
Assertion.assertNotNegative(inIdeTime),
// fraction of in-IDE time and total time
// Assertion.assertInClosedIntervalFrom0To1(inIdeTimeRatio),
// minutes spent outside the IDE
Assertion.assertNotNegative(outsideIdeTime),
// fraction of outside-IDE time and total time
// Assertion.assertInClosedIntervalFrom0To1(outsideIdeTimeRatio),
// active time spent in the IDE
Assertion.assertByCondition(inIdeActiveTime, 0 <= inIdeActiveTime && inIdeActiveTime <= inIdeTime),
// inactive time spent in the IDE
Assertion.assertByCondition(inIdeInactiveTime, 0 <= inIdeInactiveTime && inIdeInactiveTime <= inIdeTime),
// ratio of active time interval in the IDE related to duration of slice
// Assertion.assertInClosedIntervalFrom0To1(activeTimeRatio),
// ratio of active time interval in the IDE related to duration in IDE
// Assertion.assertInClosedIntervalFrom0To1(inIdeActiveTimeRatio),
// ratio of the sum of inactive time interval in the IDE and outside the IDE related to duration of slice
// Assertion.assertInClosedIntervalFrom0To1(inactiveTimeRatio),
// ratio of inactive time interval in the IDE related to duration in IDE
// Assertion.assertInClosedIntervalFrom0To1(inIdeInactiveTimeRatio),
};
}
/**
* in a comprehensible way, we define several concept:
* <ul>
* <li><strong>project switching sequence</strong>: a sequence consists of N times
* project switches, denotes as project_1, project_2, ..., project_N and project_i
* is not equal to project_{i+1}. There are k projects appear in the sequence, we
* assume project i appears n_i times, so \Sigma_{i=1}^k n_i = N.</li>
* <li><strong>file switching sequence</strong>: a sequence consists of N times file
* switches, denotes as file_1, file_2, ..., file_N and file_i is not equal to
* file_{i+1}. There are k files appear in the sequence, we assume file i appears n_i
* times, so \Sigma_{i=1}^k n_i = N.</li>
* <li><strong>file modifying sequence</strong>: a sequence consists of N times file
* modifies, denotes as file_1, file_2, ..., file_N and file_i is not equal to
* file_{i+1}. There are k files appear in the sequence, we assume file i appears n_i
* times, so \Sigma_{i=1}^k n_i = N.</li>
* <li><strong>file modifying history</strong>: given a file i, the modifying history
* is a sequence of line of code.</li>
* <li><strong>cursor changing history</strong>: given a file i, the changing history
* is a sequence of line of cursor.</li>
* </ul>
* @param events
* @return
*/
private double[] aggregateWindowFileCursorEvents(List<IntelliJEvent> events) {
List<IntelliJEvent> windowFileCursorEvents = MeasureUtil.getWindowFileCursorEventSequence(events);
List<String> projectSwitchingSequence = new ArrayList<String>();
List<String> fileSwitchingSequence = new ArrayList<String>();
List<String> fileModifyingSequence = new ArrayList<String>();
Map<String, List<long[]>> fileModifyingHistory = new HashMap<String, List<long[]>>();
Map<String, List<long[]>> cursorChangingHistory = new HashMap<String, List<long[]>>();
Set<String> browsedProjects = new HashSet<String>();
Set<String> modifiedProjects = new HashSet<String>();
Set<String> browsedFiles = new HashSet<String>();
Set<String> modifiedFiles = new HashSet<String>();
if (windowFileCursorEvents != null) {
for (IntelliJEvent event : windowFileCursorEvents) {
if (event instanceof WindowEvent) {
WindowEvent windowEvent = (WindowEvent) event;
Assertion.assertTrue(windowEvent.isWhenWindowActivated());
String thisName = windowEvent.getThisWindowName();
Assertion.assertNotNull(thisName);
Assertion.assertTrue(thisName.startsWith("frame"));
String oppoName = windowEvent.getOppositeWindowName();
Assertion.assertTrue(oppoName == null || oppoName.startsWith("frame"));
updateSequence(projectSwitchingSequence, thisName);
browsedProjects.add(thisName);
} else if (event instanceof FileEvent) {
FileEvent fileEvent = (FileEvent) event;
Assertion.assertTrue(fileEvent.isWhenSelected() || fileEvent.isWhenChanged());
if (fileEvent.isWhenSelected()) {
String newPath = fileEvent.getNewPathField();
Assertion.assertNotNull(newPath);
updateSequence(fileSwitchingSequence, newPath);
String frame = fileEvent.getExtraData("frame");
if (frame != null) {
browsedProjects.add(frame);
}
browsedFiles.add(newPath);
} else {
String path = fileEvent.getPathField();
Assertion.assertNotNull(path);
updateSequence(fileModifyingSequence, path);
MeasureUtil.updateListMap(fileModifyingHistory, path,
new long[]{fileEvent.getTime(), Long.parseLong(fileEvent.getLineField())});
String frame = fileEvent.getExtraData("frame");
if (frame != null) {
browsedProjects.add(frame);
modifiedProjects.add(frame);
}
browsedFiles.add(path);
modifiedFiles.add(path);
}
} else {
CursorEvent cursorEvent = (CursorEvent) event;
Assertion.assertTrue(cursorEvent.isWhenCursorChanged() || cursorEvent.isWhenSelectChanged());
String file = cursorEvent.getExtraData("file");
if (file == null) {
file = "anonymous";
}
if (cursorEvent.isWhenCursorChanged()) {
Assertion.assertTrue(cursorEvent.isUntypedEditor());
long newLine = CursorEvent.getLineAndColumn(cursorEvent.getNewPosField())[0];
long oldLine = CursorEvent.getLineAndColumn(cursorEvent.getOldPosField())[0];
MeasureUtil.updateListMap(cursorChangingHistory, file,
new long[]{cursorEvent.getTime(), newLine, oldLine});
}
}
}
}
// count
int browsedProjectCount = browsedProjects.size(); // number of browsed projects
int modifiedProjectCount = modifiedProjects.size(); // number of modified projects
double projectModifiedRatio = safeDivision(modifiedProjectCount, browsedProjectCount);
int browsedFileCount = browsedFiles.size(); // number of browsed files
int modifiedFileCount = modifiedFiles.size(); // number of modified files
double fileModifiedRatio = safeDivision(modifiedFileCount, browsedFileCount);
// focus switches
int projectSwitchingCount = projectSwitchingSequence.size();
int fileSwitchingCount = fileSwitchingSequence.size();
int fileModifyingCount = fileModifyingSequence.size();
double averageProjectSwitchingCount = safeDivision(projectSwitchingCount, new HashSet<String>(projectSwitchingSequence).size());
double averageFileSwitchingCount = safeDivision(fileSwitchingCount, new HashSet<String>(fileSwitchingSequence).size());
double averageFileModifyingCount = safeDivision(fileModifyingCount, new HashSet<String>(fileModifyingSequence).size());
double projectSwitchingEntropy = MeasureUtil.shannonEntropy(projectSwitchingSequence);
double fileSwitchingEntropy = MeasureUtil.shannonEntropy(fileSwitchingSequence);
double fileModifyingEntropy = MeasureUtil.shannonEntropy(fileModifyingSequence);
// region of interest & walking distance
Map<String, Long> regionOfInterest = new HashMap<String, Long>();
Map<String, Long> walkingDistance = new HashMap<String, Long>();
for (Map.Entry<String, List<long[]>> entry : cursorChangingHistory.entrySet()) {
List<long[]> positions = entry.getValue();
MeasureUtil.removeOutliers(positions);
if (positions.isEmpty()) {
continue;
}
long prevLine = positions.get(0)[1];
long top = prevLine;
long bottom = prevLine;
long distance = 0;
for (int i = 1, l = positions.size(); i < l; i++) {
long nextLine = positions.get(i)[1];
if (nextLine < top) {
top = nextLine;
}
if (nextLine > bottom) {
bottom = nextLine;
}
distance += Math.abs(nextLine - prevLine);
prevLine = nextLine;
}
long region = bottom - top;
Assertion.assertTrue(region >= 0);
Assertion.assertTrue(region <= distance);
if (region > 0) {
regionOfInterest.put(entry.getKey(), region);
walkingDistance.put(entry.getKey(), distance);
}
}
double regionOfInterestSum = MeasureUtil.countMap(regionOfInterest);
double regionOfInterestPerFile = safeDivision(regionOfInterestSum, regionOfInterest.size());
double walkingDistanceSum = MeasureUtil.countMap(walkingDistance);
double walkingDistancePerFile = safeDivision(walkingDistanceSum, walkingDistance.size());
double regionOfInterestEntropy = MeasureUtil.shannonEntropy(regionOfInterest, regionOfInterestSum);
double walkingDistanceEntropy = MeasureUtil.shannonEntropy(walkingDistance, walkingDistanceSum);
double duplicateRatio = safeDivision(walkingDistanceSum, regionOfInterestSum);
// line of code
Map<String, long[]> lineOfCode = new HashMap<String, long[]>();
for (Map.Entry<String, List<long[]>> entry : fileModifyingHistory.entrySet()) {
long addedLoc = 0, deletedLoc = 0;
List<long[]> lines = entry.getValue();
long prevLine = lines.get(0)[1];
for (int i = 1, l = lines.size(); i < l; i++) {
long nextLine = lines.get(i)[1];
long deltaLine = nextLine - prevLine;
if (deltaLine > 0) {
addedLoc += deltaLine;
} else if (deltaLine < 0) {
deletedLoc -= deltaLine;
}
prevLine = nextLine;
}
Assertion.assertNotNegative(addedLoc);
Assertion.assertNotNegative(deletedLoc);
if (addedLoc > 0 || deletedLoc > 0) {
lineOfCode.put(entry.getKey(), new long[]{addedLoc, deletedLoc});
}
}
double addedLocSum = 0;
double deletedLocSum = 0;
for (Map.Entry<String, long[]> entry : lineOfCode.entrySet()) {
long[] value = entry.getValue();
addedLocSum += value[0];
deletedLocSum += value[1];
}
double addedOrDeletedLocSum = addedLocSum + deletedLocSum;
double locGainSum = addedLocSum - deletedLocSum;
double addedLocPerFile = safeDivision(addedLocSum, lineOfCode.size());
double deletedLocPerFile = safeDivision(deletedLocSum, lineOfCode.size());
double addedOrDeletedLocPerFile = safeDivision(addedOrDeletedLocSum, lineOfCode.size());
double locGainPerFile = safeDivision(locGainSum, lineOfCode.size());
return new double[]{
// number of browsed projects
Assertion.assertNotNegative(browsedProjectCount),
// number of modified projects
Assertion.assertNotNegative(modifiedProjectCount),
// ratio of modified related to browsed projects
// Assertion.assertInClosedIntervalFrom0To1(projectModifiedRatio),
// number of browsed files
Assertion.assertNotNegative(browsedFileCount),
// number of modified files
Assertion.assertNotNegative(modifiedFileCount),
// ratio of modified related to browsed files
// Assertion.assertInClosedIntervalFrom0To1(fileModifiedRatio),
// number of project switching
Assertion.assertNotNegative(projectSwitchingCount),
// number of file switching
Assertion.assertNotNegative(fileSwitchingCount),
// number of file modifying
Assertion.assertNotNegative(fileModifyingCount),
// average number of project switching
// Assertion.assertNotNegative(averageProjectSwitchingCount),
// average number of file switching
// Assertion.assertNotNegative(averageFileSwitchingCount),
// average number of file modifying
// Assertion.assertNotNegative(averageFileModifyingCount),
// entropy of focus switches at project level
Assertion.assertNotNegative(projectSwitchingEntropy),
// entropy of focus switches at browsed file level
Assertion.assertNotNegative(fileSwitchingEntropy),
// entropy of focus switches at modified file level
Assertion.assertNotNegative(fileModifyingEntropy),
// sum of region of interest of each file
Assertion.assertNotNegative(regionOfInterestSum),
// region of interest per file
// Assertion.assertNotNegative(regionOfInterestPerFile),
// sum of walking distance of each file
Assertion.assertByCondition(walkingDistanceSum, walkingDistanceSum >= regionOfInterestSum),
// walking distance per file
// Assertion.assertByCondition(walkingDistancePerFile, walkingDistancePerFile >= regionOfInterestPerFile),
// entropy of region of interest
// Assertion.assertNotNegative(regionOfInterestEntropy),
// entropy of walking distance
// Assertion.assertNotNegative(walkingDistanceEntropy),
// sum of walking distance divided by sum of region of interest
Assertion.assertByCondition(duplicateRatio, duplicateRatio == 0 || duplicateRatio >= 1),
// sum of added line of code
Assertion.assertNotNegative(addedLocSum),
// sum of deleted line of code
Assertion.assertNotNegative(deletedLocSum),
// gain of line of code
Assertion.assertNotNaN(locGainSum),
// sum of added/deleted line of code
Assertion.assertNotNegative(addedOrDeletedLocSum),
// added line of code per file
// Assertion.assertNotNegative(addedLocPerFile),
// deleted line of code per file
// Assertion.assertNotNegative(deletedLocPerFile),
// gain of line of code per file
// Assertion.assertNotNaN(locGainPerFile),
// added/deleted line of code per file
// Assertion.assertNotNegative(addedOrDeletedLocPerFile),
};
}
private double[] aggregateExecuteEvents(List<IntelliJEvent> events) {
int scheduledRunCount = 0;
int cancelledRunCount = 0;
int scheduledDebugCount = 0;
int cancelledDebugCount = 0;
int scheduledCoverageCount = 0;
int cancelledCoverageCount = 0;
if (!(events == null || events.isEmpty())) {
for (IntelliJEvent event : events) {
if (!IntelliJEvent.isExecuteEvent(event)) {
continue;
}
ExecuteEvent executeEvent = IntelliJEvent.toExecuteEvent(event);
if (executeEvent.isWhenScheduled()) {
String id = executeEvent.getIdField();
if ("Run".equals(id)) {
scheduledRunCount++;
} else if ("Debug".equals(id)) {
scheduledDebugCount++;
} else if ("Coverage".equals(id)) {
scheduledCoverageCount++;
} else {
throw new IllegalArgumentException(executeEvent.toString());
}
} else if (executeEvent.isWhenCancelled()) {
String id = executeEvent.getIdField();
if ("Run".equals(id)) {
cancelledRunCount++;
} else if ("Debug".equals(id)) {
cancelledDebugCount++;
} else if ("Coverage".equals(id)) {
cancelledCoverageCount++;
} else {
throw new IllegalArgumentException(executeEvent.toString());
}
}
}
}
Assertion.assertNotNegative(scheduledRunCount);
Assertion.assertNotNegative(scheduledDebugCount);
Assertion.assertNotNegative(scheduledCoverageCount);
Assertion.assertNotNegative(cancelledRunCount);
Assertion.assertNotNegative(cancelledDebugCount);
Assertion.assertNotNegative(cancelledCoverageCount);
int scheduledExecuteCount = scheduledRunCount + scheduledDebugCount + scheduledCoverageCount;
int cancelledExecuteCount = cancelledRunCount + cancelledDebugCount + cancelledCoverageCount;
return new double[]{
// number of all terminated running
// Math.max(0, scheduledRunCount - cancelledRunCount),
// number of all cancelled running
// cancelledRunCount,
// number of all terminated debugging
// Math.max(0, scheduledDebugCount - cancelledDebugCount),
// number of all cancelled debugging
// cancelledDebugCount,
// number of all terminated coverage
// Math.max(0, scheduledCoverageCount - cancelledCoverageCount),
// number of all cancelled coverage
// cancelledCoverageCount,
// number of all terminated executing
// Math.max(0, scheduledExecuteCount - cancelledExecuteCount),
// number of all cancelled executing
// cancelledExecuteCount,
};
}
private double[] aggregateActionEvents(List<IntelliJEvent> events) {
int editActionCount = 0, codeActionCount = 0, refactorActionCount = 0;
int findActionCount = 0, navigateActionCount = 0, debugActionCount = 0;
int buildActionCount = 0, runActionCount = 0, testActionCount = 0, analyzeActionCount = 0;
int fileActionCount = 0, vcsActionCount = 0, windowActionCount = 0;
int otherActionCount = 0, actionCount = 0;
if (!(events == null || events.isEmpty())) {
for (IntelliJEvent event : events) {
if (!IntelliJEvent.isActionEvent(event)) {
continue;
}
ActionEvent actionEvent = IntelliJEvent.toActionEvent(event);
ActionCategory.Category category = ActionCategory.getCategory(actionEvent);
if (category == null) {
otherActionCount++;
} else {
switch (category) {
case Edit:
editActionCount++;
break;
case Code:
codeActionCount++;
break;
case Refactor:
refactorActionCount++;
break;
case Find:
findActionCount++;
break;
case Navigate:
navigateActionCount++;
break;
case Debug:
debugActionCount++;
break;
case Build:
buildActionCount++;
break;
case Run:
runActionCount++;
break;
case Test:
testActionCount++;
break;
case Analyze:
analyzeActionCount++;
break;
case File:
fileActionCount++;
break;
case VCS:
vcsActionCount++;
break;
case Window:
windowActionCount++;
break;
default:
break;
}
}
actionCount++;
}
}
Assertion.assertEqual(actionCount, editActionCount + codeActionCount + refactorActionCount +
findActionCount + navigateActionCount + debugActionCount +
buildActionCount + runActionCount + testActionCount + analyzeActionCount +
fileActionCount + vcsActionCount + windowActionCount + otherActionCount);
int editingActionCount = editActionCount + codeActionCount + refactorActionCount;
int understandingActionCount = findActionCount + navigateActionCount + debugActionCount;
int executingActionCount = buildActionCount + runActionCount + testActionCount + analyzeActionCount;
int generalActionCount = fileActionCount + vcsActionCount + windowActionCount;
Assertion.assertEqual(actionCount, editingActionCount + understandingActionCount +
executingActionCount + generalActionCount + otherActionCount);
double editActionRatio = safeDivision(editActionCount, actionCount);
double codeActionRatio = safeDivision(codeActionCount, actionCount);
double refactorActionRatio = safeDivision(refactorActionCount, actionCount);
double findActionRatio = safeDivision(findActionCount, actionCount);
double navigateActionRatio = safeDivision(navigateActionCount, actionCount);
double debugActionRatio = safeDivision(debugActionCount, actionCount);
double buildActionRatio = safeDivision(buildActionCount, actionCount);
double runActionRatio = safeDivision(runActionCount, actionCount);
double testActionRatio = safeDivision(testActionCount, actionCount);
double analyzeActionRatio = safeDivision(analyzeActionCount, actionCount);
double fileActionRatio = safeDivision(fileActionCount, actionCount);
double vcsActionRatio = safeDivision(vcsActionCount, actionCount);
double windowActionRatio = safeDivision(windowActionCount, actionCount);
double otherActionRatio = safeDivision(otherActionCount, actionCount);
double editingActionRatio = safeDivision(editingActionCount, actionCount);
double understandingActionRatio = safeDivision(understandingActionCount, actionCount);
double executingActionRatio = safeDivision(executingActionCount, actionCount);
double generalActionRatio = safeDivision(generalActionCount, actionCount);
return new double[]{
Assertion.assertNotNegative(actionCount),
Assertion.assertNotNegative(editActionCount),
Assertion.assertNotNegative(codeActionCount),
Assertion.assertNotNegative(refactorActionCount),
Assertion.assertNotNegative(findActionCount),
Assertion.assertNotNegative(navigateActionCount),
Assertion.assertNotNegative(debugActionCount),
Assertion.assertNotNegative(buildActionCount),
Assertion.assertNotNegative(runActionCount),
Assertion.assertNotNegative(testActionCount),
Assertion.assertNotNegative(analyzeActionCount),
Assertion.assertNotNegative(fileActionCount),
Assertion.assertNotNegative(vcsActionCount),
Assertion.assertNotNegative(windowActionCount),
Assertion.assertNotNegative(otherActionCount),
Assertion.assertNotNegative(editingActionCount),
Assertion.assertNotNegative(understandingActionCount),
Assertion.assertNotNegative(executingActionCount),
Assertion.assertNotNegative(generalActionCount),
// Assertion.assertInClosedIntervalFrom0To1(editActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(codeActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(refactorActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(findActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(navigateActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(debugActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(buildActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(runActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(testActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(analyzeActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(fileActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(vcsActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(windowActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(otherActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(editingActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(understandingActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(executingActionRatio),
// Assertion.assertInClosedIntervalFrom0To1(generalActionRatio),
};
}
private double[] aggregateKeyMouseEvents(List<IntelliJEvent> events, long begTime, long endTime) {
int letterKeyCount = 0, numberKeyCount = 0, punctuationKeyCount = 0;
int enterKeyCount = 0, backspaceAndDeleteKeyCount = 0, escapeKeyCount = 0;
int directionKeyCount = 0, functionKeyCount = 0, otherKeyCount = 0;
int keyTypeCount = 0;
int mouseClickCount = 0, mouseDragCount = 0;
int mouseWheelCount = 0, mouseMoveCount = 0;
int mouseEnterCount = 0, mouseExitCount = 0;
MouseEvent prevMouseEvent = null;
List<MouseEvent> mouseMovingHistory = new ArrayList<MouseEvent>();
if (!(events == null || events.isEmpty())) {
for (IntelliJEvent event : events) {
if (IntelliJEvent.isKeyEvent(event)) {
KeyEvent keyEvent = IntelliJEvent.toKeyEvent(event);
if (keyEvent.isWhenKeyPressed() && keyEvent.getTextField() != null) {
keyTypeCount++;
if (keyEvent.isPureOrShiftLetter()) {
letterKeyCount++;
} else if (keyEvent.isPureOrShiftNumber()) {
numberKeyCount++;
} else if (keyEvent.isPureOrShiftPunctuation()) {
punctuationKeyCount++;
} else if (keyEvent.isEnter()) {
enterKeyCount++;
} else if (keyEvent.isBackspace() || keyEvent.isDelete()) {
backspaceAndDeleteKeyCount++;
} else if (keyEvent.isEscape()) {
escapeKeyCount++;
} else if (keyEvent.isDirection()) {
directionKeyCount++;
} else if (keyEvent.isFunction()) {
functionKeyCount++;
} else {
otherKeyCount++;
}
}
} else if (IntelliJEvent.isMouseEvent(event)) {
MouseEvent mouseEvent = IntelliJEvent.toMouseEvent(event);
if (mouseEvent.isWhenMouseClicked()) {
mouseClickCount++;
} else if (mouseEvent.isWhenMouseDragged()) {
if (prevMouseEvent != null && (!prevMouseEvent.isWhenMouseDragged())) {
mouseDragCount++;
}
} else if (mouseEvent.isWhenMouseMoved()) {
if (prevMouseEvent != null && (!prevMouseEvent.isWhenMouseMoved())) {
mouseMoveCount++;
}
mouseMovingHistory.add(mouseEvent);
} else if (mouseEvent.isWhenMouseWheel()) {
if (prevMouseEvent != null && (!prevMouseEvent.isWhenMouseWheel())) {
mouseWheelCount++;
}
} else if (mouseEvent.isWhenMouseEntered()) {
mouseEnterCount++;
} else if (mouseEvent.isWhenMouseExited()) {
mouseExitCount++;
}
prevMouseEvent = mouseEvent;
}
}
}
Assertion.assertTrue(keyTypeCount == letterKeyCount + numberKeyCount + punctuationKeyCount +
enterKeyCount + backspaceAndDeleteKeyCount + escapeKeyCount +
directionKeyCount + functionKeyCount + otherKeyCount);
int oneMinute = 60 * 1000;
double letterKeyRatio = safeDivision(letterKeyCount, keyTypeCount);
double numberKeyRatio = safeDivision(numberKeyCount, keyTypeCount);
double punctuationKeyRatio = safeDivision(punctuationKeyCount, keyTypeCount);
double enterKeyRatio = safeDivision(enterKeyCount, keyTypeCount);
double backspaceAndDeleteKeyRatio = safeDivision(backspaceAndDeleteKeyCount, keyTypeCount);
double escapeKeyRatio = safeDivision(escapeKeyCount, keyTypeCount);
double directionKeyRatio = safeDivision(directionKeyCount, keyTypeCount);
double functionKeyRatio = safeDivision(functionKeyCount, keyTypeCount);
double otherKeyRatio = safeDivision(otherKeyCount, keyTypeCount);
double keyTypeSpeed = safeDivision(keyTypeCount, safeDivision(endTime - begTime, oneMinute));
double mouseMoveDistance = 0;
if (!mouseMovingHistory.isEmpty()) {
int[] prevPoint = mouseMovingHistory.get(0).getAbsoluteCoordinate();
for (int i = 1, l = mouseMovingHistory.size(); i < l; i++) {
int[] nextPoint = mouseMovingHistory.get(i).getAbsoluteCoordinate();
double deltaX = nextPoint[0] - prevPoint[0];
double deltaY = nextPoint[1] - prevPoint[1];
mouseMoveDistance += Math.sqrt(deltaX * deltaX + deltaY * deltaY);
prevPoint = nextPoint;
}
}
double mouseMoveSpeed = safeDivision(mouseMoveDistance, safeDivision(endTime - begTime, oneMinute));
return new double[]{
// #(keystrokes)
Assertion.assertNotNegative(keyTypeCount),
// #(letter)
Assertion.assertNotNegative(letterKeyCount),
// #(number)
Assertion.assertNotNegative(numberKeyCount),
// #(punctuation)
Assertion.assertNotNegative(punctuationKeyCount),
// #(enter)
Assertion.assertNotNegative(enterKeyCount),
// #(backspace & delete)
Assertion.assertNotNegative(backspaceAndDeleteKeyCount),
// #(escape)
Assertion.assertNotNegative(escapeKeyCount),
// #(Up & Down & Left & Right & Home & End & PgUp & PgDn)
Assertion.assertNotNegative(directionKeyCount),
// #(Fn family)
Assertion.assertNotNegative(functionKeyCount),
// #(others)
Assertion.assertNotNegative(otherKeyCount),
// #(letter) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(letterKeyRatio),
// #(number) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(numberKeyRatio),
// #(punctuation) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(punctuationKeyRatio),
// #(enter) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(enterKeyRatio),
// #(backspace & delete) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(backspaceAndDeleteKeyRatio),
// #(escape) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(escapeKeyRatio),
// #(Up & Down & Left & Right & Home & End & PgUp & PgDn) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(directionKeyRatio),
// #(Fn family) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(functionKeyRatio),
// #(others) / #(keystrokes)
// Assertion.assertInClosedIntervalFrom0To1(otherKeyRatio),
// #(keystrokes) per minute
// Assertion.assertNotNegative(keyTypeSpeed),
// #(mouse clicked)
Assertion.assertNotNegative(mouseClickCount),
// #(mouse dragged)
Assertion.assertNotNegative(mouseDragCount),
// #(mouse moved)
Assertion.assertNotNegative(mouseMoveCount),
// #(mouse wheel)
Assertion.assertNotNegative(mouseWheelCount),
// #(mouse entered)
// Assertion.assertNotNegative(mouseEnterCount),
// #(mouse exited)
// Assertion.assertNotNegative(mouseExitCount),
// distance of mouse moved per minute
Assertion.assertNotNegative(mouseMoveSpeed),
};
}
private int copyValues(Object[] dest, double[] src, int offset) {
for (int i = 0, l = src.length; i < l; i++) {
dest[i + offset] = src[i];
}
return src.length;
}
private void updateSequence(List<String> list, String item) {
if (list.isEmpty()) {
list.add(item);
} else if (!list.get(list.size() - 1).equals(item)) {
list.add(item);
}
}
private double safeDivision(double numerator, double denominator) {
return denominator == 0 ? 0 : numerator / denominator;
}
}

View File

@ -0,0 +1,216 @@
package edu.nju.ics.frontier.recognition.feature;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.common.io.OkTextWriter;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Constants;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
public class FeatureMain {
public static void main(String[] args) {
String prefix = "time_aligned_";
String srcRoot1 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", prefix + "slice");
String destRoot1 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", prefix + "sample");
Executor.executeToUserDir(srcRoot1, destRoot1, new FeatureExtractionTask());
String srcRoot2 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", prefix + "sample");
String destRoot2 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", prefix + "instance");
Executor.executeToUserDir(srcRoot2, destRoot2, new FeatureEngineeringTask(
Configuration.STANDARDIZE, Configuration.NORMALIZE));
String srcRoot3 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", prefix + "instance");
String destRoot3 = "C:\\Users\\zzw\\Desktop\\fujitsu\\" + prefix + "instances.csv";
mergeInstances(srcRoot3, destRoot3);
}
private static void mergeInstances(String srcRoot, String destPath) {
File destFile = new File(destPath);
FileUtil.delete(destFile);
OkTextReader reader = new OkTextReader();
OkTextWriter writer = new OkTextWriter();
File[] teamDirs = FileUtil.listSubDirectories(new File(srcRoot));
if (teamDirs == null) {
return;
}
writer.open(destFile);
int instanceCount = 0;
// write header
for (int i = 0, l = HEADER.length - 1; i < l; i++) {
writer.print(HEADER[i] + ",");
}
writer.println(HEADER[HEADER.length - 1]);
for (File teamDir : teamDirs) {
File[] userDirs = FileUtil.listSubDirectories(teamDir);
if (userDirs == null) {
continue;
}
for (File userDir : userDirs) {
File instanceFile = new File(FileUtil.combine(userDir.getAbsolutePath(), "instances.csv"));
if (!instanceFile.exists()) {
continue;
}
reader.open(instanceFile);
String line;
while ((line = reader.readLine()) != null) {
writer.println(line);
instanceCount++;
}
reader.close();
}
}
writer.close();
if (instanceCount == 0) {
FileUtil.delete(destFile);
System.out.println("no instance found.");
} else {
System.out.printf("%d instances are merged into %s.\n", instanceCount, destFile);
}
}
public static final String[] HEADER = new String[]{
// id & time
"team",
"user",
"date",
"sliceBegTime",
"sliceEndTime",
"sessionBegTime",
"sessionEndTime",
"sessionId",
// app features
// "enterIdeCount",
"exitIdeCount",
"inIdeTime",
// "inIdeTimeRatio",
"outsideIdeTime",
// "outsideIdeTimeRatio",
"inIdeActiveTime",
"inIdeInactiveTime",
// "activeTimeRatio",
// "inIdeActiveTimeRatio",
// "inactiveTimeRatio",
// "inIdeInactiveTimeRatio",
// window & file & cursor features
"browsedProjectCount",
"modifiedProjectCount",
// "projectModifiedRatio",
"browsedFileCount",
"modifiedFileCount",
// "fileModifiedRatio",
"projectSwitchingCount",
"fileSwitchingCount",
"fileModifyingCount",
// "averageProjectSwitchingCount",
// "averageFileSwitchingCount",
// "averageFileModifyingCount",
"projectSwitchingEntropy",
"fileSwitchingEntropy",
"fileModifyingEntropy",
"regionOfInterestSum",
// "regionOfInterestPerFile",
"walkingDistanceSum",
// "walkingDistancePerFile",
// "regionOfInterestEntropy",
// "walkingDistanceEntropy",
"duplicateRatio",
"addedLocSum",
"deletedLocSum",
"locGainSum",
"addedOrDeletedLocSum",
// "addedLocPerFile",
// "deletedLocPerFile",
// "locGainPerFile",
// "addedOrDeletedLocPerFile",
// execute features
// "scheduledRunCount",
// "cancelledRunCount",
// "scheduledDebugCount",
// "cancelledDebugCount",
// "scheduledCoverageCount",
// "cancelledCoverageCount",
// "scheduledExecuteCount",
// "cancelledExecuteCount",
// action features
"actionCount",
"editActionCount",
"codeActionCount",
"refactorActionCount",
"findActionCount",
"navigateActionCount",
"debugActionCount",
"buildActionCount",
"runActionCount",
"testActionCount",
"analyzeActionCount",
"fileActionCount",
"vcsActionCount",
"windowActionCount",
"otherActionCount",
"editingActionCount",
"understandingActionCount",
"executingActionCount",
"generalActionCount",
// "editActionRatio",
// "codeActionRatio",
// "refactorActionRatio",
// "findActionRatio",
// "navigateActionRatio",
// "debugActionRatio",
// "buildActionRatio",
// "runActionRatio",
// "testActionRatio",
// "analyzeActionRatio",
// "fileActionRatio",
// "vcsActionRatio",
// "windowActionRatio",
// "otherActionRatio",
// "editingActionRatio",
// "understandingActionRatio",
// "executingActionRatio",
// "generalActionRatio",
// key & mouse features
"keyTypeCount",
"letterKeyCount",
"numberKeyCount",
"punctuationKeyCount",
"enterKeyCount",
"backspaceAndDeleteKeyCount",
"escapeKeyCount",
"directionKeyCount",
"functionKeyCount",
"otherKeyCount",
// "letterKeyRatio",
// "numberKeyRatio",
// "punctuationKeyRatio",
// "enterKeyRatio",
// "backspaceAndDeleteKeyRatio",
// "escapeKeyRatio",
// "directionKeyRatio",
// "functionKeyRatio",
// "otherKeyRatio",
// "keyTypeSpeed",
"mouseClickCount",
"mouseDragCount",
"mouseMoveCount",
"mouseWheelCount",
// "mouseEnterCount",
// "mouseExitCount",
"mouseMoveSpeed",
// labels
"internalScore",
"externalScore",
};
}

View File

@ -0,0 +1,591 @@
package edu.nju.ics.frontier.recognition.feature;
import edu.nju.ics.frontier.bean.intellij.*;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.DataSource;
import java.util.*;
public class MeasureUtil {
/**
* select out app events out of a given event sequence. It worth noting that the
* selected app event sequence is alternating <strong>app activated event</strong>
* and <storng>app deactivated event</storng>.
* @param events the given event sequence
* @return the selected app event sequence
*/
public static List<AppEvent> getAppEventSequence(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<AppEvent> appEvents = new ArrayList<AppEvent>();
for (IntelliJEvent event : events) {
if (IntelliJEvent.isAppEvent(event)) {
AppEvent appEvent = IntelliJEvent.toAppEvent(event);
if (appEvent.isWhenActivated()) {
if (appEvents.isEmpty()) {
appEvents.add(appEvent);
} else if (appEvents.get(appEvents.size() - 1).isWhenDeactivated()) {
appEvents.add(appEvent);
}
} else if (appEvent.isWhenDeactivated()) {
if (appEvents.isEmpty()) {
appEvents.add(appEvent);
} else if (appEvents.get(appEvents.size() - 1).isWhenActivated()) {
appEvents.add(appEvent);
}
}
}
}
return appEvents.isEmpty() ? null : appEvents;
}
/**
* separate durations in the IDE of the given event sequence, and the durations
* outside the IDE are ignored.
* @param events the given event sequence
* @param appEvents the selected app event sequence of <code>events</code>
* @param begTime begin time of <code>events</code>
* @param endTime end time of <code>events</code>
* @return durations in the IDE
*/
public static List<List<IntelliJEvent>> getDurationInIdes(List<IntelliJEvent> events, List<AppEvent> appEvents, long begTime, long endTime) {
if (events == null || events.isEmpty()) {
return null;
}
List<List<IntelliJEvent>> durationInIDEs = new ArrayList<List<IntelliJEvent>>();
if (appEvents == null || appEvents.isEmpty()) {
durationInIDEs.add(events);
} else {
AppEvent prevAppEvent = appEvents.get(0);
if (prevAppEvent.isWhenDeactivated()) {
List<IntelliJEvent> tmp = DataSource.sliceEvents(events, begTime, prevAppEvent.getTime());
if (tmp != null) {
durationInIDEs.add(tmp);
}
}
for (int i = 1, l = appEvents.size(); i < l; i++) {
AppEvent nextAppEvent = appEvents.get(i);
if (nextAppEvent.isWhenDeactivated()) {
List<IntelliJEvent> tmp = DataSource.sliceEvents(events, prevAppEvent.getTime(), nextAppEvent.getTime());
if (tmp != null) {
durationInIDEs.add(tmp);
}
}
prevAppEvent = nextAppEvent;
}
if (prevAppEvent.isWhenActivated()) {
List<IntelliJEvent> tmp = DataSource.sliceEvents(events, prevAppEvent.getTime(), endTime);
if (tmp != null) {
durationInIDEs.add(tmp);
}
}
}
return durationInIDEs;
}
/**
* @see MeasureUtil#getDurationInIdes(List, List, long, long)
* @param events
* @param begTime
* @param endTime
* @return
*/
public static List<List<IntelliJEvent>> getDurationInIdes(List<IntelliJEvent> events, long begTime, long endTime) {
List<AppEvent> appEvents = getAppEventSequence(events);
return getDurationInIdes(events, appEvents, begTime, endTime);
}
/**
* get the number of entering IDE and the number of exiting IDE.
* @param appEvents selected app events
* @return the number of entering IDE and the number of exiting IDE
*/
public static int[] getEnterAndExitIdeCount(List<AppEvent> appEvents) {
int appEventCount = (appEvents == null) ? 0 : appEvents.size();
int exitIdeCount;
if (appEventCount % 2 == 0) { // 0, 2, 4, 6, 8, ...
exitIdeCount = appEventCount / 2;
} else { // 1, 3, 5, 7, 9, ...
exitIdeCount = appEvents.get(0).isWhenActivated() ? ((appEventCount - 1) / 2) : ((appEventCount + 1) / 2);
}
int enterIdeCount = appEventCount - exitIdeCount;
return new int[]{enterIdeCount, exitIdeCount};
}
/**
* get the time interval stay in the IDE and the time interval outside the IDE.
* @param appEvents selected app events
* @param begTime begin time
* @param endTime end time
* @return the time interval stay in the IDE and the time interval outside the IDE
*/
public static long[] getInAndOutsideIdeTime(List<AppEvent> appEvents, long begTime, long endTime) {
long inIdeTime; // time spent in the IDE
long outsideIdeTime;// time spent outside the IDE
if (appEvents == null || appEvents.isEmpty()) {
// this means that developers always stays in the IDE.
inIdeTime = endTime - begTime;
outsideIdeTime = 0;
} else {
inIdeTime = 0;
outsideIdeTime = 0;
AppEvent prevAppEvent = appEvents.get(0);
if (prevAppEvent.isWhenActivated()) {
outsideIdeTime += (prevAppEvent.getTime() - begTime);
} else {
inIdeTime += (prevAppEvent.getTime() - begTime);
}
for (int i = 1, l = appEvents.size(); i < l; i++) {
AppEvent nextAppEvent = appEvents.get(i);
if (nextAppEvent.isWhenActivated()) {
outsideIdeTime += (nextAppEvent.getTime() - prevAppEvent.getTime());
} else {
inIdeTime += (nextAppEvent.getTime() - prevAppEvent.getTime());
}
prevAppEvent = nextAppEvent;
}
if (prevAppEvent.isWhenActivated()) {
inIdeTime += (endTime - prevAppEvent.getTime());
} else {
outsideIdeTime += (endTime - prevAppEvent.getTime());
}
}
return new long[]{inIdeTime, outsideIdeTime};
}
/**
* get the time interval of inactivity in the IDE.
* @param events event sequence
* @param appEvents selected app events
* @param begTime begin time
* @param endTime end time
* @return the number and the time interval of inactivity in the IDE
*/
public static long getInactiveTimeInIDE(List<IntelliJEvent> events, List<AppEvent> appEvents, long begTime, long endTime, long threshold) {
long inactiveTime = 0;
List<List<IntelliJEvent>> durationInIdes = getDurationInIdes(events, appEvents, begTime, endTime);
if (durationInIdes == null) {
return endTime - begTime;
}
for (List<IntelliJEvent> durationInIde : durationInIdes) {
IntelliJEvent prevEvent = durationInIde.get(0);
for (int i = 1, l = durationInIde.size(); i < l; i++) {
IntelliJEvent nextEvent = durationInIde.get(i);
long deltaTime = nextEvent.getTime() - prevEvent.getTime();
if (deltaTime >= threshold) {
Assertion.assertTrue(deltaTime < 900000); // 15 minutes
inactiveTime += deltaTime;
}
prevEvent = nextEvent;
}
}
return inactiveTime;
}
/**
* select out context-related events including:
* <ul>
* <li><strong>Window activated events</strong>: we require ones whose <i>this</i> field denotes an IDE frame;</li>
* <li><strong>File selected events</strong>: we require ones whose <i>newPath</i> field is not null;</li>
* <li><strong>File changed events</strong>: we require ones whose <i>path</i> field is not null;</li>
* <li><strong>Cursor changed events</strong>: we require ones whose <i>editor</i> field denotes a main editor;</li>
* <li><strong>Cursor select changed events</strong>: we require all these.</li>
* </ul>
* @param events the given event sequence
* @return context-related events
*/
public static List<IntelliJEvent> getWindowFileCursorEventSequence(List<IntelliJEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
List<WindowEvent> windowEvents = new ArrayList<WindowEvent>();
List<FileEvent> fileEvents = new ArrayList<FileEvent>();
List<CursorEvent> cursorEvents = new ArrayList<CursorEvent>();
for (IntelliJEvent event : events) {
if (IntelliJEvent.isWindowEvent(event)) {
WindowEvent windowEvent = IntelliJEvent.toWindowEvent(event);
if (windowEvent.isWhenWindowActivated()) {
String thisName = windowEvent.getThisWindowName();
if (thisName != null && thisName.startsWith("frame")) {
windowEvents.add(windowEvent);
}
}
} else if (IntelliJEvent.isFileEvent(event)) {
FileEvent fileEvent = IntelliJEvent.toFileEvent(event);
if (fileEvent.isWhenSelected()) {
if (!(fileEvent.getNewPathField() == null && fileEvent.getOldPathField() == null)) {
fileEvents.add(fileEvent);
}
} else if (fileEvent.isWhenChanged()) {
if (fileEvent.getPathField() != null) {
fileEvents.add(fileEvent);
}
}
} else if (IntelliJEvent.isCursorEvent(event)) {
CursorEvent cursorEvent = IntelliJEvent.toCursorEvent(event);
if (cursorEvent.isWhenCursorChanged()) {
if (cursorEvent.isUntypedEditor() && (!"(0,0)".equals(cursorEvent.getOldPosField()))) {
cursorEvents.add(cursorEvent);
}
} else if (cursorEvent.isWhenSelectChanged()) {
cursorEvents.add(cursorEvent);
}
}
}
List<WindowEvent> selectedWindowEvents = new ArrayList<WindowEvent>();
String initFrame = selectWindowEvents(windowEvents, selectedWindowEvents);
List<FileEvent> selectedFileEvents = new ArrayList<FileEvent>();
String initFile = selectFileEvents(fileEvents, selectedFileEvents, selectedWindowEvents, initFrame);
List<IntelliJEvent> selectedEvents = DataSource.mergeEvents(DataSource.mergeEvents(
selectedWindowEvents, selectedFileEvents), cursorEvents);
if (selectedEvents == null) {
return null;
}
String focusedFrame = initFrame;
Map<String, String> frame2FocusedFile = new HashMap<String, String>();
updateStringMap(frame2FocusedFile, initFrame, initFile);
for (IntelliJEvent event : selectedEvents) {
if (event instanceof WindowEvent) {
WindowEvent windowEvent = (WindowEvent) event;
focusedFrame = windowEvent.getThisWindowName();
} else if (event instanceof FileEvent) {
FileEvent fileEvent = (FileEvent) event;
// Assertion.assertEqual(focusedFrame, fileEvent.getExtraData("frame"));
String frame = fileEvent.getExtraData("frame");
if (frame != null) {
focusedFrame = frame;
}
if (fileEvent.isWhenSelected()) {
updateStringMap(frame2FocusedFile, focusedFrame, fileEvent.getNewPathField());
} else {
updateStringMap(frame2FocusedFile, focusedFrame, fileEvent.getPathField());
}
} else {
CursorEvent cursorEvent = (CursorEvent) event;
cursorEvent.addExtraData("file", frame2FocusedFile.get(focusedFrame));
cursorEvent.addExtraData("frame", focusedFrame);
}
}
return selectedEvents;
}
private static String selectWindowEvents(List<WindowEvent> src, List<WindowEvent> dest) {
String initFrame = null;
String currFrame = null;
for (int i = 0, l = src.size(); i < l; i++) {
WindowEvent windowEvent = src.get(i);
Assertion.assertTrue(windowEvent.isWhenWindowActivated());
String thisName = windowEvent.getThisWindowName();
Assertion.assertNotNull(thisName);
Assertion.assertTrue(thisName.startsWith("frame"));
String oppoName = windowEvent.getOppositeWindowName();
if (i == 0) {
if (oppoName == null || oppoName.startsWith("frame")) {
initFrame = oppoName;
} else {
initFrame = thisName;
}
}
if (!thisName.equals(currFrame)) {
currFrame = thisName;
if (oppoName == null || oppoName.startsWith("frame")) {
dest.add(windowEvent);
}
}
}
return initFrame;
}
private static String selectFileEvents(List<FileEvent> src, List<FileEvent> dest, List<WindowEvent> selectedWindowEvents, String initFrame) {
Map<String, Set<String>> file2Frames = new HashMap<String, Set<String>>();
Map<String, Integer> fileFrame2Count = new HashMap<String, Integer>();
for (FileEvent fileEvent : src) {
Assertion.assertTrue(fileEvent.isWhenSelected() || fileEvent.isWhenChanged());
String frame = initFrame;
for (WindowEvent windowEvent : selectedWindowEvents) {
if (windowEvent.getTime() <= fileEvent.getTime()) {
frame = windowEvent.getThisWindowName();
} else {
break;
}
}
if (frame == null) {
continue;
}
if (fileEvent.isWhenSelected()) {
String oldPath = fileEvent.getOldPathField();
String newPath = fileEvent.getNewPathField();
Assertion.assertNotAllNull(oldPath, newPath);
updateSetMap(file2Frames, oldPath, frame);
updateSetMap(file2Frames, newPath, frame);
updateCountMap(fileFrame2Count, String.format("%s:%s", frame, oldPath));
updateCountMap(fileFrame2Count, String.format("%s:%s", frame, newPath));
} else {
String path = fileEvent.getPathField();
Assertion.assertNotNull(path);
updateSetMap(file2Frames, path, frame);
updateCountMap(fileFrame2Count, String.format("%s:%s", frame, path));
}
}
Map<String, String> file2Frame = new HashMap<String, String>();
for (Map.Entry<String, Set<String>> entry : file2Frames.entrySet()) {
String frame = null;
int count = 0;
for (String s : entry.getValue()) {
int v = fileFrame2Count.get(String.format("%s:%s", s, entry.getKey()));
if (v > count) {
frame = s;
count = v;
}
}
Assertion.assertNotNull(frame);
file2Frame.put(entry.getKey(), frame);
}
String initFile = null;
boolean isFileInitialized = false;
for (FileEvent fileEvent : src) {
String path = fileEvent.isWhenSelected() ? fileEvent.getNewPathField() : fileEvent.getPathField();
String frame = file2Frame.get(path);
fileEvent.addExtraData("frame", frame);
if (path != null) {
dest.add(fileEvent);
}
if ((!isFileInitialized) && frame != null && frame.equals(initFrame)) {
if (fileEvent.isWhenSelected()) {
initFile = fileEvent.getOldPathField();
} else {
initFile = fileEvent.getPathField();
}
isFileInitialized = true;
}
}
return initFile;
}
// private static void selectCursorEvents(List<CursorEvent> src, List<IntelliJEvent> windowFileEvents) {
// for (CursorEvent cursorEvent : src) {
// Assertion.assertTrue(cursorEvent.isWhenCursorChanged() || cursorEvent.isWhenSelectChanged());
// if (cursorEvent.isWhenCursorChanged()) {
// Assertion.assertTrue(cursorEvent.isUntypedEditor());
// Assertion.assertNotEqual(cursorEvent.getOldPosField(), "(0,0)");
// }
//
// String frame = null;
// String file = null;
// for (FileEvent fileEvent : selectedFileEvents) {
// if (fileEvent.getTime() <= cursorEvent.getTime()) {
// file = fileEvent.isWhenSelected() ? fileEvent.getNewPathField() : fileEvent.getPathField();
// frame = fileEvent.getExtraData("frame");
// } else {
// break;
// }
// }
//
// if (file == null) {
// frame = initFrame;
// for (WindowEvent windowEvent : selectedWindowEvents) {
// if (windowEvent.getTime() <= cursorEvent.getTime()) {
// frame = windowEvent.getThisWindowName();
// } else {
// break;
// }
// }
// }
//
// cursorEvent.addExtraData("file", file);
// cursorEvent.addExtraData("frame", frame);
// }
// }
public static <T> void updateListMap(Map<String, List<T>> map, String k, T v) {
if (k == null || v == null) {
return;
}
List<T> list = map.get(k);
if (list == null) {
list = new ArrayList<T>();
}
list.add(v);
map.put(k, list);
}
public static <T> void updateSetMap(Map<String, Set<T>> map, String k, T v) {
if (k == null || v == null) {
return;
}
Set<T> set = map.get(k);
if (set == null) {
set = new HashSet<T>();
}
set.add(v);
map.put(k, set);
}
public static void updateStringMap(Map<String, String> map, String k, String v) {
if (k == null || v == null) {
return;
}
map.put(k, v);
}
public static void updateCountMap(Map<String, Integer> map, String k) {
if (k == null) {
return;
}
Integer v = map.get(k);
if (v == null) {
v = 0;
}
v++;
map.put(k, v);
}
public static <T> Map<T, Integer> countList(List<T> list) {
if (list == null || list.isEmpty()) {
return null;
}
Map<T, Integer> map = new HashMap<T, Integer>();
for (T item : list) {
Integer count = map.get(item);
if (count == null) {
count = 0;
}
count++;
map.put(item, count);
}
return map;
}
public static <K, V extends Number> double countMap(Map<K, V> map) {
if (map == null || map.isEmpty()) {
return 0;
}
double count = 0;
for (Map.Entry<K, V> entry : map.entrySet()) {
count += entry.getValue().doubleValue();
}
return count;
}
private static final double LOG_10_2 = Math.log(2);
/**
* calculate Shannon entropy: -\Sigma_{i=1}^n p_i * log_2 p_i, where p_i equals to frequency / count.
* @param frequencies frequency of each item
* @param count \Sigma_{i=1}^n frequency_i
* @return Shannon entropy
*/
public static <K, V extends Number> double shannonEntropy(Map<K, V> frequencies, double count) {
if (frequencies == null || frequencies.isEmpty() || count <= 0) {
return 0;
}
double result = 0;
for (Map.Entry<K, V> entry : frequencies.entrySet()) {
double frequency = entry.getValue().doubleValue();
Assertion.assertPositive(frequency);
double probability = frequency / count;
result -= (probability * Math.log(probability) / LOG_10_2);
}
return result;
}
/**
* @see MeasureUtil#shannonEntropy(Map, double)
* @param frequencies
* @param <K>
* @param <V>
* @return
*/
public static <K, V extends Number> double shannonEntropy(Map<K, V> frequencies) {
if (frequencies == null || frequencies.isEmpty()) {
return 0;
}
double count = countMap(frequencies);
return shannonEntropy(frequencies, count);
}
/**
* @see MeasureUtil#shannonEntropy(Map, double)
* @param sequence
* @param <T>
* @return
*/
public static <T> double shannonEntropy(List<T> sequence) {
if (sequence == null || sequence.isEmpty()) {
return 0;
}
Map<T, Integer> frequencies = countList(sequence);
return shannonEntropy(frequencies, sequence.size());
}
/**
* @see MeasureUtil#removeOutliers(List, int, int)
*/
public static void removeOutliers(List<long[]> cursorChangingHistory) {
if (cursorChangingHistory == null || cursorChangingHistory.isEmpty()) {
return;
}
int maxDis = 5;
int minHop = Math.min(cursorChangingHistory.size(), Math.max(5, cursorChangingHistory.size() / 10));
removeOutliers(cursorChangingHistory, maxDis, minHop);
}
/**
* Removing outliers in the cursor changed sequence.
* @param cursorChangingHistory the history of changing the position (line) of the cursor.
* @param maxDis for any two adjacent cursor changed events, the difference between
* the former's {@code newPos} and the latter's {@code oldPos} must be
* less than the given {@code maxDis}.
* @param minHop the continuous sub cursor changed sequence is valid
* if and only if its hops exceed {@code minHop}.
*/
public static void removeOutliers(List<long[]> cursorChangingHistory, int maxDis, int minHop) {
if (cursorChangingHistory == null || cursorChangingHistory.isEmpty()) {
return;
}
List<List<long[]>> traces = new ArrayList<List<long[]>>();
List<long[]> tmp = null;
for (long[] item : cursorChangingHistory) {
if (tmp == null) {
tmp = new ArrayList<long[]>();
tmp.add(item);
traces.add(tmp);
} else {
long lastNewLine = tmp.get(tmp.size() - 1)[1];
long currOldLine = item[2];
if (Math.abs(currOldLine - lastNewLine) < maxDis) {
tmp.add(item);
} else {
tmp = new ArrayList<long[]>();
tmp.add(item);
traces.add(tmp);
}
}
}
for (List<long[]> trace : traces) {
if (trace.size() < minHop) {
cursorChangingHistory.removeAll(trace);
}
}
}
}

View File

@ -0,0 +1,111 @@
package edu.nju.ics.frontier.recognition.segment;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractSliceSeparateTask implements Executor.Task {
protected long mWindowSize;
protected double mOverlap;
protected Gson mGson;
/**
* Constructor.
* @param windowSize window size in minute.
*/
public AbstractSliceSeparateTask(long windowSize, double overlap) {
mWindowSize = windowSize * 60 * 1000;
mOverlap = overlap;
mGson = DataSource.getGson();
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
FileUtil.delete(new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName())));
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
int sliceCount = 0;
for (File dateDir : dateDirs) {
File sessionFile = new File(FileUtil.combine(dateDir.getAbsolutePath(), "sessions.json"));
if (!sessionFile.exists()) {
continue;
}
List<Session> sessions = FileUtil.fromJson(mGson, new TypeToken<ArrayList<Session>>(){}.getType(), sessionFile);
if (sessions == null || sessions.isEmpty()) {
continue;
}
List<Slice> slices = new ArrayList<Slice>();
for (Session session : sessions) {
List<Slice> tmp = createSlice(session);
if (tmp != null) {
slices.addAll(tmp);
}
}
if (!slices.isEmpty()) {
FileUtil.toJson(mGson, slices, new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName(), dateDir.getName(), "slices.json")));
sliceCount += slices.size();
}
}
System.out.printf("%s/%s -> %d slices\n", teamDir.getName(), userDir.getName(), sliceCount);
}
/**
* Separate slices out of a session.
* @param session session
* @return new slices, or null if no slice meets all the conditions.
*/
protected abstract List<Slice> createSlice(Session session);
protected List<Label> filterLabels(List<Label> labels, long sessionBegTime, long sessionEndTime) {
if (labels == null || labels.isEmpty()) {
return null;
}
List<Label> result = new ArrayList<Label>();
for (Label label : labels) {
// check whether the developer answer all of the questions
Label.Type type = label.getCanonicalType();
if (type == Label.Type.REFUSE_WITHOUT_SELECT || type == Label.Type.CLOSE_WITHOUT_SELECT) {
continue;
}
// check is there sufficient events for the label
long sliceEndTime = label.getEndTime();
long sliceBegTime = sliceEndTime - mWindowSize;
if (!(sessionBegTime <= sliceBegTime && sliceEndTime <= sessionEndTime)) {
continue;
}
// check whether submit repeatedly in a short time
if (!result.isEmpty()) {
long t = result.get(result.size() - 1).getEndTime();
if (sliceBegTime <= t && t <= sliceEndTime) {
double ratio = ((double) (t - sliceBegTime)) / ((double) mWindowSize);
Assertion.assertInClosedIntervalFrom0To1(ratio);
if (ratio > mOverlap) {
result.remove(result.size() - 1);
}
}
}
// usable label
result.add(label);
}
return result.isEmpty() ? null : result;
}
}

View File

@ -0,0 +1,85 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.DataSource;
import java.util.ArrayList;
import java.util.List;
public class EventBasedSliceSeparateTask extends AbstractSliceSeparateTask {
public EventBasedSliceSeparateTask(long windowSize, double overlap) {
super(windowSize, overlap);
}
@Override
protected List<Slice> createSlice(Session session) {
String team = session.getTeam();
String user = session.getUser();
String date = session.getDate();
long sessionBegTime = session.getBegTime();
long sessionEndTime = session.getEndTime();
int sessionId = session.getSessionId();
List<IntelliJEvent> events = session.getEvents();
List<Label> labels = filterLabels(session.getLabels(), sessionBegTime, sessionEndTime);
if (events == null || labels == null) {
return null;
}
Assertion.assertTrue(sessionEndTime - sessionBegTime >= mWindowSize);
List<Slice> result = new ArrayList<Slice>();
int labelOffset = 0;
long sliceOffset = (long) ((1 - mOverlap) * mWindowSize);
for (long sliceBegTime = sessionBegTime; sliceBegTime < sessionEndTime; sliceBegTime += sliceOffset) {
long sliceEndTime = sliceBegTime + mWindowSize;
if (sliceEndTime <= sessionEndTime) {
List<IntelliJEvent> eventCache = DataSource.sliceEvents(events, sliceBegTime, sliceEndTime);
Label label = getLabel(labels, labelOffset, sliceBegTime, sliceEndTime);
if (label != null) {
labelOffset++;
}
result.add(new Slice(team, user, date, sliceBegTime, sliceEndTime,
sessionBegTime, sessionEndTime, sessionId, eventCache, label));
} else {
Assertion.assertFalse(result.isEmpty());
long extraTime = sliceEndTime - sessionEndTime;
Assertion.assertTrue(0 < extraTime && extraTime <= sliceOffset);
Label newLabel = getLabel(labels, labelOffset, sliceBegTime, sliceEndTime);
if (newLabel != null) {
labelOffset++;
Slice slice = result.get(result.size() - 1);
Label oldLabel = slice.getLabel();
if (oldLabel != null) {
Assertion.assertTrue(newLabel.getEndTime() - oldLabel.getEndTime() >= sliceOffset);
}
slice.setLabel(newLabel);
}
break;
}
}
Assertion.assertEqual(labelOffset, labels.size());
return result.isEmpty() ? null : result;
}
private Label getLabel(List<Label> labels, int offset, long sliceBegTime, long sliceEndTime) {
if (labels == null || labels.isEmpty() || offset < 0 || offset >= labels.size()) {
return null;
}
Label label = labels.get(offset);
long time = label.getEndTime();
if (time < sliceBegTime) {
throw new IllegalArgumentException("this means there is a missing label!");
} else {
if (time <= sliceEndTime) {
return label;
} else {
return null;
}
}
}
}

View File

@ -0,0 +1,57 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import java.util.ArrayList;
import java.util.List;
public class InputEventIterator {
private List<IntelliJEvent> mEvents;
private int mOffset;
private int mBreakpoint;
private boolean mIsOffsetUpdated;
public InputEventIterator(List<IntelliJEvent> events) {
mEvents = events;
mOffset = 0;
mBreakpoint = 0;
mIsOffsetUpdated = false;
}
public IntelliJEvent nextInputEvent() {
if (mEvents == null ||mEvents.isEmpty() || mOffset >= mEvents.size()) {
return null;
}
for (int i = mOffset, l = mEvents.size(); i < l; i++) {
IntelliJEvent event = mEvents.get(i);
if (IntelliJEvent.isKeyEvent(event) || IntelliJEvent.isMouseEvent(event)) {
mOffset = i + 1;
mIsOffsetUpdated = true;
return event;
}
}
mOffset = mEvents.size();
mIsOffsetUpdated = true;
return null;
}
public List<IntelliJEvent> separate() {
if (mEvents == null || mEvents.isEmpty() || (!mIsOffsetUpdated) || mBreakpoint == mOffset - 1) {
return null;
}
int begIndex = mBreakpoint;
int endIndex = mOffset - 2;
if (begIndex < 0 || endIndex >= mEvents.size() || begIndex > endIndex) {
throw new IllegalArgumentException(String.format("begin index=%d, end index=%d, interval=[0, %d]",
begIndex, endIndex, mEvents.size()));
}
List<IntelliJEvent> result = new ArrayList<IntelliJEvent>();
for (int i = begIndex; i <= endIndex; i++) {
result.add(mEvents.get(i));
}
mBreakpoint = mOffset - 1;
return result;
}
}

View File

@ -0,0 +1,50 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.DataSource;
import java.util.LinkedList;
import java.util.List;
public class LabelBasedSliceSeparateTask extends AbstractSliceSeparateTask {
public LabelBasedSliceSeparateTask(long windowSize, double overlap) {
super(windowSize, overlap);
}
@Override
protected List<Slice> createSlice(Session session) {
String team = session.getTeam();
String user = session.getUser();
String date = session.getDate();
long sessionBegTime = session.getBegTime();
long sessionEndTime = session.getEndTime();
int sessionId = session.getSessionId();
List<IntelliJEvent> events = session.getEvents();
List<Label> labels = filterLabels(session.getLabels(), sessionBegTime, sessionEndTime);
if (events == null || labels == null) {
return null;
}
List<Slice> slices = new LinkedList<Slice>();
for (Label label : labels) {
long sliceEndTime = label.getEndTime();
long sliceBegTime = sliceEndTime - mWindowSize;
List<IntelliJEvent> eventCache = DataSource.sliceEvents(events, sliceBegTime, sliceEndTime);
boolean hasAnyInputEvent = false;
for (IntelliJEvent event : eventCache) {
if (IntelliJEvent.isKeyEvent(event) || IntelliJEvent.isMouseEvent(event)) {
hasAnyInputEvent = true;
break;
}
}
if (hasAnyInputEvent) {
slices.add(new Slice(team, user, date, sliceBegTime, sliceEndTime,
sessionBegTime, sessionEndTime, sessionId, eventCache, label));
} else {
throw new IllegalArgumentException("no any input event in the slice!");
}
}
return slices.isEmpty() ? null : slices;
}
}

View File

@ -0,0 +1,282 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* A Study of Visual Studio Usage in Practice.
* Since we cannot directly determine the kind of inactivity from our events, we heuristically
* separate inactivity intervals by their duration, reasoning that longer inactivity is more
* likely to be an actual interruption of a developers work in the IDE. We separate short
* inactivity from long inactivity using a threshold t.
*/
public class SaturationEffectTask implements Executor.Task {
private long[] mShortInactivityCounts;
private long[] mShortInactivityTimes;
private long[] mLongInactivityCounts;
private long[] mLongInactivityTimes;
private boolean mIsOnlyInput;
public SaturationEffectTask(boolean isOnlyInput) {
this.mShortInactivityCounts = initLongArray(30, 0);
this.mShortInactivityTimes = initLongArray(30, 0);
this.mLongInactivityCounts = initLongArray(30, 0);
this.mLongInactivityTimes = initLongArray(30, 0);
this.mIsOnlyInput = isOnlyInput;
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
int workdayCount = 0;
long[] shortInactivityCounts = initLongArray(30, 0);
long[] shortInactivityTimes = initLongArray(30, 0);
long[] longInactivityCounts = initLongArray(30, 0);
long[] longInactivityTimes = initLongArray(30, 0);
for (File dateDir : dateDirs) {
File[] eventFiles = FileUtil.listSubFiles(dateDir, "event", ".json");
if (eventFiles == null) {
continue;
}
List<IntelliJEvent> events = new ArrayList<IntelliJEvent>();
for (File eventFile : eventFiles) {
List<IntelliJEvent> tmp = DataSource.loadEvents(eventFile);
if (tmp != null) {
events.addAll(tmp);
}
}
if (events.isEmpty()) {
continue;
}
if (mIsOnlyInput) {
calculateDeltaTimeBetweenInputEvents(events, shortInactivityCounts, shortInactivityTimes, longInactivityCounts, longInactivityTimes);
} else {
calculateDeltaTimeBetweenEvents(events, shortInactivityCounts, shortInactivityTimes, longInactivityCounts, longInactivityTimes);
}
workdayCount++;
}
addArray(mShortInactivityCounts, shortInactivityCounts);
addArray(mShortInactivityTimes, shortInactivityTimes);
addArray(mLongInactivityCounts, longInactivityCounts);
addArray(mLongInactivityTimes, longInactivityTimes);
System.out.printf("%s/%s: #workday=%d\n", teamDir.getName(), userDir.getName(), workdayCount);
determineSaturationEffect(shortInactivityCounts, shortInactivityTimes, longInactivityCounts, longInactivityTimes, true);
}
private void calculateDeltaTimeBetweenInputEvents(
List<IntelliJEvent> events, long[] shortInactivityCounts, long[] shortInactivityTimes,
long[] longInactivityCounts, long[] longInactivityTimes) {
InputEventIterator iterator = new InputEventIterator(events);
IntelliJEvent prevEvent = iterator.nextInputEvent();
if (prevEvent == null) {
return;
}
IntelliJEvent nextEvent;
while ((nextEvent = iterator.nextInputEvent()) != null) {
long deltaTime = nextEvent.getTime() - prevEvent.getTime();
for (int j = 0, n = shortInactivityCounts.length; j < n; j++) {
long threshold = 60000 * (j + 1);
if (deltaTime < threshold) {
shortInactivityCounts[j]++;
shortInactivityTimes[j] += deltaTime;
} else {
longInactivityCounts[j]++;
longInactivityTimes[j] += deltaTime;
}
}
prevEvent = nextEvent;
}
}
private void calculateDeltaTimeBetweenEvents(
List<IntelliJEvent> events, long[] shortInactivityCounts, long[] shortInactivityTimes,
long[] longInactivityCounts, long[] longInactivityTimes) {
IntelliJEvent prevEvent = events.get(0);
for (int i = 1, m = events.size(); i < m; i++) {
IntelliJEvent nextEvent = events.get(i);
long deltaTime = nextEvent.getTime() - prevEvent.getTime();
for (int j = 0, n = shortInactivityCounts.length; j < n; j++) {
long threshold = 60000 * (j + 1);
if (deltaTime < threshold) {
shortInactivityCounts[j]++;
shortInactivityTimes[j] += deltaTime;
} else {
longInactivityCounts[j]++;
longInactivityTimes[j] += deltaTime;
}
}
prevEvent = nextEvent;
}
}
private double[][] percentageAssignedToLongInactivity(long[] shortInactivityCounts, long[] shortInactivityTimes,
long[] longInactivityCounts, long[] longInactivityTimes) {
double[][] result = new double[longInactivityCounts.length][2];
for (int i = 0, l = result.length; i < l; i++) {
result[i][0] = ((double) longInactivityCounts[i]) / ((double) (shortInactivityCounts[i] + longInactivityCounts[i]));
result[i][1] = ((double) longInactivityTimes[i]) / ((double) (shortInactivityTimes[i] + longInactivityTimes[i]));
}
return result;
}
public void determineSaturationEffect(boolean isShowDetails) {
determineSaturationEffect(mShortInactivityCounts, mShortInactivityTimes, mLongInactivityCounts, mLongInactivityTimes, isShowDetails);
}
private void determineSaturationEffect(long[] shortInactivityCounts, long[] shortInactivityTimes,
long[] longInactivityCounts, long[] longInactivityTimes,
boolean isShowDetails) {
double[][] percentages = percentageAssignedToLongInactivity(
shortInactivityCounts, shortInactivityTimes,
longInactivityCounts, longInactivityTimes);
int deltaSICountT = -1, deltaLICountT = -1, deltaSICountPercentageT = -1;
int deltaSITimeT = -1, deltaLITimeT = -1, deltaSITimePercentageT = -1;
StringBuilder builder = new StringBuilder();
builder.append(" t | #SI(i-1,i+1) #LI(i-1,i+1) #LI%(i-1,i+1) | △SI(i-1,i+1) △LI(i-1,i+1) △LI%(i-1,i+1)\n");
builder.append("--------------------------------------------------------------------------------------\n");
double deltaSICount1 = Double.NaN;
double deltaSICount2 = calculateRelativeDelta(shortInactivityCounts[1], shortInactivityCounts[0]);
double deltaLICount1 = Double.NaN;
double deltaLICount2 = calculateRelativeDelta(longInactivityCounts[1], longInactivityCounts[0]);
double deltaLICountPercentage1 = Double.NaN;
double deltaLICountPercentage2 = calculateRelativeDelta(percentages[1][0], percentages[0][0]);
double deltaSITime1 = Double.NaN;
double deltaSITime2 = calculateRelativeDelta(shortInactivityTimes[1], shortInactivityTimes[0]);
double deltaLITime1 = Double.NaN;
double deltaLITime2 = calculateRelativeDelta(longInactivityTimes[1], longInactivityTimes[0]);
double deltaLITimePercentage1 = Double.NaN;
double deltaLITimePercentage2 = calculateRelativeDelta(percentages[1][1], percentages[0][1]);
builder.append(String.format("%2d | %5.1f,%5.1f %5.1f,%5.1f %5.1f,%5.1f | %5.1f,%5.1f %5.1f,%5.1f %5.1f,%5.1f\n",
1,
deltaSICount1, deltaSICount2, deltaLICount1, deltaLICount2, deltaLICountPercentage1, deltaLICountPercentage2,
deltaSITime1, deltaSITime2, deltaLITime1, deltaLITime2, deltaLITimePercentage1, deltaLITimePercentage2));
for (int i = 1; i < percentages.length - 1; i++) {
deltaSICount1 = calculateRelativeDelta(shortInactivityCounts[i], shortInactivityCounts[i - 1]);
deltaSICount2 = calculateRelativeDelta(shortInactivityCounts[i + 1], shortInactivityCounts[i]);
deltaLICount1 = calculateRelativeDelta(longInactivityCounts[i], longInactivityCounts[i - 1]);
deltaLICount2 = calculateRelativeDelta(longInactivityCounts[i + 1], longInactivityCounts[i]);
deltaLICountPercentage1 = calculateRelativeDelta(percentages[i][0], percentages[i - 1][0]);
deltaLICountPercentage2 = calculateRelativeDelta(percentages[i + 1][0], percentages[i][0]);
deltaSITime1 = calculateRelativeDelta(shortInactivityTimes[i], shortInactivityTimes[i - 1]);
deltaSITime2 = calculateRelativeDelta(shortInactivityTimes[i + 1], shortInactivityTimes[i]);
deltaLITime1 = calculateRelativeDelta(longInactivityTimes[i], longInactivityTimes[i - 1]);
deltaLITime2 = calculateRelativeDelta(longInactivityTimes[i + 1], longInactivityTimes[i]);
deltaLITimePercentage1 = calculateRelativeDelta(percentages[i][1], percentages[i - 1][1]);
deltaLITimePercentage2 = calculateRelativeDelta(percentages[i + 1][1], percentages[i][1]);
builder.append(String.format("%2d | %5.1f,%5.1f %5.1f,%5.1f %5.1f,%5.1f | %5.1f,%5.1f %5.1f,%5.1f %5.1f,%5.1f\n",
i + 1,
deltaSICount1, deltaSICount2, deltaLICount1, deltaLICount2, deltaLICountPercentage1, deltaLICountPercentage2,
deltaSITime1, deltaSITime2, deltaLITime1, deltaLITime2, deltaLITimePercentage1, deltaLITimePercentage2));
if (deltaSICountT == -1) {
if (Math.abs(deltaSICount1) <= 5.0 && Math.abs(deltaSICount2) <= 5.0) {
deltaSICountT = i + 1;
}
}
if (deltaLICountT == -1) {
if (Math.abs(deltaLICount1) <= 5.0 && Math.abs(deltaLICount2) <= 5.0) {
deltaLICountT = i + 1;
}
}
if (deltaSICountPercentageT == -1) {
if (Math.abs(deltaLICountPercentage1) <= 5.0 && Math.abs(deltaLICountPercentage2) <= 5.0) {
deltaSICountPercentageT = i + 1;
}
}
if (deltaSITimeT == -1) {
if (Math.abs(deltaSITime1) <= 5.0 && Math.abs(deltaSITime2) <= 5.0) {
deltaSITimeT = i + 1;
}
}
if (deltaLITimeT == -1) {
if (Math.abs(deltaLITime1) <= 5.0 && Math.abs(deltaLITime2) <= 5.0) {
deltaLITimeT = i + 1;
}
}
if (deltaSITimePercentageT == -1) {
if (Math.abs(deltaLITimePercentage1) <= 5.0 && Math.abs(deltaLITimePercentage2) <= 5.0) {
deltaSITimePercentageT = i + 1;
}
}
}
deltaSICount1 = calculateRelativeDelta(shortInactivityCounts[shortInactivityCounts.length - 1], shortInactivityCounts[shortInactivityCounts.length - 2]);
deltaSICount2 = Double.NaN;
deltaLICount1 = calculateRelativeDelta(longInactivityCounts[longInactivityCounts.length - 1], longInactivityCounts[longInactivityCounts.length - 2]);
deltaLICount2 = Double.NaN;
deltaLICountPercentage1 = calculateRelativeDelta(percentages[percentages.length - 1][0], percentages[percentages.length - 2][0]);
deltaLICountPercentage2 = Double.NaN;
deltaSITime1 = calculateRelativeDelta(shortInactivityTimes[shortInactivityTimes.length - 1], shortInactivityTimes[shortInactivityTimes.length - 2]);
deltaSITime2 = Double.NaN;
deltaLITime1 = calculateRelativeDelta(longInactivityTimes[longInactivityTimes.length - 1], longInactivityTimes[longInactivityTimes.length - 2]);
deltaLITime2 = Double.NaN;
deltaLITimePercentage1 = calculateRelativeDelta(percentages[percentages.length - 1][1], percentages[percentages.length - 2][1]);
deltaLITimePercentage2 = Double.NaN;
builder.append(String.format("%2d | %5.1f,%5.1f %5.1f,%5.1f %5.1f,%5.1f | %5.1f,%5.1f %5.1f,%5.1f %5.1f,%5.1f\n",
shortInactivityCounts.length,
deltaSICount1, deltaSICount2, deltaLICount1, deltaLICount2, deltaLICountPercentage1, deltaLICountPercentage2,
deltaSITime1, deltaSITime2, deltaLITime1, deltaLITime2, deltaLITimePercentage1, deltaLITimePercentage2));
if (isShowDetails) {
System.out.print(builder.toString());
}
System.out.printf("deltaSICountT:%2d, deltaLICountT:%2d, deltaLICountPercentageT:%2d, deltaSITimeT:%2d, deltaLITimeT:%2d, deltaLITimePercentageT:%2d\n",
deltaSICountT, deltaLICountT, deltaSICountPercentageT, deltaSITimeT, deltaLITimeT, deltaSITimePercentageT);
}
private long[] initLongArray(int length, long value) {
if (length <= 0) {
return null;
}
long[] array = new long[length];
for (int i = 0; i < length; i++) {
array[i] = value;
}
return array;
}
/**
* Change of p relative to q.
* @param p
* @param q
* @return
*/
private double calculateRelativeDelta(double p, double q) {
return Double.isNaN(p) || Double.isNaN(q) ? Double.NaN : 100 * (p - q) / q;
}
private void addArray(long[] dest, long[] src) {
if (dest == null || src == null || dest.length != src.length) {
return;
}
for (int i = 0, l = dest.length; i < l; i++) {
dest[i] += src[i];
}
}
}

View File

@ -0,0 +1,251 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.configuration.Configuration;
import edu.nju.ics.frontier.util.Constants;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.util.HashMap;
import java.util.Map;
public class SegmentMain {
public static void main(String[] args) {
// String srcRoot1 = FileUtil.combine(Constants.FUJITSU_ROOT, "parse");
// ThresholdDetermineTask thresholdDetermineTask = new ThresholdDetermineTask(false);
// Executor.executeToUserDir(srcRoot1, "", thresholdDetermineTask);
// thresholdDetermineTask.determineSuitableLambda(true);
// SaturationEffectTask saturationEffectTask = new SaturationEffectTask(false);
// Executor.executeToUserDir(srcRoot1, "", saturationEffectTask);
// saturationEffectTask.determineSaturationEffect(true);
// String srcRoot2 = FileUtil.combine(Constants.FUJITSU_ROOT, "parse");
// String destRoot2 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "session");
// Executor.executeToUserDir(srcRoot2, destRoot2, new SessionSeparateTask(15, false));
// final Map<String, Integer> lambdas = getLambdaForIndividual();
// Executor.executeToUserDir(srcRoot2, destRoot2, new Executor.TaskFactory() {
// public Executor.Task newTask(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
// Integer lambda = lambdas.get(userDir.getName());
// if (lambda == null) {
// throw new IllegalArgumentException("no lambda for " + userDir.getName());
// }
// return new SessionSeparateTask(lambda, 0, false);
// }
// });
// String srcRoot3 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "session");
// String destRoot3 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "label_based_slice");
// Executor.executeToUserDir(srcRoot3, destRoot3, new LabelBasedSliceSeparateTask(30, 0.5));
// String srcRoot4 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "session");
// String destRoot4 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "event_based_slice");
// Executor.executeToUserDir(srcRoot4, destRoot4, new EventBasedSliceSeparateTask(30, 0.5));
String srcRoot5 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "session");
String destRoot5 = FileUtil.combine(Constants.FUJITSU_ROOT, "recognition", "time_aligned_slice");
TimeAlignedSliceSeparateTask timeAlignedSliceSeparateTask = new TimeAlignedSliceSeparateTask(
Configuration.WINDOW_SIZE_IN_MINUTE, Configuration.OVERLAP);
Executor.executeToUserDir(srcRoot5, destRoot5, timeAlignedSliceSeparateTask);
timeAlignedSliceSeparateTask.showLabelProperties();
}
private static Map<String, Integer> getLambdaForIndividual() {
Map<String, Integer> result = new HashMap<String, Integer>();
result.put("张瑶", 5);
result.put("徐敏敏", 5);
result.put("成小林", 12);
result.put("闾爽", 9);
result.put("刘亚辉", 7);
result.put("卢正华", 10);
result.put("司维", 10);
result.put("吴凡", 8);
result.put("夏国仟", 15);
result.put("孙昭", 12);
result.put("张怡文", 12);
result.put("彭钰", 19);
result.put("毛文婷", 8);
result.put("江超", 9);
result.put("王海飞", 15);
result.put("邵乃钊", 17);
result.put("陈键", 12);
result.put("伏秀秀", 8);
result.put("叶坤坤", 7);
result.put("周静", 7);
result.put("孙海明", 17);
result.put("孙荣荣", 14);
result.put("廖清", 15);
result.put("张学刚", 17);
result.put("张馨月", 10);
result.put("曹玺", 15);
result.put("李政", 12);
result.put("林翠平", 9);
result.put("王鑫", 25);
result.put("缪忍忍", 26);
result.put("钱佳峰", 11);
result.put("陈乐", 15);
return result;
}
}
/*
Output of ThresholdDetermineTask:
lambda #segment change(i-1,i+1)
1 22584 NaN,-39.2
2 13726 -39.2,-27.0
3 10021 -27.0,-20.7
4 7942 -20.7,-15.7
5 6699 -15.7,-25.4
6 4999 -25.4,-10.7
7 4462 -10.7, -8.7
8 4073 -8.7, -8.0
9 3746 -8.0, -7.6
10 3463 -7.6, -6.5
11 3238 -6.5, -6.0
12 3043 -6.0, -5.0
13 2891 -5.0, -5.7
14 2726 -5.7, -4.9
15 2592 -4.9, -4.8
16 2468 -4.8, -4.1
17 2367 -4.1, -3.8
18 2276 -3.8, -4.5
19 2173 -4.5, -3.5
20 2098 -3.5, -4.0
21 2014 -4.0, -4.0
22 1933 -4.0, -3.2
23 1872 -3.2, -2.5
24 1825 -2.5, -3.5
25 1761 -3.5, -2.4
26 1718 -2.4, -2.4
27 1677 -2.4, -2.7
28 1631 -2.7, -2.3
29 1593 -2.3, -2.3
30 1556 -2.3, -3.6
31 1500 -3.6, -3.1
32 1454 -3.1, -2.3
33 1421 -2.3, -2.6
34 1384 -2.6, -2.6
35 1348 -2.6, -2.2
36 1319 -2.2, -1.9
37 1294 -1.9, -2.0
38 1268 -2.0, -2.4
39 1238 -2.4, -2.2
40 1211 -2.2, -1.2
41 1196 -1.2, -2.3
42 1168 -2.3, -2.7
43 1136 -2.7, -1.7
44 1117 -1.7, -1.5
45 1100 -1.5, -0.8
46 1091 -0.8, -1.6
47 1074 -1.6, -1.8
48 1055 -1.8, -1.1
49 1043 -1.1, -1.2
50 1031 -1.2, -1.8
51 1012 -1.8, -1.6
52 996 -1.6, -1.2
53 984 -1.2, -1.2
54 972 -1.2, -2.2
55 951 -2.2, -2.2
56 930 -2.2, -2.4
57 908 -2.4, -2.3
58 887 -2.3, -1.8
59 871 -1.8, -2.0
60 854 -2.0, -1.5
61 841 -1.5, -2.3
62 822 -2.3, -2.4
63 802 -2.4, -2.4
64 783 -2.4, -2.2
65 766 -2.2, -3.9
66 736 -3.9, -3.5
67 710 -3.5, -2.8
68 690 -2.8, -2.5
69 673 -2.5, -1.8
70 661 -1.8, -1.8
71 649 -1.8, -3.5
72 626 -3.5, -2.1
73 613 -2.1, -2.4
74 598 -2.4, -2.2
75 585 -2.2, -2.6
76 570 -2.6, -1.8
77 560 -1.8, -1.6
78 551 -1.6, -1.5
79 543 -1.5, -2.0
80 532 -2.0, -1.9
81 522 -1.9, -1.0
82 517 -1.0, -1.2
83 511 -1.2, -1.8
84 502 -1.8, -2.2
85 491 -2.2, -1.6
86 483 -1.6, -2.1
87 473 -2.1, -1.9
88 464 -1.9, -1.7
89 456 -1.7, -1.8
90 448 -1.8, -1.1
91 443 -1.1, -1.8
92 435 -1.8, -1.8
93 427 -1.8, -0.9
94 423 -0.9, -0.5
95 421 -0.5, -1.9
96 413 -1.9, -0.5
97 411 -0.5, -1.0
98 407 -1.0, -2.5
99 397 -2.5, -3.3
100 384 -3.3, -2.9
101 373 -2.9, -1.3
102 368 -1.3, -1.9
103 361 -1.9, -0.8
104 358 -0.8, -0.3
105 357 -0.3, -1.4
106 352 -1.4, -1.7
107 346 -1.7, -1.7
108 340 -1.7, -1.2
109 336 -1.2, -1.8
110 330 -1.8, -2.1
111 323 -2.1, -0.6
112 321 -0.6, -1.2
113 317 -1.2, -1.3
114 313 -1.3, -1.6
115 308 -1.6, -0.6
116 306 -0.6, -1.3
117 302 -1.3, -1.7
118 297 -1.7, -0.7
119 295 -0.7, -1.0
120 292 -1.0, NaN
-------------------------------
minimum suitable lambda: 15
*/
/*
Output of SaturationEffectTask:
t | #SI(i-1,i+1) #LI(i-1,i+1) #LI%(i-1,i+1) | SI(i-1,i+1) LI(i-1,i+1) LI%(i-1,i+1)
--------------------------------------------------------------------------------------
1 | NaN, 0.0 NaN,-39.2 NaN,-39.2 | NaN, 23.8 NaN, -5.7 NaN, -5.7
2 | 0.0, 0.0 -39.2,-27.0 -39.2,-27.0 | 23.8, 14.0 -5.7, -4.4 -5.7, -4.4
3 | 0.0, 0.0 -27.0,-20.7 -27.0,-20.7 | 14.0, 9.8 -4.4, -3.6 -4.4, -3.6
4 | 0.0, 0.0 -20.7,-15.7 -20.7,-15.7 | 9.8, 6.9 -3.6, -2.9 -3.6, -2.9
5 | 0.0, 0.0 -15.7,-25.4 -15.7,-25.4 | 6.9, 10.2 -2.9, -4.8 -2.9, -4.8
6 | 0.0, 0.0 -25.4,-10.7 -25.4,-10.7 | 10.2, 3.6 -4.8, -2.0 -4.8, -2.0
7 | 0.0, 0.0 -10.7, -8.7 -10.7, -8.7 | 3.6, 2.9 -2.0, -1.7 -2.0, -1.7
8 | 0.0, 0.0 -8.7, -8.0 -8.7, -8.0 | 2.9, 2.7 -1.7, -1.6 -1.7, -1.6
9 | 0.0, 0.0 -8.0, -7.6 -8.0, -7.6 | 2.7, 2.6 -1.6, -1.6 -1.6, -1.6
10 | 0.0, 0.0 -7.6, -6.5 -7.6, -6.5 | 2.6, 2.2 -1.6, -1.4 -1.6, -1.4
11 | 0.0, 0.0 -6.5, -6.0 -6.5, -6.0 | 2.2, 2.0 -1.4, -1.4 -1.4, -1.4
12 | 0.0, 0.0 -6.0, -5.0 -6.0, -5.0 | 2.0, 1.7 -1.4, -1.2 -1.4, -1.2
13 | 0.0, 0.0 -5.0, -5.7 -5.0, -5.7 | 1.7, 2.0 -1.2, -1.4 -1.2, -1.4
14 | 0.0, 0.0 -5.7, -4.9 -5.7, -4.9 | 2.0, 1.7 -1.4, -1.2 -1.4, -1.2
15 | 0.0, 0.0 -4.9, -4.8 -4.9, -4.8 | 1.7, 1.6 -1.2, -1.3 -1.2, -1.3
16 | 0.0, 0.0 -4.8, -4.1 -4.8, -4.1 | 1.6, 1.4 -1.3, -1.1 -1.3, -1.1
17 | 0.0, 0.0 -4.1, -3.8 -4.1, -3.8 | 1.4, 1.3 -1.1, -1.1 -1.1, -1.1
18 | 0.0, 0.0 -3.8, -4.5 -3.8, -4.5 | 1.3, 1.5 -1.1, -1.3 -1.1, -1.3
19 | 0.0, 0.0 -4.5, -3.5 -4.5, -3.5 | 1.5, 1.2 -1.3, -1.0 -1.3, -1.0
20 | 0.0, 0.0 -3.5, -4.0 -3.5, -4.0 | 1.2, 1.4 -1.0, -1.2 -1.0, -1.2
21 | 0.0, 0.0 -4.0, -4.0 -4.0, -4.0 | 1.4, 1.4 -1.2, -1.2 -1.2, -1.2
22 | 0.0, 0.0 -4.0, -3.2 -4.0, -3.2 | 1.4, 1.1 -1.2, -1.0 -1.2, -1.0
23 | 0.0, 0.0 -3.2, -2.5 -3.2, -2.5 | 1.1, 0.8 -1.0, -0.8 -1.0, -0.8
24 | 0.0, 0.0 -2.5, -3.5 -2.5, -3.5 | 0.8, 1.2 -0.8, -1.1 -0.8, -1.1
25 | 0.0, 0.0 -3.5, -2.4 -3.5, -2.4 | 1.2, 0.8 -1.1, -0.8 -1.1, -0.8
26 | 0.0, 0.0 -2.4, -2.4 -2.4, -2.4 | 0.8, 0.8 -0.8, -0.8 -0.8, -0.8
27 | 0.0, 0.0 -2.4, -2.7 -2.4, -2.7 | 0.8, 0.9 -0.8, -0.9 -0.8, -0.9
28 | 0.0, 0.0 -2.7, -2.3 -2.7, -2.3 | 0.9, 0.8 -0.9, -0.8 -0.9, -0.8
29 | 0.0, 0.0 -2.3, -2.3 -2.3, -2.3 | 0.8, 0.8 -0.8, -0.8 -0.8, -0.8
30 | 0.0, NaN -2.3, NaN -2.3, NaN | 0.8, NaN -0.8, NaN -0.8, NaN
deltaSICountT: 2, deltaLICountT:15, deltaLICountPercentageT:15, deltaSITimeT: 7, deltaLITimeT: 3, deltaLITimePercentageT: 3
*/

View File

@ -0,0 +1,60 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import java.util.List;
public class Session {
private String team;
private String user;
private String date;
private long begTime;
private long endTime;
private int sessionId;
private List<IntelliJEvent> events;
private List<Label> labels;
public Session(String team, String user, String date, int sessionId, List<IntelliJEvent> events, List<Label> labels) {
this.team = team;
this.user = user;
this.date = date;
this.begTime = events.get(0).getTime();
this.endTime = events.get(events.size() - 1).getTime();
this.sessionId = sessionId;
this.events = events;
this.labels = labels;
}
public String getTeam() {
return team;
}
public String getUser() {
return user;
}
public String getDate() {
return date;
}
public long getBegTime() {
return begTime;
}
public long getEndTime() {
return endTime;
}
public int getSessionId() {
return sessionId;
}
public List<IntelliJEvent> getEvents() {
return events;
}
public List<Label> getLabels() {
return labels;
}
}

View File

@ -0,0 +1,171 @@
package edu.nju.ics.frontier.recognition.segment;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Action;
import edu.nju.ics.frontier.bean.label.ActionTypeConverter;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class SessionSeparateTask implements Executor.Task {
private long mLambda; // threshold to separate sessions
private boolean mIsOnlyInput;
private Gson mGson;
private int mSessionId;
/**
* Constructor.
* @param lambda threshold to separate sessions, in minute.
*/
public SessionSeparateTask(int lambda, boolean isOnlyInput) {
mLambda = lambda * 60 * 1000;
mIsOnlyInput = isOnlyInput;
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Action.class, new ActionTypeConverter());
mGson = builder.create();
mSessionId = 0;
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
// delete old directories and files
FileUtil.delete(new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName())));
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
int sessionCount = 0;
for (File dateDir : dateDirs) {
// load events
File[] eventFiles = FileUtil.listSubFiles(dateDir, "event", ".json");
if (eventFiles == null) {
continue;
}
List<IntelliJEvent> events = new ArrayList<IntelliJEvent>();
for (File eventFile : eventFiles) {
List<IntelliJEvent> tmp = DataSource.loadEvents(eventFile);
if (tmp != null) {
events.addAll(tmp);
}
}
if (events.isEmpty()) {
continue;
}
// load labels
File[] labelFiles = FileUtil.listSubFiles(dateDir, "label", ".json");
List<Label> labels = new ArrayList<Label>();
if (labelFiles != null) {
for (File labelFile : labelFiles) {
List<Label> tmp = DataSource.loadLabels(labelFile);
if (tmp != null) {
labels.addAll(tmp);
}
}
}
// segment
List<Session> sessions = new ArrayList<Session>();
if (mIsOnlyInput) {
separateSessionByInputEvent(sessions, events, labels, teamDir, userDir, dateDir);
} else {
separateSessionByEvent(sessions, events, labels, teamDir, userDir, dateDir);
}
// restore sessions
if (!sessions.isEmpty()) {
FileUtil.toJson(mGson, sessions, new File(FileUtil.combine(destRoot, teamDir.getName(), userDir.getName(), dateDir.getName(), "sessions.json")));
sessionCount += sessions.size();
}
}
System.out.printf("%s/%s -> %d sessions\n",
teamDir.getName(), userDir.getName(), sessionCount);
}
private void separateSessionByInputEvent(List<Session> sessions, List<IntelliJEvent> events, List<Label> labels,
File teamDir, File userDir, File dateDir) {
InputEventIterator iterator = new InputEventIterator(events);
IntelliJEvent prevEvent = iterator.nextInputEvent();
if (prevEvent == null) {
System.out.printf("%s/%s/%s: no any input event in the %d events.\n",
teamDir.getName(), userDir.getName(), dateDir.getName(), events.size());
return;
}
IntelliJEvent nextEvent;
while ((nextEvent = iterator.nextInputEvent()) != null) {
if (nextEvent.getTime() - prevEvent.getTime() >= mLambda) {
Session session = createSession(teamDir, userDir, dateDir, iterator.separate(), labels);
if (session != null) {
sessions.add(session);
}
}
prevEvent = nextEvent;
}
Session session = createSession(teamDir, userDir, dateDir, iterator.separate(), labels);
if (session != null) {
sessions.add(session);
}
}
private void separateSessionByEvent(List<Session> sessions, List<IntelliJEvent> events, List<Label> labels,
File teamDir, File userDir, File dateDir) {
List<IntelliJEvent> eventCache = new ArrayList<IntelliJEvent>();
IntelliJEvent prevEvent = events.get(0);
eventCache.add(prevEvent);
for (int i = 1, l = events.size(); i < l; i++) {
IntelliJEvent nextEvent = events.get(i);
if (nextEvent.getTime() - prevEvent.getTime() >= mLambda) {
Session session = createSession(teamDir, userDir, dateDir, eventCache, labels);
if (session != null) {
sessions.add(session);
}
eventCache = new ArrayList<IntelliJEvent>();
}
prevEvent = nextEvent;
eventCache.add(prevEvent);
}
Session session = createSession(teamDir, userDir, dateDir, eventCache, labels);
if (session != null) {
sessions.add(session);
}
}
/**
* create a session given the events and labels. A session is successful created
* if all the below conditions are met:
* <ul>
* <li>The time interval between any pair of subsequent events is less than
* <code>mLambda</code>.</li>
* <li>The duration of the session, which is the time interval between the
* first and the last events, is not less than <code>mLength</code>;</li>
* <li>There is at least one label is located in the session's duration.</li>
* </ul>
* @param teamDir team
* @param userDir user
* @param dateDir date
* @param eventCache events
* @param labels labels
* @return An new session, or null if any condition is not met.
*/
private Session createSession(File teamDir, File userDir, File dateDir,
List<IntelliJEvent> eventCache, List<Label> labels) {
if (eventCache == null || eventCache.isEmpty()) {
throw new IllegalArgumentException("no any event in the session!");
}
long begTime = eventCache.get(0).getTime();
long endTime = eventCache.get(eventCache.size() - 1).getTime();
List<Label> labelCache = DataSource.sliceLabels(labels, begTime, endTime, false);
return new Session(teamDir.getName(), userDir.getName(), dateDir.getName(), ++mSessionId,
eventCache, labelCache);
}
}

View File

@ -0,0 +1,77 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import java.util.List;
public class Slice {
private String team;
private String user;
private String date;
private long sliceBegTime;
private long sliceEndTime;
private long sessionBegTime;
private long sessionEndTime;
private int sessionId;
private List<IntelliJEvent> events;
private Label label;
public Slice(String team, String user, String date, long sliceBegTime, long sliceEndTime, long sessionBegTime,
long sessionEndTime, int sessionId, List<IntelliJEvent> events, Label label) {
this.team = team;
this.user = user;
this.date = date;
this.sliceBegTime = sliceBegTime;
this.sliceEndTime = sliceEndTime;
this.sessionBegTime = sessionBegTime;
this.sessionEndTime = sessionEndTime;
this.sessionId = sessionId;
this.events = events;
this.label = label;
}
public String getTeam() {
return team;
}
public String getUser() {
return user;
}
public String getDate() {
return date;
}
public int getSessionId() {
return sessionId;
}
public long getSliceBegTime() {
return sliceBegTime;
}
public long getSliceEndTime() {
return sliceEndTime;
}
public long getSessionBegTime() {
return sessionBegTime;
}
public long getSessionEndTime() {
return sessionEndTime;
}
public List<IntelliJEvent> getEvents() {
return events;
}
public Label getLabel() {
return label;
}
public void setLabel(Label label) {
this.label = label;
}
}

View File

@ -0,0 +1,174 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.util.DataSource;
import edu.nju.ics.frontier.util.Executor;
import edu.nju.ics.frontier.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* [17_JSS_CCF-B_Patterns of developers behaviour: A 1000-hour industrial study] defined
* a <b>working segment</b> as a sequence of events captured on the same workstation in
* which two subsequent events occur in less than <i>lambda</i> minutes.
* With a suitable <i>lambda</i>_i value, the number of sessions obtained by applying
* <i>lambda</i>_i as splitting threshold does not change of more than 5% with respect
* to <i>lambda</i>_{i-1} and <i>lambda</i>_{i+1}, where <i>lambda</i>_{i-1} + 1 =
* <i>lambda</i>_i = <i>lambda</i>_{i+1} - 1.
*/
public class ThresholdDetermineTask implements Executor.Task {
/**
* We varied lambda between 1 and 120 minute at steps of 1 minute,
* and count the number of sessions with the specified lambda.
*/
private long[] mSessionNums;
private boolean mIsOnlyInput;
public ThresholdDetermineTask(boolean isOnlyInput) {
this.mSessionNums = initLongArray(120, 0);
this.mIsOnlyInput = isOnlyInput;
}
public void run(String srcRoot, String destRoot, File teamDir, File userDir, String dateStr) {
File[] dateDirs = FileUtil.listSubDirectories(userDir);
if (dateDirs == null) {
return;
}
long[] sessionNums = initLongArray(120, 0);
int workdayCount = 0;
for (File dateDir : dateDirs) {
File[] eventFiles = FileUtil.listSubFiles(dateDir, "event", ".json");
if (eventFiles == null) {
continue;
}
List<IntelliJEvent> events = new ArrayList<IntelliJEvent>();
for (File eventFile : eventFiles) {
List<IntelliJEvent> tmp = DataSource.loadEvents(eventFile);
if (tmp != null) {
events.addAll(tmp);
}
}
if (events.isEmpty()) {
continue;
}
if (mIsOnlyInput) {
calculateDeltaTimeBetweenInputEvents(events, sessionNums);
} else {
calculateDeltaTimeBetweenEvents(events, sessionNums);
}
workdayCount++;
}
addArray(mSessionNums, sessionNums);
System.out.printf("%s/%s: #workday=%d\n", teamDir.getName(), userDir.getName(), workdayCount);
determineSuitableLambda(sessionNums, false);
}
private void calculateDeltaTimeBetweenInputEvents(List<IntelliJEvent> events, long[] sessionNums) {
InputEventIterator iterator = new InputEventIterator(events);
IntelliJEvent prevEvent = iterator.nextInputEvent();
if (prevEvent == null) {
return;
}
IntelliJEvent nextEvent;
while ((nextEvent = iterator.nextInputEvent()) != null) {
int diffMin = (int) ((nextEvent.getTime() - prevEvent.getTime()) / 60000);
for (int j = 0, n = sessionNums.length; j < n; j++) {
if (diffMin >= (j + 1)) {
sessionNums[j]++;
} else {
break;
}
}
prevEvent = nextEvent;
}
}
private void calculateDeltaTimeBetweenEvents(List<IntelliJEvent> events, long[] sessionNums) {
IntelliJEvent prevEvent = events.get(0);
for (int i = 1, m = events.size(); i < m; i++) {
IntelliJEvent currEvent = events.get(i);
int diffMin = (int) ((currEvent.getTime() - prevEvent.getTime()) / 60000);
for (int j = 0, n = sessionNums.length; j < n; j++) {
if (diffMin >= (j + 1)) {
sessionNums[j]++;
} else {
break;
}
}
prevEvent = currEvent;
}
}
public int determineSuitableLambda(boolean isShowDetails) {
return determineSuitableLambda(mSessionNums, isShowDetails);
}
private int determineSuitableLambda(long[] sessionNums, boolean isShowDetails) {
List<Integer> candidates = new ArrayList<Integer>();
StringBuilder builder = new StringBuilder();
builder.append("lambda #segment change(i-1,i+1)\n");
builder.append(String.format("%6d %8d %5.1f,%5.1f\n",
1, sessionNums[0], Double.NaN,
(((double) (sessionNums[1] - sessionNums[0])) / ((double) sessionNums[0])) * 100.0));
for (int i = 1; i < sessionNums.length - 1; i++) {
long lastNum = sessionNums[i - 1];
long currNum = sessionNums[i];
long nextNum = sessionNums[i + 1];
double delta1 = (((double) (currNum - lastNum)) / ((double) lastNum)) * 100.0;
double delta2 = (((double) (nextNum - currNum)) / ((double) currNum)) * 100.0;
builder.append(String.format("%6d %8d %5.1f,%5.1f\n", (i + 1), sessionNums[i], delta1, delta2));
if (Math.abs(delta1) <= 5.0 && Math.abs(delta2) <= 5.0) {
candidates.add(i + 1);
}
}
builder.append(String.format("%6d %8d %5.1f,%5.1f\n",
sessionNums.length, sessionNums[sessionNums.length - 1],
(((double) (sessionNums[sessionNums.length - 1] - sessionNums[sessionNums.length - 2])) / ((double) sessionNums[sessionNums.length - 2])) * 100.0, Double.NaN));
builder.append("-------------------------------\n");
if (isShowDetails) {
System.out.print(builder.toString());
}
System.out.print("minimum suitable lambda: ");
if (candidates.isEmpty()) {
System.out.println("null");
return -1;
} else {
System.out.println(candidates.get(0));
return candidates.get(0);
}
}
private long[] initLongArray(int length, long value) {
if (length <= 0) {
return null;
}
long[] array = new long[length];
for (int i = 0; i < length; i++) {
array[i] = value;
}
return array;
}
private void addArray(long[] dest, long[] src) {
if (dest == null || src == null || dest.length != src.length) {
return;
}
for (int i = 0, l = dest.length; i < l; i++) {
dest[i] += src[i];
}
}
}

View File

@ -0,0 +1,326 @@
package edu.nju.ics.frontier.recognition.segment;
import edu.nju.ics.frontier.bean.label.SelectOrSubmitAction;
import edu.nju.ics.frontier.common.io.OkTextReader;
import edu.nju.ics.frontier.bean.intellij.IntelliJEvent;
import edu.nju.ics.frontier.bean.label.Label;
import edu.nju.ics.frontier.bean.label.SelectAction;
import edu.nju.ics.frontier.recognition.feature.MeasureUtil;
import edu.nju.ics.frontier.util.Assertion;
import edu.nju.ics.frontier.util.DataSource;
import org.apache.commons.math3.stat.StatUtils;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.stat.descriptive.rank.Max;
import org.apache.commons.math3.stat.descriptive.rank.Median;
import org.apache.commons.math3.stat.descriptive.rank.Min;
import java.text.SimpleDateFormat;
import java.util.*;
public class TimeAlignedSliceSeparateTask extends AbstractSliceSeparateTask {
public TimeAlignedSliceSeparateTask(long windowSize, double overlap) {
super(windowSize, overlap);
}
@Override
protected List<Slice> createSlice(Session session) {
long sessionBegTime = session.getBegTime();
long sessionEndTime = session.getEndTime();
if (sessionEndTime - sessionBegTime < mWindowSize) {
return null;
}
String team = session.getTeam();
String user = session.getUser();
String date = session.getDate();
int sessionId = session.getSessionId();
List<IntelliJEvent> events = session.getEvents();
List<Label> labels = session.getLabels();
long step = (long) ((1 - mOverlap) * mWindowSize);
List<Slice> result = new ArrayList<Slice>();
Slice slice;
long sliceBegTime = findMaximumAlignedTimeBeforeSessionBegTime(sessionBegTime);
long sliceEndTime = sliceBegTime + mWindowSize;
List<IntelliJEvent> eventCache = DataSource.sliceEvents(events, sessionBegTime, sessionBegTime + mWindowSize);
List<Label> labelCache = DataSource.sliceLabels(labels, sessionBegTime, sessionBegTime + mWindowSize, true);
Label label = labelCache == null ? null : labelCache.get(labelCache.size() - 1);
label = eraseLabel(label);
slice = new Slice(team, user, date, sliceBegTime, sliceEndTime, sessionBegTime, sessionEndTime, sessionId, eventCache, label);
result.add(slice);
// TODO: check
recordLabelProperties(slice);
if (sessionEndTime - sessionBegTime == mWindowSize) {
return result;
}
for (sliceBegTime += step; sliceBegTime + mWindowSize < sessionEndTime; sliceBegTime += step) {
sliceEndTime = sliceBegTime + mWindowSize;
eventCache = DataSource.sliceEvents(events, sliceBegTime, sliceEndTime);
labelCache = DataSource.sliceLabels(labels, sliceBegTime, sliceEndTime, true);
label = labelCache == null ? null : labelCache.get(labelCache.size() - 1);
label = eraseLabel(label);
slice = new Slice(team, user, date, sliceBegTime, sliceEndTime, sessionBegTime, sessionEndTime, sessionId, eventCache, label);
result.add(slice);
// TODO: check
recordLabelProperties(slice);
}
sliceEndTime = sliceBegTime + mWindowSize;
eventCache = DataSource.sliceEvents(events, sessionEndTime - mWindowSize, sessionEndTime);
labelCache = DataSource.sliceLabels(labels, sessionEndTime - mWindowSize, sessionEndTime, true);
label = labelCache == null ? null : labelCache.get(labelCache.size() - 1);
label = eraseLabel(label);
slice = new Slice(team, user, date, sliceBegTime, sliceEndTime, sessionBegTime, sessionEndTime, sessionId, eventCache, label);
result.add(slice);
// TODO: check
recordLabelProperties(slice);
return result;
}
private long aligningTime(long millisecond) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(millisecond);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
}
private long findMaximumAlignedTimeBeforeSessionBegTime(long sessionBegTime) {
long sliceBegTime = aligningTime(sessionBegTime);
Assertion.assertTrue(sliceBegTime <= sessionBegTime);
if (sliceBegTime == sessionBegTime) {
return sliceBegTime;
}
long step = (long) ((1 - mOverlap) * mWindowSize);
Assertion.assertPositive(step);
while (sliceBegTime < sessionBegTime) {
if (sliceBegTime + step < sessionBegTime) {
sliceBegTime += step;
} else if (sliceBegTime + step > sessionBegTime) {
break;
} else {
sliceBegTime += step;
break;
}
}
Assertion.assertTrue(sliceBegTime <= sessionBegTime && sliceBegTime + step > sessionBegTime);
return sliceBegTime;
}
private static final SimpleDateFormat DATE_SDF = new SimpleDateFormat("yyyyMMdd");
private static final Map<String, Set<Long>> BAD_LABEL = new HashMap<String, Set<Long>>();
private static final int BAD_LABEL_NUM;
static {
int badDataNum = 0;
OkTextReader reader = new OkTextReader();
reader.open("C:\\Users\\zzw\\Desktop\\fujitsu\\bad_data.csv");
String line;
while ((line = reader.readLine()) != null) {
String[] info = line.split(",");
Assertion.assertEqual(info.length, 2);
String user = info[0];
long timestamp = Long.parseLong(info[1]);
MeasureUtil.updateSetMap(BAD_LABEL, user, timestamp);
badDataNum++;
}
reader.close();
BAD_LABEL_NUM = badDataNum;
System.out.println("load bad label: " + BAD_LABEL_NUM);
}
@SuppressWarnings("unchecked")
private List<Object[]>[][] labelProperties = new ArrayList[][]{
{
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
},
{
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
new ArrayList<Object[]>(),
},
};
public void showLabelProperties() {
for (int j = 0; j < labelProperties[0].length; j++) {
showLabelProperties("B_" + (j + 1), labelProperties[0][j]);
showLabelProperties("G_" + (j + 1), labelProperties[1][j]);
}
}
private Label eraseLabel(Label label) {
if (label == null) {
return null;
}
int engScore = label.getLabelScore(SelectOrSubmitAction.Dimension.ENGAGEMENT);
int proScore = label.getLabelScore(SelectOrSubmitAction.Dimension.PRODUCTIVITY);
long deltaFormTime = label.getEndTime() - label.getBegTime();
Assertion.assertNotNegative(deltaFormTime);
List<SelectAction> engActions = label.getSelectActions(SelectOrSubmitAction.Dimension.ENGAGEMENT);
List<SelectAction> proActions = label.getSelectActions(SelectOrSubmitAction.Dimension.PRODUCTIVITY);
long lastEngSelectTime = engActions.get(engActions.size() - 1).getTime();
long lastProSelectTime = proActions.get(proActions.size() - 1).getTime();
long deltaItemTime = Math.abs(lastProSelectTime - lastEngSelectTime);
switch (engScore) {
case 1: {
if (proScore != 1) {
return null;
}
break;
}
case 2: {
if (proScore != 2) {
return null;
}
break;
}
case 3: {
if (proScore != 3 || deltaItemTime > 1000) {
return null;
}
break;
}
case 4: {
if (proScore != 4 || deltaFormTime > 7000) {
return null;
}
break;
}
case 5: {
if (proScore != 5) {
return null;
}
break;
}
default: {
break;
}
}
return label;
}
private void recordLabelProperties(Slice slice) {
Label label = slice.getLabel();
if (label == null) {
return;
}
long labelBegTime = label.getBegTime();
long labelEndTime = label.getEndTime();
String type = label.getCanonicalType().name();
String date = DATE_SDF.format(new Date(labelEndTime));
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(labelEndTime);
String hour = String.valueOf(calendar.get(Calendar.HOUR_OF_DAY));
int engScore = label.getLabelScore(SelectOrSubmitAction.Dimension.ENGAGEMENT);
int proScore = label.getLabelScore(SelectOrSubmitAction.Dimension.PRODUCTIVITY);
int difScore = engScore - proScore;
long formDiffTime = labelEndTime - labelBegTime;
Assertion.assertNotNegative(formDiffTime);
List<SelectAction> engActions = label.getSelectActions(SelectOrSubmitAction.Dimension.ENGAGEMENT);
List<SelectAction> proActions = label.getSelectActions(SelectOrSubmitAction.Dimension.PRODUCTIVITY);
long lastEngSelectTime = engActions.get(engActions.size() - 1).getTime();
long lastProSelectTime = proActions.get(proActions.size() - 1).getTime();
long itemDiffTime = Math.abs(lastProSelectTime - lastEngSelectTime);
long sessDiffTime = labelEndTime - slice.getSessionBegTime();
Assertion.assertNotNegative(sessDiffTime);
Object[] tmp = new Object[]{
type, date, hour, proScore, difScore,
formDiffTime, itemDiffTime, sessDiffTime,
};
Set<Long> timestamps = BAD_LABEL.get(slice.getUser());
if (timestamps != null && timestamps.contains(slice.getSliceBegTime())) {
labelProperties[0][engScore - 1].add(tmp);
} else {
labelProperties[1][engScore - 1].add(tmp);
}
}
private void showLabelProperties(String title, List<Object[]> dataList) {
if (dataList.isEmpty()) {
return;
}
System.out.println("==================================================");
System.out.println(title);
// type, proScore, difScore, formDiffTime,
// itemDiffTime, sessDiffTime, date, hour,
Map<String, Integer> type = new HashMap<String, Integer>();
Map<String, Integer> date = new HashMap<String, Integer>();
Map<String, Integer> hour = new HashMap<String, Integer>();
double[] proScore = new double[dataList.size()];
double[] difScore = new double[dataList.size()];
double[] formDiffTime = new double[dataList.size()];
double[] itemDiffTime = new double[dataList.size()];
double[] sessDiffTime = new double[dataList.size()];
for (int i = 0; i < dataList.size(); i++) {
Object[] item = dataList.get(i);
MeasureUtil.updateCountMap(type, ((String) item[0]));
MeasureUtil.updateCountMap(date, ((String) item[1]));
MeasureUtil.updateCountMap(hour, ((String) item[2]));
proScore[i] = ((Integer) item[3]);
difScore[i] = ((Integer) item[4]);
formDiffTime[i] = ((Long) item[5]);
itemDiffTime[i] = ((Long) item[6]);
sessDiffTime[i] = ((Long) item[7]);
}
showCountMap("type", type);
showCountMap("date", date);
showCountMap("hour", hour);
boxPlot(proScore, "proScore");
boxPlot(difScore, "difScore");
boxPlot(formDiffTime, "formDiffTime");
boxPlot(itemDiffTime, "itemDiffTime");
boxPlot(sessDiffTime, "sessDiffTime");
}
private void showCountMap(String title, Map<String, Integer> map) {
List<String> keys = new ArrayList<String>(map.keySet());
Collections.sort(keys, new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
System.out.printf("%s{", title);
int sum = 0;
for (String key : keys) {
int value = map.get(key);
System.out.printf("#(%s)=%4d, ", key, value);
sum += value;
}
System.out.printf("#(SUM)=%4d}\n", sum);
}
private void boxPlot(double[] vector, String tag) {
double min = new Min().evaluate(vector);
double max = new Max().evaluate(vector);
double q1 = StatUtils.percentile(vector, 25);
double median = new Median().evaluate(vector);
double q3 = StatUtils.percentile(vector, 75);
double mean = new Mean().evaluate(vector);
System.out.printf("%s:{avg:%f,min:%f,max:%f,q1:%f,q2:%f,q3:%f}\n",
tag, mean, min, max, q1, median, q3);
}
}

Some files were not shown because too many files have changed in this diff Show More