瀏覽代碼

feat:特征处理完成

zhaohaipeng 10 月之前
父節點
當前提交
2cbbbce050

+ 27 - 14
ad-engine-commons/pom.xml

@@ -1,7 +1,8 @@
 <?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">
+<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">
     <parent>
         <artifactId>ad-engine</artifactId>
         <groupId>com.tzld.piaoquan</groupId>
@@ -57,17 +58,29 @@
         </dependency>
 
 
-
-        <!--        <dependency>-->
-<!--            <groupId>net.devh</groupId>-->
-<!--            <artifactId>grpc-client-spring-boot-starter</artifactId>-->
-<!--            <version>2.9.0.RELEASE</version>-->
-<!--        </dependency>-->
-<!--        <dependency>-->
-<!--            <groupId>org.projectlombok</groupId>-->
-<!--            <artifactId>lombok</artifactId>-->
-<!--            <version>1.18.22</version>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>org.xm</groupId>
+            <artifactId>similarity</artifactId>
+            <version>1.1</version>
+            <exclusions>
+<!--                <exclusion>-->
+<!--                    <artifactId>logback-access</artifactId>-->
+<!--                    <groupId>ch.qos.logback</groupId>-->
+<!--                </exclusion>-->
+<!--                <exclusion>-->
+<!--                    <artifactId>logback-classic</artifactId>-->
+<!--                    <groupId>ch.qos.logback</groupId>-->
+<!--                </exclusion>-->
+<!--                <exclusion>-->
+<!--                    <artifactId>logback-core</artifactId>-->
+<!--                    <groupId>ch.qos.logback</groupId>-->
+<!--                </exclusion>-->
+                <exclusion>
+                    <artifactId>slf4j-api</artifactId>
+                    <groupId>org.slf4j</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 
 </project>

+ 200 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/util/ExtractorUtils.java

@@ -0,0 +1,200 @@
+package com.tzld.piaoquan.ad.engine.commons.util;
+
+import org.xm.Similarity;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ExtractorUtils {
+
+    public static double sigmoid(double x) {
+        return 1.0 / (1.0 + Math.exp(-x));
+    }
+    public static int findInsertPosition(double[] sortedArray, double target) {
+        int low = 0;
+        int high = sortedArray.length - 1;
+
+        while (low <= high) {
+            int mid = low + (high - low) / 2;
+            double midValue = sortedArray[mid];
+
+            if (midValue < target) {
+                low = mid + 1;
+            } else if (midValue > target) {
+                high = mid - 1;
+            } else {
+                // 找到相等的值,尝试在右侧寻找插入点
+                while (mid < sortedArray.length - 1 && sortedArray[mid + 1] == target) {
+                    mid++;
+                }
+                return mid + 1; // 返回当前mid的下一个位置作为插入点
+            }
+        }
+
+        return low; // 返回low作为插入点
+    }
+    public static Double[] funcC34567ForTags(String tags, String title) {
+        String[] tagsList = tags.split(",");
+        int d1 = 0;
+        List<String> d2 = new ArrayList<>();
+        double d3 = 0.0;
+        double d4 = 0.0;
+
+        for (String tag : tagsList) {
+            if (title.contains(tag)) {
+                d1++;
+                d2.add(tag);
+            }
+            double score = Similarity.conceptSimilarity(tag, title);
+            if (score > d3) {
+                d3 = score;
+            }
+            d4 += score;
+        }
+
+        d4 = (tagsList.length > 0) ? d4 / tagsList.length : d4;
+
+        // 使用数组来返回多个值
+        return new Double[]{(double) d1, d3, d4};
+    }
+    public static Double calDiv(double a, double b){
+        if (a == 0 || b == 0){
+            return 0D;
+        }
+        return a / b;
+    }
+    public static Double calLog(double a){
+        if (a <= 0){
+            return 0D;
+        }
+        return Math.log(a + 1.0);
+    }
+    public static Double division(String s1, String s2, Map<String, String> maps){
+        double rate = 0.0;
+        if (maps.containsKey(s1) && maps.containsKey(s2)){
+            Double d1 = Double.valueOf(maps.get(s1));
+            if (isDoubleEqualToZero(d1)){
+                return rate;
+            }
+            Double d2 = Double.valueOf(maps.get(s2));
+            rate = d2 / d1;
+        }
+        return rate;
+    }
+    public static Double divisionDouble(Double d1, Double d2){
+        double rate = 0.0;
+        if (isDoubleEqualToZero(d1)){
+            return rate;
+        }
+        rate = d2 / d1;
+        return rate;
+    }
+
+    public static boolean isDoubleEqualToZero(double value) {
+        final double epsilon = 1e-10; // 定义一个很小的误差范围
+        // 判断value是否在误差范围内
+        return Math.abs(value) < epsilon;
+    }
+
+
+
+    public static double calculateVariance(List<Double> numbers) {
+        double average = numbers.stream()
+                .mapToDouble(Double::doubleValue)
+                .average()
+                .orElse(0.0);
+
+        double squaredDiffSum = numbers.stream()
+                .mapToDouble(Double::doubleValue)
+                .map(x -> Math.pow(x - average, 2))
+                .average()
+                .orElse(0.0);
+
+        return squaredDiffSum;
+    }
+
+    public static double calculateAverage(List<Double> numbers) {
+        if (numbers == null || numbers.isEmpty()) {
+            return 0.0;
+        }
+        return numbers.stream()
+                .mapToDouble(Number::doubleValue)
+                .average()
+                .orElse(0.0);
+    }
+
+    public static List<Double> calculateDifferences(List<Double> numbers) {
+        List<Double> differences = new ArrayList<>();
+
+        for (int i = 0; i < numbers.size() - 1; i++) {
+            Double diff = 0.0;
+            if (!isDoubleEqualToZero(numbers.get(i))){
+                diff = (numbers.get(i + 1) - numbers.get(i)) / numbers.get(i);
+            }
+            differences.add(diff);
+        }
+
+        return differences;
+    }
+
+    public static List<String> generateHourStrings(String timeString, int N) {
+        LocalDateTime dateTime = LocalDateTime.parse(timeString, DateTimeFormatter.ofPattern("yyyyMMddHH"));
+        List<String> hourStrings = new ArrayList<>();
+        for (int i = 0; i < N; i++) {
+            hourStrings.add(dateTime.minusHours(i).format(DateTimeFormatter.ofPattern("yyyyMMddHH")));
+        }
+
+        return hourStrings;
+    }
+
+    public static String subtractHours(String inputDateTime, int hoursToSubtract) {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHH");
+        LocalDateTime dateTime = LocalDateTime.parse(inputDateTime, formatter);
+        LocalDateTime subtractedDateTime = dateTime.minusHours(hoursToSubtract);
+        return subtractedDateTime.format(formatter);
+    }
+
+    // 针对0-1的数字,进行分桶。
+    public static Integer ceilLogRate(Double key) {
+        double bucket = Math.ceil(
+                Math.pow(key, 0.2) * 100
+        );
+        if (bucket > 300) {
+            bucket = 300;
+        }
+        if (bucket < 0) {
+            bucket = 0;
+        }
+        return (int)bucket;
+    }
+
+    // 针对大于1的数字,进行分桶。
+    public static int bucketCnt(Double key) {
+        long bucket = Math.round(Math.log((key * 10 + 1.0)) * 10);
+        if (bucket > 300) {
+            bucket = 300;
+        }
+        if (bucket < 0) {
+            bucket = 0;
+        }
+        return (int)bucket;
+    }
+
+    public static void main(String[] args) {
+//        System.out.println(ceilLogRate(0.0002));
+//        System.out.println(ceilLogRate(0.01));
+//        System.out.println(ceilLogRate(0.2));
+//        System.out.println(ceilLogRate(4.));
+//        System.out.println(bucketCnt(1.));
+//        System.out.println(bucketCnt(20.));
+//        System.out.println(bucketCnt(500.));
+//        System.out.println(bucketCnt(50000.));
+
+        System.out.println(generateHourStrings("2024011603", 5));
+
+    }
+
+}

+ 12 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/util/NumUtil.java

@@ -0,0 +1,12 @@
+package com.tzld.piaoquan.ad.engine.commons.util;
+
+public class NumUtil {
+
+    public static double div(double d1, double d2) {
+        if (d1 == 0 || d2 == 0) {
+            return 0d;
+        }
+        return d1 / d2;
+    }
+
+}

+ 9 - 9
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/feature/FeatureService.java

@@ -4,7 +4,6 @@ import com.google.common.reflect.TypeToken;
 import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
 import com.tzld.piaoquan.ad.engine.commons.util.JSONUtils;
 import com.tzld.piaoquan.ad.engine.service.remote.FeatureV2RemoteService;
-import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
 import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRequestContext;
 import com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto;
 import lombok.extern.slf4j.Slf4j;
@@ -26,19 +25,15 @@ public class FeatureService {
     private static final String vidUkFormat = "v:%s:%s";
     private static final String midUkFormat = "m:%s:%s";
 
-    public Feature getFeature(List<AdRankItem> cidList, ScoreParam param) {
+    public Feature getFeature(Collection<String> cidList, Collection<String> adVerIdList, ScoreParam param) {
         AdRequestContext context = param.getRequestContext();
 
         List<FeatureKeyProto> protos = new ArrayList<>();
-        for (AdRankItem adRankItem : cidList) {
-            String cidStr = String.valueOf(adRankItem.getAdId());
+        for (String cidStr : cidList) {
 
             // cid
             protos.add(genWithCid("alg_cid_feature_basic_info", cidStr));
-            protos.add(genWithAdVerId("alg_cid_feature_cid_action", cidStr));
-
-            // adverid
-            protos.add(genWithAdVerId("alg_cid_feature_adver_action", adRankItem.getAdVerId()));
+            protos.add(genWithCid("alg_cid_feature_cid_action", cidStr));
 
             // cid + region
             protos.add(genWithCidAndRegion("alg_cid_feature_region_action", cidStr, context.getRegion()));
@@ -59,7 +54,12 @@ public class FeatureService {
             protos.add(genWithCidAndWechatVersion("alg_cid_feature_weChatVersion_action", cidStr, context.getMachineinfoWechatversion()));
 
             // cid + vid
-            protos.add(genWithCidAndVid("alg_cid_featuer_vid_cf", cidStr, param.getVideoId().toString()));
+            protos.add(genWithCidAndVid("alg_cid_feature_vid_cf", cidStr, param.getVideoId().toString()));
+        }
+
+        for (String adVerId : adVerIdList) {
+            // adverid
+            protos.add(genWithAdVerId("alg_cid_feature_adver_action", adVerId));
         }
 
         // vid

+ 2 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/dto/AdPlatformCreativeDTO.java

@@ -14,6 +14,8 @@ public class AdPlatformCreativeDTO {
 
     private String creativeCode;
 
+    private String adVerId;
+
     private Integer bidType;
 
     private Double pctr;

+ 394 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/impl/RankService680.java

@@ -1,17 +1,411 @@
 package com.tzld.piaoquan.ad.engine.service.score.impl;
 
+import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
+import com.tzld.piaoquan.ad.engine.commons.util.ExtractorUtils;
+import com.tzld.piaoquan.ad.engine.commons.util.NumUtil;
+import com.tzld.piaoquan.ad.engine.service.feature.Feature;
+import com.tzld.piaoquan.ad.engine.service.feature.FeatureService;
 import com.tzld.piaoquan.ad.engine.service.score.RankService;
+import com.tzld.piaoquan.ad.engine.service.score.convert.RequestConvert;
+import com.tzld.piaoquan.ad.engine.service.score.dto.AdPlatformCreativeDTO;
 import com.tzld.piaoquan.ad.engine.service.score.param.RankRecommendRequestParam;
 import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+import java.util.stream.Collectors;
+
 @Slf4j
 @Service
 public class RankService680 implements RankService {
+
+    @Autowired
+    private FeatureService featureService;
+
     @Override
     public AdRankItem adItemRank(RankRecommendRequestParam request) {
+
+        long ts = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) / 1000;
+
+        ScoreParam scoreParam = RequestConvert.requestConvert(request);
+        Feature feature = this.getFeature(scoreParam, request);
+        Map<String, Map<String, String>> userFeature = feature.getUserFeature();
+        Map<String, Map<String, String>> videoFeature = feature.getVideoFeature();
+        Map<String, Map<String, Map<String, String>>> allAdVerFeature = feature.getAdVerFeature();
+        Map<String, Map<String, Map<String, String>>> allCidFeature = feature.getCidFeature();
+
+        Map<String, String> userFeatureMap = new HashMap<>();
+        Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", new HashMap<>());
+        List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);
+
+        Map<String, Double> midTimeDiffMap = this.parseC1FeatureListToTimeDiffMap(midActionList, ts);
+        Map<String, Double> actionStaticMap = this.parseC1FeatureListToActionStaticMap(midActionList);
+
+        Map<String, String> d1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", new HashMap<>());
+        this.handleD1Feature(d1Feature, userFeatureMap);
+
+        Map<String, String> e1Feature = userFeature.getOrDefault("alg_mid_feature_return_tags", new HashMap<>());
+        Map<String, String> e2Feature = userFeature.getOrDefault("alg_mid_feature_share_tags", new HashMap<>());
+
+        Map<String, String> d2Feature = videoFeature.getOrDefault("alg_cid_feature_vid_cf_rank", new HashMap<>());
+
+        for (AdPlatformCreativeDTO dto : request.getAdIdList()) {
+            String cidStr = dto.getCreativeId().toString();
+            Map<String, String> cidFeatureMap = new HashMap<>();
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
+            Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", new HashMap<>());
+
+            Map<String, Map<String, String>> adVerFeature = allAdVerFeature.getOrDefault(dto.getAdVerId(), new HashMap<>());
+
+            this.handleB1Feature(b1Feature, cidFeatureMap);
+
+            this.handleB2ToB5AndB8Feature(cidFeature, adVerFeature, cidFeatureMap);
+
+            this.handleB6ToB7Feature(cidFeature, cidFeatureMap);
+
+            this.handleC1UIFeature(midTimeDiffMap, actionStaticMap, cidFeatureMap, cidStr);
+
+            this.handleD2Feature(d2Feature, cidFeatureMap, cidStr);
+
+            String title = b1Feature.getOrDefault("cidtitle", "");
+            this.handleE1AndE2Feature(e1Feature, e2Feature, title, cidFeatureMap);
+        }
+
+
         return null;
     }
 
+    private Feature getFeature(ScoreParam param, RankRecommendRequestParam request) {
+        List<AdPlatformCreativeDTO> adIdList = request.getAdIdList();
+        List<String> cidList = adIdList.stream()
+                .map(AdPlatformCreativeDTO::getCreativeId)
+                .map(Object::toString)
+                .collect(Collectors.toList());
+
+        List<String> adVerIdList = adIdList.stream()
+                .map(AdPlatformCreativeDTO::getAdVerId)
+                .distinct()
+                .collect(Collectors.toList());
+        return featureService.getFeature(cidList, adVerIdList, param);
+    }
+
+    private void handleB1Feature(Map<String, String> b1Feature, Map<String, String> cidFeatureMap) {
+        if (StringUtils.isNotBlank(b1Feature.get("adid"))) {
+            String adId = b1Feature.get("adid");
+            cidFeatureMap.put("adid_" + adId, "1");
+        }
+        if (StringUtils.isNotBlank(b1Feature.get("adverid"))) {
+            String adVerId = b1Feature.get("adverid");
+            cidFeatureMap.put("adverid_" + adVerId, "1");
+        }
+        if (StringUtils.isNotBlank(b1Feature.get("targeting_conversion"))) {
+            String targetingConversion = b1Feature.get("targeting_conversion");
+            cidFeatureMap.put("targeting_conversion_" + targetingConversion, "1");
+        }
+        if (StringUtils.isNotBlank(b1Feature.get("cpa"))) {
+            String cpa = b1Feature.get("cpa");
+            cidFeatureMap.put("cpa", cpa);
+        }
+    }
+
+    private void handleB2ToB5AndB8Feature(Map<String, Map<String, String>> c1Feature, Map<String, Map<String, String>> adVerFeature, Map<String, String> cidFeatureMap) {
+        Map<String, String> b2Feature = adVerFeature.getOrDefault("alg_cid_feature_adver_action", new HashMap<>());
+        Map<String, String> b3Feature = c1Feature.getOrDefault("alg_cid_feature_cid_action", new HashMap<>());
+        Map<String, String> b4Feature = c1Feature.getOrDefault("alg_cid_feature_region_action", new HashMap<>());
+        Map<String, String> b5Feature = c1Feature.getOrDefault("alg_cid_feature_app_action", new HashMap<>());
+        Map<String, String> b8Feature = c1Feature.getOrDefault("alg_cid_feature_brand_action", new HashMap<>());
+
+        List<String> timeList = Arrays.asList("3h", "6h", "12h", "1d", "3d", "7d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(b2Feature, "b2"),
+                new Tuple2<>(b3Feature, "b3"),
+                new Tuple2<>(b4Feature, "b4"),
+                new Tuple2<>(b5Feature, "b5"),
+                new Tuple2<>(b8Feature, "b8")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
+                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
+                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
+                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
+                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(NumUtil.div(click, view)));
+                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(NumUtil.div(conver, view)));
+                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.div(conver, click)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
+                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(NumUtil.div(income * 1000, view)));
+            }
+        }
+
+    }
+
+    private void handleB6ToB7Feature(Map<String, Map<String, String>> c1Feature, Map<String, String> cidFeatureMap) {
+        Map<String, String> b6Feature = c1Feature.getOrDefault("alg_cid_feature_week_action", new HashMap<>());
+        Map<String, String> b7Feature = c1Feature.getOrDefault("alg_cid_feature_hour_action", new HashMap<>());
+
+        List<String> timeList = Arrays.asList("7d", "14d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(b6Feature, "b6"),
+                new Tuple2<>(b7Feature, "b7")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
+                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
+                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
+                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
+                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(NumUtil.div(click, view)));
+                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(NumUtil.div(conver, view)));
+                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.div(conver, click)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
+                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(NumUtil.div(income * 1000, view)));
+            }
+        }
+
+    }
+
+    private List<TupleMapEntry<Tuple5>> handleC1Feature(Map<String, String> c1Feature, Map<String, String> featureMap) {
+
+        // 用户特征
+        List<TupleMapEntry<Tuple5>> midActionList = new ArrayList<>();
+        if (c1Feature.containsKey("action")) {
+            String action = c1Feature.get("action");
+            midActionList = Arrays.stream(action.split(","))
+                    .map(r -> {
+                        String[] rList = r.split(":");
+                        Tuple5 tuple5 = new Tuple5(rList[1], rList[2], rList[3], rList[4], rList[5]);
+                        return new TupleMapEntry<>(rList[0], tuple5);
+                    })
+                    .sorted((a, b) -> Integer.compare(Integer.parseInt(b.value.f1), Integer.parseInt(a.value.f1)))
+                    .collect(Collectors.toList());
+        }
+
+        double viewAll = midActionList.size();
+        double clickAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f2)).sum();
+        double converAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f3)).sum();
+        double incomeAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f4)).sum();
+        featureMap.put("viewAll", String.valueOf(viewAll));
+        featureMap.put("clickAll", String.valueOf(clickAll));
+        featureMap.put("converAll", String.valueOf(converAll));
+        featureMap.put("incomeAll", String.valueOf(incomeAll));
+        featureMap.put("ctr_all", String.valueOf(NumUtil.div(clickAll, viewAll)));
+        featureMap.put("ctcvr_all", String.valueOf(NumUtil.div(converAll, viewAll)));
+        featureMap.put("cvr_all", String.valueOf(NumUtil.div(clickAll, converAll)));
+        featureMap.put("ecpm_all", String.valueOf(NumUtil.div(incomeAll * 1000, viewAll)));
+
+        return midActionList;
+    }
+
+    private void handleC1UIFeature(Map<String, Double> midTimeDiffMap, Map<String, Double> midActionStatic, Map<String, String> featureMap, String cid) {
+        if (midTimeDiffMap.containsKey("timediff_view_" + cid)) {
+            featureMap.put("timediff_view_" + cid, String.valueOf(midTimeDiffMap.getOrDefault("timediff_view_" + cid, 0.0)));
+        }
+        if (midTimeDiffMap.containsKey("timediff_click_" + cid)) {
+            featureMap.put("timediff_click_" + cid, String.valueOf(midTimeDiffMap.getOrDefault("timediff_click_" + cid, 0.0)));
+        }
+        if (midTimeDiffMap.containsKey("timediff_conver_" + cid)) {
+            featureMap.put("timediff_conver_" + cid, String.valueOf(midTimeDiffMap.getOrDefault("timediff_conver_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid)) {
+            featureMap.put("actionstatic_view_" + cid, String.valueOf(midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            featureMap.put("actionstatic_click_" + cid, String.valueOf(midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_conver_" + cid)) {
+            featureMap.put("actionstatic_conver_" + cid, String.valueOf(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_income_" + cid)) {
+            featureMap.put("actionstatic_income_" + cid, String.valueOf(midActionStatic.getOrDefault("actionstatic_income_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            double ctr = NumUtil.div(
+                    midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0),
+                    midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)
+            );
+            featureMap.put("actionstatic_ctr", String.valueOf(ctr));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("timediff_conver_" + cid)) {
+            double ctcvr = NumUtil.div(
+                    midActionStatic.getOrDefault("timediff_conver_" + cid, 0.0),
+                    midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)
+            );
+            featureMap.put("actionstatic_ctcvr", String.valueOf(ctcvr));
+        }
+        if (midActionStatic.containsKey("actionstatic_conver_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            double cvr = NumUtil.div(
+                    midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0),
+                    midActionStatic.getOrDefault("timediff_conver_" + cid, 0.0)
+            );
+            featureMap.put("actionstatic_cvr", String.valueOf(cvr));
+        }
+    }
+
+    private void handleD1Feature(Map<String, String> d1Feature, Map<String, String> featureMap) {
+        for (String prefix : Arrays.asList("3h", "6h", "12h", "1d", "3d", "7d")) {
+            double view = Double.parseDouble(d1Feature.getOrDefault("ad_view_" + prefix, "0"));
+            double click = Double.parseDouble(d1Feature.getOrDefault("ad_click_" + prefix, "0"));
+            double conver = Double.parseDouble(d1Feature.getOrDefault("ad_conversion_" + prefix, "0"));
+            double income = Double.parseDouble(d1Feature.getOrDefault("ad_income_" + prefix, "0"));
+            featureMap.put("d1_feature_" + prefix + "_ctr", String.valueOf(NumUtil.div(click, view)));
+            featureMap.put("d1_feature_" + prefix + "_ctcvr", String.valueOf(NumUtil.div(conver, view)));
+            featureMap.put("d1_feature_" + prefix + "_cvr", String.valueOf(NumUtil.div(conver, click)));
+            featureMap.put("d1_feature_" + prefix + "_conver", String.valueOf(conver));
+            featureMap.put("d1_feature_" + prefix + "_ecpm", String.valueOf(NumUtil.div(income * 1000, view)));
+        }
+    }
+
+    private void handleD2Feature(Map<String, String> d2Feature, Map<String, String> featureMap, String cid) {
+        if (MapUtils.isEmpty(d2Feature)) {
+            return;
+        }
+
+        Map<String, Map<String, Double>> vidRankMaps = new HashMap<>();
+        for (Map.Entry<String, String> entry : d2Feature.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            Map<String, Double> valueMap = Arrays.stream(value.split(","))
+                    .map(r -> r.split(":"))
+                    .collect(Collectors.toMap(rList -> rList[0], rList -> Double.parseDouble(rList[2])));
+            vidRankMaps.put(key, valueMap);
+        }
+
+        List<String> prefixes1 = Arrays.asList("ctr", "ctcvr", "ecpm");
+        List<String> prefixes2 = Arrays.asList("1d", "3d", "7d", "14d");
+
+        for (String prefix1 : prefixes1) {
+            for (String prefix2 : prefixes2) {
+                String combinedKey = prefix1 + "_" + prefix2;
+                if (vidRankMaps.containsKey(combinedKey)) {
+                    Double rank = vidRankMaps.get(combinedKey).getOrDefault(cid, 0.0);
+                    if (rank >= 1.0) {
+                        featureMap.put("vid_rank_" + combinedKey, String.valueOf(NumUtil.div(1, rank)));
+                    }
+                }
+            }
+        }
+    }
+
+    private void handleE1AndE2Feature(Map<String, String> e1Feature, Map<String, String> e2Feature, String title, Map<String, String> featureMap) {
+        if (StringUtils.isEmpty(title)) {
+            return;
+        }
+
+        List<Tuple2<Map<String, String>, String>> tuple2List = Arrays.asList(
+                new Tuple2<>(e1Feature, "e1"),
+                new Tuple2<>(e2Feature, "e2")
+        );
+
+        List<String> tagsFieldList = Arrays.asList("tags_3d", "tags_7d", "tags_14d");
+        for (Tuple2<Map<String, String>, String> tuple2 : tuple2List) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            if (MapUtils.isEmpty(feature)) {
+                continue;
+            }
+
+            for (String tagsField : tagsFieldList) {
+                if (StringUtils.isNotEmpty(feature.get(tagsField))) {
+                    String tags = feature.get(tagsField);
+                    Double[] doubles = ExtractorUtils.funcC34567ForTags(tags, title);
+                    featureMap.put(prefix + "_" + tagsField + "_matchnum", String.valueOf(doubles[0]));
+                    featureMap.put(prefix + "_" + tagsField + "_maxscore", String.valueOf(doubles[1]));
+                    featureMap.put(prefix + "_" + tagsField + "_avgscore", String.valueOf(doubles[2]));
+                }
+            }
+        }
+    }
+
+    private Map<String, Double> parseC1FeatureListToTimeDiffMap(List<TupleMapEntry<Tuple5>> midActionList, long ts) {
+        Map<String, Double> midTimeDiffMap = new HashMap<>();
+        for (TupleMapEntry<Tuple5> entry : midActionList) {
+            String cid = entry.key;
+            long tsHistory = Long.parseLong(entry.value.f1);
+            long click = Long.parseLong(entry.value.f2);
+            long conver = Long.parseLong(entry.value.f3);
+            long d = (ts - tsHistory) / 3600 / 21;
+            if (!midTimeDiffMap.containsKey("timediff_view_" + cid)) {
+                midTimeDiffMap.put("timediff_view_" + cid, NumUtil.div(1, d));
+            }
+            if (!midTimeDiffMap.containsKey("timediff_click_" + cid) && click > 0) {
+                midTimeDiffMap.put("timediff_click_" + cid, NumUtil.div(1, d));
+            }
+            if (!midTimeDiffMap.containsKey("timediff_conver_" + cid) && conver > 0) {
+                midTimeDiffMap.put("timediff_conver_" + cid, NumUtil.div(1, d));
+            }
+        }
+        return midTimeDiffMap;
+    }
+
+    private Map<String, Double> parseC1FeatureListToActionStaticMap(List<TupleMapEntry<Tuple5>> midActionList) {
+        Map<String, Double> midActionStaticsMap = new HashMap<>();
+        for (TupleMapEntry<Tuple5> entry : midActionList) {
+            String cid = entry.key;
+            double click = Double.parseDouble(entry.value.f2);
+            double conver = Double.parseDouble(entry.value.f3);
+            double income = Double.parseDouble(entry.value.f4);
+
+            Double viewSum = midActionStaticsMap.getOrDefault("actionstatic_view_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_view_" + cid, 1 + viewSum);
+
+            Double clickSum = midActionStaticsMap.getOrDefault("actionstatic_click_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_click_" + cid, clickSum + click);
+
+            Double converSum = midActionStaticsMap.getOrDefault("actionstatic_conver_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_conver_" + cid, converSum + conver);
+
+            Double incomSum = midActionStaticsMap.getOrDefault("actionstatic_income_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_income_" + cid, incomSum + income);
+        }
+
+        return midActionStaticsMap;
+    }
+
+    public static class Tuple5 {
+        public String f1;
+        public String f2;
+        public String f3;
+        public String f4;
+        public String f5;
+
+        public Tuple5(String f1, String f2, String f3, String f4, String f5) {
+            this.f1 = f1;
+            this.f2 = f2;
+            this.f3 = f3;
+            this.f4 = f4;
+            this.f5 = f5;
+        }
+    }
+
+    public static class TupleMapEntry<T> {
+        public String key;
+        public T value;
+
+        public TupleMapEntry(String key, T value) {
+            this.key = key;
+            this.value = value;
+        }
+    }
+
+    public static class Tuple2<F1, F2> {
+        public F1 f1;
+
+        public F2 f2;
+
+        public Tuple2(F1 first, F2 name) {
+            this.f1 = first;
+            this.f2 = name;
+        }
+
+    }
 }