Determine Event Recurrence Pattern for a set of dates

后端 未结 8 431
梦毁少年i
梦毁少年i 2021-01-31 19:32

I am looking for a pattern, algorithm, or library that will take a set of dates and return a description of the recurrence if one exits, i.e. the set [11-01-2010, 11-08-2010, 11

8条回答
  •  遇见更好的自我
    2021-01-31 19:43

    I like @arjen answer but I don't think there is any need for complex algorithm. This is so so simple. If there is a pattern, there is a pattern... therefore a simple algorithm would work. First we need to think of the types of patterns we are looking for: daily, weekly, monthly and yearly.

    How to recognize?

    Daily: there is a record every day Weekly: there is a record every week Monthly: there is a record every month Yearly: there is a record every year

    Difficult? No. Just count how many repetitions you have and then classify.

    Here is my implementation

    RecurrencePatternAnalyser.java

    public class RecurrencePatternAnalyser {
    
        // Local copy of calendars by add() method 
        private ArrayList mCalendars = new ArrayList();
    
        // Used to count the uniqueness of each year/month/day 
        private HashMap year_count = new HashMap();
        private HashMap month_count = new HashMap();
        private HashMap day_count = new HashMap();
        private HashMap busday_count = new HashMap();
    
        // Used for counting payments before due date on weekends
        private int day_goodpayer_ocurrences = 0;
        private int day_goodPayer = 0;
    
        // Add a new calendar to the analysis
        public void add(Calendar date)
        {
            mCalendars.add(date);
            addYear( date.get(Calendar.YEAR) );
            addMonth( date.get(Calendar.MONTH) );
            addDay( date.get(Calendar.DAY_OF_MONTH) );
            addWeekendDays( date );
        }
    
        public void printCounts()
        {
            System.out.println("Year: " +  getYearCount() + 
                    " month: " +  getMonthCount() + " day: " +  getDayCount());
        }
    
        public RecurrencePattern getPattern()
        {
            int records = mCalendars.size();
            if (records==1)
                return null;
    
            RecurrencePattern rp = null;
    
            if (getYearCount()==records)
            {
                rp = new RecurrencePatternYearly();
                if (records>=3)
                    rp.setConfidence(1);
                else if (records==2)
                    rp.setConfidence(0.9f);
            }
            else if (getMonthCount()==records)
            {
                rp = new RecurrencePatternMonthly();
                if (records>=12)
                    rp.setConfidence(1);
                else
                    rp.setConfidence(1-(-0.0168f * records + 0.2f));
            } 
            else 
            {
                calcDaysRepetitionWithWeekends();
                if (day_goodpayer_ocurrences==records)
                {
                    rp = new RecurrencePatternMonthly();
                    rp.setPattern(RecurrencePattern.PatternType.MONTHLY_GOOD_PAYER);
                    if (records>=12)
                        rp.setConfidence(0.95f);
                    else
                        rp.setConfidence(1-(-0.0168f * records + 0.25f));
                }
            }
    
            return rp;
        }
    
        // Increment one more year/month/day on each count variable
        private void addYear(int key_year)  { incrementHash(year_count, key_year); }
        private void addMonth(int key_month)    { incrementHash(month_count, key_month); }
        private void addDay(int key_day)    { incrementHash(day_count, key_day); }
    
        // Retrieve number of unique entries for the records
        private int getYearCount() { return year_count.size(); }
        private int getMonthCount() { return month_count.size(); }
        private int getDayCount() { return day_count.size(); }
    
        // Generic function to increment the hash by 1  
        private void incrementHash(HashMap var, Integer key)
        {
            Integer oldCount = var.get(key);
            Integer newCount = 0;
            if ( oldCount != null ) {
                newCount = oldCount;
            }
            newCount++;
            var.put(key, newCount);
        }
    
        // As Bank are closed during weekends, some dates might be anticipated
        // to Fridays. These will be false positives for the recurrence pattern.
        // This function adds Saturdays and Sundays to the count when a date is 
        // Friday.
        private void addWeekendDays(Calendar c)
        {
            int key_day = c.get(Calendar.DAY_OF_MONTH);
            incrementHash(busday_count, key_day);
            if (c.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY)
            {
                // Adds Saturday
                c.add(Calendar.DATE, 1);
                key_day = c.get(Calendar.DAY_OF_MONTH);
                incrementHash(busday_count, key_day);
                // Adds Sunday
                c.add(Calendar.DATE, 1);
                key_day = c.get(Calendar.DAY_OF_MONTH);
                incrementHash(busday_count, key_day);
            }
        }
    
        private void calcDaysRepetitionWithWeekends()
        {               
            Iterator> it =
                    busday_count.entrySet().iterator();
            while (it.hasNext()) {
                @SuppressWarnings("rawtypes")
                Map.Entry pair = (Map.Entry)it.next();
                if ((int)pair.getValue() > day_goodpayer_ocurrences)
                {
                    day_goodpayer_ocurrences = (int) pair.getValue();
                    day_goodPayer = (int) pair.getKey();
                }
                //it.remove(); // avoids a ConcurrentModificationException
            }
        }
    }
    

    RecurrencePattern.java

    public abstract class RecurrencePattern {
    
        public enum PatternType {
            YEARLY, MONTHLY, WEEKLY, DAILY, MONTHLY_GOOD_PAYER 
        }   
        public enum OrdinalType {
            FIRST, SECOND, THIRD, FOURTH, FIFTH 
        }
    
        protected PatternType pattern;
        private float confidence;
        private int frequency;
    
        public PatternType getPattern() {
            return pattern;
        }
    
        public void setPattern(PatternType pattern) {
            this.pattern = pattern;
        }
    
        public float getConfidence() {
            return confidence;
        }
        public void setConfidence(float confidence) {
            this.confidence = confidence;
        }
        public int getFrequency() {
            return frequency;
        }
        public void setFrequency(int frequency) {
            this.frequency = frequency;
        }   
    }
    

    RecurrencePatternMonthly.java

    public class RecurrencePatternMonthly extends RecurrencePattern {
        private boolean isDayFixed;
        private boolean isDayOrdinal;
        private OrdinalType ordinaltype;
    
        public RecurrencePatternMonthly()
        {
            this.pattern = PatternType.MONTHLY;
        }
    }
    

    RecurrencePatternYearly.java

    public class RecurrencePatternYearly extends RecurrencePattern {
        private boolean isDayFixed;
        private boolean isMonthFixed;
        private boolean isDayOrdinal;
        private OrdinalType ordinaltype;
    
        public RecurrencePatternYearly()
        {
            this.pattern = PatternType.YEARLY;
        }
    }   
    

    Main.java

    public class Algofin {
    
        static Connection c = null;
    
        public static void main(String[] args) {
            //openConnection();
            //readSqlFile();
    
            RecurrencePatternAnalyser r = new RecurrencePatternAnalyser();
    
            //System.out.println(new GregorianCalendar(2015,1,30).get(Calendar.MONTH));
            r.add(new GregorianCalendar(2015,0,1));
            r.add(new GregorianCalendar(2015,0,30));
            r.add(new GregorianCalendar(2015,1,27));
            r.add(new GregorianCalendar(2015,3,1));
            r.add(new GregorianCalendar(2015,4,1));
    
            r.printCounts();
    
            RecurrencePattern rp;
            rp=r.getPattern();
            System.out.println("Pattern: " + rp.getPattern() + " confidence: " + rp.getConfidence());
        }
    }
    

提交回复
热议问题