Coverage for agentos/marketplace/skills/data-analysis/data-analysis.py: 2%

85 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-06 08:01 +0800

1""" 

2data-analysis — 基本数据分析:描述性统计、相关性、频率分布。 

3 

4Category: data 

5""" 

6 

7 

8def run(action: str, file_path: str = "", column: str = "", group_by: str = "", 

9 delimiter: str = ",", encoding: str = "utf-8") -> str: 

10 """数据分析工具。action: describe/correlation/freq/top_n。输入为 CSV 文件。""" 

11 import csv, os, math, collections 

12 

13 if not file_path or not os.path.isfile(file_path): 

14 return f"[data-analysis] 文件不存在: {file_path}" 

15 

16 try: 

17 with open(file_path,"r",encoding=encoding,newline="") as f: 

18 reader = csv.DictReader(f, delimiter=delimiter) 

19 if reader.fieldnames is None: 

20 return "[data-analysis] 无法解析表头" 

21 headers = list(reader.fieldnames) 

22 rows = list(reader) 

23 

24 def _numeric(col): 

25 vals = [] 

26 for row in rows: 

27 v = row.get(col,"") 

28 try: vals.append(float(v)) 

29 except ValueError: pass 

30 return vals 

31 

32 if action == "describe": 

33 target = [column] if column and column in headers else headers 

34 result = [f"文件: {file_path}", f"行数: {len(rows)}", ""] 

35 for col in target: 

36 vals = _numeric(col) 

37 if not vals: 

38 result.append(f"{col}: 无数值数据") 

39 continue 

40 n = len(vals) 

41 mean = sum(vals)/n 

42 srt = sorted(vals) 

43 median = srt[n//2] if n%2 else (srt[n//2-1]+srt[n//2])/2 

44 var = sum((x-mean)**2 for x in vals)/n 

45 std = math.sqrt(var) 

46 result.append( 

47 f"{col}: count={n}, mean={mean:.4f}, std={std:.4f}, " 

48 f"min={min(vals):.4f}, 25%={srt[n//4]:.4f}, median={median:.4f}, " 

49 f"75%={srt[3*n//4]:.4f}, max={max(vals):.4f}" 

50 ) 

51 return "\n".join(result) 

52 

53 if action == "correlation": 

54 numeric_cols = [h for h in headers if _numeric(h)] 

55 if len(numeric_cols) < 2: 

56 return "[data-analysis] 需要至少 2 个数值列" 

57 if column: 

58 targets = [column] if column in numeric_cols else numeric_cols[:2] 

59 else: 

60 targets = numeric_cols[:min(5, len(numeric_cols))] 

61 

62 def _pearson(xs, ys): 

63 n = min(len(xs), len(ys)) 

64 mx, my = sum(xs)/n, sum(ys)/n 

65 num = sum((xs[i]-mx)*(ys[i]-my) for i in range(n)) 

66 dx = math.sqrt(sum((x-mx)**2 for x in xs)) 

67 dy = math.sqrt(sum((y-my)**2 for y in ys)) 

68 return num/(dx*dy) if dx and dy else 0 

69 

70 result = ["相关性矩阵:", ""] 

71 result.append(" " + " ".join(f"{t:>8}" for t in targets)) 

72 for t1 in targets: 

73 row_vals = [f"{t1:<8}"] 

74 vals1 = _numeric(t1) 

75 for t2 in targets: 

76 if t1 == t2: row_vals.append(f"{1.0:>8.3f}") 

77 elif t1 < t2: row_vals.append(f"{_pearson(vals1,_numeric(t2)):>8.3f}") 

78 else: row_vals.append(" ") 

79 result.append(" ".join(row_vals)) 

80 return "\n".join(result) 

81 

82 if action == "freq": 

83 if not column or column not in headers: 

84 return f"[data-analysis] 请指定有效列名。可用: {', '.join(headers)}" 

85 counter = collections.Counter(row.get(column,"") for row in rows) 

86 lines = [f"{column} 频率分布 (共{len(counter)}个不同值):"] 

87 for val, cnt in counter.most_common(20): 

88 pct = cnt/len(rows)*100 

89 lines.append(f" {val}: {cnt} ({pct:.1f}%)") 

90 return "\n".join(lines) 

91 

92 if action == "top_n": 

93 if not column or column not in headers: 

94 return f"[data-analysis] 请指定有效列名。可用: {', '.join(headers)}" 

95 vals = [] 

96 for row in rows: 

97 v = row.get(column,"") 

98 try: vals.append((float(v), row)) 

99 except ValueError: pass 

100 vals.sort(key=lambda x: x[0], reverse=True) 

101 lines = [f"{column} Top 10:"] 

102 for i,(val,row) in enumerate(vals[:10]): 

103 lines.append(f" {i+1}. {column}={val} | {dict(row)}") 

104 return "\n".join(lines) 

105 

106 return f"[data-analysis] 未知操作: {action}, 支持: describe/correlation/freq/top_n" 

107 except Exception as e: 

108 return f"[data-analysis] 失败: {e}" 

109 

110 

111__all__ = ["run"]