File size: 7,558 Bytes
f54e1d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import argparse
import os
import json
from logging import getLogger
logger = getLogger(__name__)
from typing import Union, Dict

from modules.datafact_generator.util import DataFact
from modules.datafact_generator.value_fact import ValueFact, ValueFactGenerator
from modules.datafact_generator.trend_fact import TrendFact, TrendFactGenerator
from modules.datafact_generator.proportion_fact import ProportionFact, ProportionFactGenerator
from modules.datafact_generator.difference_fact import DifferenceFact, DifferenceFactGenerator
from .util import DataFact
from .value_fact import ValueFact, ValueFactGenerator
from .trend_fact import TrendFact, TrendFactGenerator
from .proportion_fact import ProportionFact, ProportionFactGenerator
from .difference_fact import DifferenceFact, DifferenceFactGenerator
from .correlation_fact import CorrelationFact, CorrelationFactGenerator

class DatafactGenerator:
    def __init__(self, data: dict, topk: int=5):
        self.data = data
        self.topk = topk

        self.value_facts: list[ValueFact] = []
        self.trend_facts: list[TrendFact] = []
        self.proportion_facts: list[ProportionFact] = []
        self.difference_facts: list[DifferenceFact] = []
        self.correlation_facts: list[CorrelationFact] = []

        self.datafacts: list[DataFact] = []
    
    def generate_datafacts(self, topk=5):
        """ 生成 datafacts """
        try:
            value_fact_generator = ValueFactGenerator(self.data)
            self.value_facts = value_fact_generator.extract_value_facts()
        except Exception as e:
            logger.error(f"生成value facts失败: {str(e)}")
            self.value_facts = []

        try:
            trend_fact_generator = TrendFactGenerator(self.data)
            self.trend_facts = trend_fact_generator.extract_trend_facts()
        except Exception as e:
            logger.error(f"生成trend facts失败: {str(e)}")
            self.trend_facts = []

        try:
            proportion_fact_generator = ProportionFactGenerator(self.data, self.value_facts)
            self.proportion_facts = proportion_fact_generator.extract_proportion_facts()
        except Exception as e:
            logger.error(f"生成proportion facts失败: {str(e)}")
            self.proportion_facts = []

        try:
            difference_fact_generator = DifferenceFactGenerator(self.data, self.value_facts)
            self.difference_facts = difference_fact_generator.extract_difference_facts()
        except Exception as e:
            logger.error(f"生成difference facts失败: {str(e)}")
            self.difference_facts = []

        try:
            correlation_fact_generator = CorrelationFactGenerator(self.data)
            self.correlation_facts = correlation_fact_generator.extract_correlation_facts()
        except Exception as e:
            logger.error(f"生成correlation facts失败: {str(e)}")
            self.correlation_facts = []

        self.datafacts = self.value_facts + self.trend_facts + self.proportion_facts + \
            self.difference_facts + self.correlation_facts

        self.datafacts = sorted(self.datafacts, key=lambda x: x.score, reverse=True)[:min(topk, len(self.datafacts))]

        return self.datafacts

def process(input: str, output: str) -> None:
    """
    Pipeline入口函数,处理单个文件的数据洞察生成
    
    Args:
        input (str): 输入JSON文件路径
        output (str): 输出JSON文件路径
    """
    try:
        # 读取输入文件
        with open(input, "r", encoding="utf-8") as f:
            data = json.load(f)
            
        # 预处理数据,确保类型正确
        processed_data = preprocess_data(data)
        
        # 调用原有的处理逻辑
        result = generate_datafacts(input_data=processed_data, input_path=None)
        
        if result:  # 确保有结果才写入
            # 保存结果
            with open(output, "w", encoding="utf-8") as f:
                json.dump(result, f, indent=2, ensure_ascii=False)
        else:
            logger.warning(f"跳过文件 {input}: 无有效结果")
            
    except Exception as e:
        logger.error(f"数据洞察生成失败: {str(e)}")
        raise

def preprocess_data(data):
    """
    预处理数据,处理类型转换问题
    """
    try:
        # 深拷贝避免修改原始数据
        processed = data.copy()
        
        # 确保data字段存在且格式正确
        if "data" in processed and isinstance(processed["data"], dict):
            # 处理数据部分
            if "data" in processed["data"]:
                rows = processed["data"]["data"]
                if isinstance(rows, list):
                    # 处理每一行数据
                    for i, row in enumerate(rows):
                        if isinstance(row, dict):
                            # 尝试将数值字符串转换为数值类型
                            for key, value in row.items():
                                if isinstance(value, str):
                                    try:
                                        # 尝试转换为数值
                                        if '.' in value:
                                            row[key] = float(value)
                                        else:
                                            row[key] = int(value)
                                    except (ValueError, TypeError):
                                        # 如果转换失败,保持原始值
                                        pass
                                elif value is None:
                                    # 将None替换为0或其他适当的默认值
                                    row[key] = 0
        
        return processed
        
    except Exception as e:
        logger.error(f"数据预处理失败: {str(e)}")
        raise

def generate_datafacts(input_data=None, input_path=None):
    """
    原有的数据洞察生成逻辑
    
    Args:
        input_data: 直接传入的数据对象
        input_path: 输入文件路径
    """
    try:
        if input_data is not None:
            data = input_data
            assert(input_path is None)
        else:
            assert(input_path and os.path.exists(input_path))
            try:
                with open(input_path, "r", encoding="utf-8") as f:
                    data = json.load(f)
            except Exception as e:
                logger.error(f"Failed to read input file: {e}")
                return None
        
        datafact_generator = DatafactGenerator(data)
        datafacts = datafact_generator.generate_datafacts()

        data["datafacts"] = [datafact.get_json() for datafact in datafacts if datafact.score > 0]

        return data
        
    except Exception as e:
        logger.error(f"生成数据洞察失败: {str(e)}")
        return None

def main():
    parser = argparse.ArgumentParser(description="Datafact Generator")
    parser.add_argument("--input", type=str, required=True, help="Input JSON file path")
    parser.add_argument("--output", type=str, required=True, help="Output JSON file path")
    parser.add_argument("--topk", type=int, default=5, help="Max number of facts to include")
    args = parser.parse_args()

    success = process(input_path=args.input, output_path=args.output, topk=args.topk)

    if success:
        print("Processing json successed.")
    else:
        print("Processing json successed.")

if __name__ == "__main__":
    main()