统计分析脚本#

脚本说明#

统计分析脚本用于对数据进行统计分析,包括描述性统计、相关性分析、频率分布等。

脚本代码#

#!/bin/bash

# 统计分析脚本
# 功能:对数据进行统计分析
# 作者:System Admin
# 日期:2024-01-01

set -euo pipefail

# 配置变量
INPUT_FILE=""
OUTPUT_FILE=""
DELIMITER=","
HAS_HEADER=true
COLUMN=""
ANALYSIS_TYPE="descriptive"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# 日志函数
log() {
    local level=$1
    shift
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $@"
}

log_info() {
    log "INFO" "$@"
}

log_error() {
    log "ERROR" "$@"
}

# 检查输入文件
check_input_file() {
    if [ ! -f "$INPUT_FILE" ]; then
        log_error "输入文件不存在: $INPUT_FILE"
        return 1
    fi
    
    if [ ! -r "$INPUT_FILE" ]; then
        log_error "输入文件不可读: $INPUT_FILE"
        return 1
    fi
    
    return 0
}

# 描述性统计
descriptive_stats() {
    local file=$1
    local column=$2
    
    log_info "描述性统计: 列$column"
    
    # 获取列号
    local col_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column" '{for(i=1;i<=NF;i++) if($i==col) print i}')
    
    if [ -z "$col_num" ]; then
        log_error "列不存在: $column"
        return 1
    fi
    
    # 提取数据
    local data=()
    if [ "$HAS_HEADER" = true ]; then
        mapfile -t data < <(tail -n +2 "$file" | awk -F"$DELIMITER" -v col="$col_num" '$col ~ /^[0-9.]+$/ {print $col}')
    else
        mapfile -t data < <(awk -F"$DELIMITER" -v col="$col_num" '$col ~ /^[0-9.]+$/ {print $col}' "$file")
    fi
    
    if [ ${#data[@]} -eq 0 ]; then
        log_error "没有有效的数值数据"
        return 1
    fi
    
    # 计算统计量
    local count=${#data[@]}
    local sum=0
    local min=${data[0]}
    local max=${data[0]}
    
    for value in "${data[@]}"; do
        sum=$(echo "$sum + $value" | bc -l)
        
        if (( $(echo "$value < $min" | bc -l) )); then
            min=$value
        fi
        
        if (( $(echo "$value > $max" | bc -l) )); then
            max=$value
        fi
    done
    
    local mean=$(echo "scale=4; $sum / $count" | bc -l)
    
    # 计算中位数
    local sorted_data=($(printf '%s\n' "${data[@]}" | sort -n))
    local median
    if [ $((count % 2)) -eq 1 ]; then
        median=${sorted_data[$((count / 2))]}
    else
        local mid1=${sorted_data[$((count / 2 - 1))]}
        local mid2=${sorted_data[$((count / 2))]}
        median=$(echo "scale=4; ($mid1 + $mid2) / 2" | bc -l)
    fi
    
    # 计算方差和标准差
    local variance_sum=0
    for value in "${data[@]}"; do
        local diff=$(echo "$value - $mean" | bc -l)
        local square=$(echo "$diff * $diff" | bc -l)
        variance_sum=$(echo "$variance_sum + $square" | bc -l)
    done
    
    local variance=$(echo "scale=4; $variance_sum / $count" | bc -l)
    local std_dev=$(echo "scale=4; sqrt($variance)" | bc -l)
    
    # 计算四分位数
    local q1_index=$((count / 4))
    local q3_index=$((count * 3 / 4))
    local q1=${sorted_data[$q1_index]}
    local q3=${sorted_data[$q3_index]}
    local iqr=$(echo "$q3 - $q1" | bc -l)
    
    # 输出结果
    echo "描述性统计"
    echo "=========="
    echo "列名: $column"
    echo "样本数: $count"
    echo ""
    echo "集中趋势:"
    echo "  均值: $mean"
    echo "  中位数: $median"
    echo "  众数: $(printf '%s\n' "${data[@]}" | sort | uniq -c | sort -nr | head -1 | awk '{print $2}')"
    echo ""
    echo "离散程度:"
    echo "  最小值: $min"
    echo "  最大值: $max"
    echo "  极差: $(echo "$max - $min" | bc -l)"
    echo "  第一四分位数 (Q1): $q1"
    echo "  第三四分位数 (Q3): $q3"
    echo "  四分位距 (IQR): $iqr"
    echo "  方差: $variance"
    echo "  标准差: $std_dev"
    echo ""
    echo "分布形态:"
    echo "  偏度: $(python3 -c "import numpy as np; data = $([\"${data[@]}\", ] | sed 's/, /, /g'); print(f'{np.skew(data):.4f}')")"
    echo "  峰度: $(python3 -c "import numpy as np; data = $([\"${data[@]}\", ] | sed 's/, /, /g'); print(f'{np.kurtosis(data):.4f}')")"
}

# 频率分布
frequency_distribution() {
    local file=$1
    local column=$2
    local bins=${3:-10}
    
    log_info "频率分布: 列$column, 分箱数=$bins"
    
    # 获取列号
    local col_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column" '{for(i=1;i<=NF;i++) if($i==col) print i}')
    
    if [ -z "$col_num" ]; then
        log_error "列不存在: $column"
        return 1
    fi
    
    # 提取数据
    local data=()
    if [ "$HAS_HEADER" = true ]; then
        mapfile -t data < <(tail -n +2 "$file" | awk -F"$DELIMITER" -v col="$col_num" '{print $col}')
    else
        mapfile -t data < <(awk -F"$DELIMITER" -v col="$col_num" '{print $col}' "$file")
    fi
    
    if [ ${#data[@]} -eq 0 ]; then
        log_error "没有数据"
        return 1
    fi
    
    # 检查是否为数值数据
    local is_numeric=$(printf '%s\n' "${data[@]}" | grep -c "^[0-9.]*$")
    
    if [ $is_numeric -eq ${#data[@]} ]; then
        # 数值数据:创建直方图
        echo "频率分布(直方图)"
        echo "==================="
        echo "列名: $column"
        echo "分箱数: $bins"
        echo ""
        
        # 计算分箱边界
        local sorted_data=($(printf '%s\n' "${data[@]}" | sort -n))
        local min=${sorted_data[0]}
        local max=${sorted_data[$((${#sorted_data[@]} - 1))]}
        local bin_width=$(echo "scale=4; ($max - $min) / $bins" | bc -l)
        
        # 统计每个分箱的频率
        for ((i=0; i<bins; i++)); do
            local bin_min=$(echo "$min + $i * $bin_width" | bc -l)
            local bin_max=$(echo "$min + ($i + 1) * $bin_width" | bc -l)
            
            local count=0
            for value in "${data[@]}"; do
                if (( $(echo "$value >= $bin_min" | bc -l) )) && (( $(echo "$value < $bin_max" | bc -l) )); then
                    count=$((count + 1))
                fi
            done
            
            local frequency=$(echo "scale=2; $count * 100 / ${#data[@]}" | bc -l)
            printf "[$(printf '%.2f' "$bin_min"), $(printf '%.2f' "$bin_max")): %d (%.2f%%)\n" "$count" "$frequency"
        done
    else
        # 分类数据:统计频率
        echo "频率分布(分类数据)"
        echo "==================="
        echo "列名: $column"
        echo ""
        
        printf '%-30s %10s %10s\n' "值" "频数" "频率"
        printf '%-30s %10s %10s\n' "----------" "----------" "----------"
        
        # 统计每个值的频率
        declare -A freq_map
        for value in "${data[@]}"; do
            freq_map["$value"]=$((${freq_map["$value"]:-0} + 1))
        done
        
        # 按频率排序
        for value in "${!freq_map[@]}"; do
            echo "${freq_map[$value]} $value"
        done | sort -rn | while read -r count value; do
            local frequency=$(echo "scale=2; $count * 100 / ${#data[@]}" | bc -l)
            printf '%-30s %10d %10.2f%%\n' "$value" "$count" "$frequency"
        done
    fi
}

# 相关性分析
correlation_analysis() {
    local file=$1
    local column1=$2
    local column2=$3
    
    log_info "相关性分析: 列$column1 vs 列$column2"
    
    # 获取列号
    local col1_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column1" '{for(i=1;i<=NF;i++) if($i==col) print i}')
    local col2_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column2" '{for(i=1;i<=NF;i++) if($i==col) print i}')
    
    if [ -z "$col1_num" ] || [ -z "$col2_num" ]; then
        log_error "列不存在"
        return 1
    fi
    
    # 提取数据
    local data1=()
    local data2=()
    
    if [ "$HAS_HEADER" = true ]; then
        while IFS="$DELIMITER" read -ra row; do
            if [ "${#data1[@]}" -gt 0 ]; then  # 跳过表头
                data1+=("${row[$((col1_num - 1))]}")
                data2+=("${row[$((col2_num - 1))]}")
            fi
        done < <(tail -n +2 "$file")
    else
        while IFS="$DELIMITER" read -ra row; do
            data1+=("${row[$((col1_num - 1))]}")
            data2+=("${row[$((col2_num - 1))]}")
        done < "$file"
    fi
    
    # 计算相关性(使用Python)
    python3 <<EOF
import numpy as np

data1 = [${data1[@]}]
data2 = [${data2[@]}]

# 计算皮尔逊相关系数
correlation = np.corrcoef(data1, data2)[0, 1]

print(f"相关性分析")
print(f"===========")
print(f"列1: $column1")
print(f"列2: $column2")
print(f"")
print(f"皮尔逊相关系数: {correlation:.4f}")
print(f"")

if correlation > 0.7:
    print(f"结论: 强正相关")
elif correlation > 0.3:
    print(f"结论: 中等正相关")
elif correlation > 0:
    print(f"结论: 弱正相关")
elif correlation < -0.7:
    print(f"结论: 强负相关")
elif correlation < -0.3:
    print(f"结论: 中等负相关")
elif correlation < 0:
    print(f"结论: 弱负相关")
else:
    print(f"结论: 无相关")
EOF
}

# 生成分析报告
generate_report() {
    local file=$1
    
    log_info "生成分析报告"
    
    {
        echo "统计分析报告"
        echo "============"
        echo "文件: $file"
        echo "时间: $(date)"
        echo ""
        
        echo "文件信息"
        echo "=========="
        echo "总行数: $(wc -l < "$file")"
        echo "列数: $(head -1 "$file" | awk -F"$DELIMITER" '{print NF}')"
        echo "文件大小: $(du -h "$file" | cut -f1)"
        echo ""
        
        echo "列名"
        echo "===="
        head -1 "$file" | awk -F"$DELIMITER" '{for(i=1;i<=NF;i++) print i": "$i}'
    } > "$OUTPUT_FILE"
    
    log_info "分析报告已生成: $OUTPUT_FILE"
}

# 显示帮助
show_help() {
    echo "用法: $0 [选项] <分析类型> [参数]"
    echo ""
    echo "选项:"
    echo "  -i <文件>        输入文件"
    echo "  -o <文件>        输出文件"
    echo "  -d <分隔符>      分隔符(默认: ,)"
    echo "  -H               无表头"
    echo "  -h               显示帮助信息"
    echo ""
    echo "分析类型:"
    echo "  descriptive <列>      描述性统计"
    echo "  frequency <列> [分箱数]  频率分布"
    echo "  correlation <列1> <列2>  相关性分析"
    echo ""
    echo "示例:"
    echo "  $0 -i data.csv descriptive \"age\""
    echo "  $0 -i data.csv frequency \"age\" 10"
    echo "  $0 -i data.csv correlation \"age\" \"salary\""
}

# 主函数
main() {
    # 解析选项
    while getopts "i:o:d:Hh" opt; do
        case $opt in
            i)
                INPUT_FILE="$OPTARG"
                log_info "输入文件: $INPUT_FILE"
                ;;
            o)
                OUTPUT_FILE="$OPTARG"
                log_info "输出文件: $OUTPUT_FILE"
                ;;
            d)
                DELIMITER="$OPTARG"
                log_info "分隔符: $DELIMITER"
                ;;
            H)
                HAS_HEADER=false
                log_info "无表头模式"
                ;;
            h)
                show_help
                exit 0
                ;;
            *)
                log_error "无效选项: $opt"
                show_help
                exit 1
                ;;
        esac
    done
    
    shift $((OPTIND - 1))
    
    # 检查输入文件
    if [ -z "$INPUT_FILE" ]; then
        log_error "缺少输入文件"
        show_help
        exit 1
    fi
    
    if ! check_input_file; then
        exit 1
    fi
    
    # 检查分析类型
    if [ $# -eq 0 ]; then
        log_error "缺少分析类型"
        show_help
        exit 1
    fi
    
    ANALYSIS_TYPE=$1
    shift
    
    # 执行分析
    case $ANALYSIS_TYPE in
        descriptive)
            if [ $# -eq 0 ]; then
                log_error "缺少列名"
                exit 1
            fi
            descriptive_stats "$INPUT_FILE" "$1"
            ;;
        frequency)
            if [ $# -eq 0 ]; then
                log_error "缺少列名"
                exit 1
            fi
            frequency_distribution "$INPUT_FILE" "$1" "${2:-10}"
            ;;
        correlation)
            if [ $# -lt 2 ]; then
                log_error "缺少列名"
                exit 1
            fi
            correlation_analysis "$INPUT_FILE" "$1" "$2"
            ;;
        *)
            log_error "无效的分析类型: $ANALYSIS_TYPE"
            show_help
            exit 1
            ;;
    esac
    
    # 生成报告
    if [ -n "$OUTPUT_FILE" ]; then
        generate_report "$INPUT_FILE"
    fi
}

# 执行主函数
main "$@"

使用说明#

  1. 添加执行权限:

    chmod +x stats_analysis.sh
  2. 基本用法:

    # 描述性统计
    ./stats_analysis.sh -i data.csv descriptive "age"
    
    # 频率分布
    ./stats_analysis.sh -i data.csv frequency "age" 10
    
    # 相关性分析
    ./stats_analysis.sh -i data.csv correlation "age" "salary"
  3. 高级用法:

    # 输出到文件
    ./stats_analysis.sh -i data.csv -o report.txt descriptive "age"
    
    # 指定分隔符
    ./stats_analysis.sh -i data.txt -d ";" descriptive "age"
    
    # 无表头模式
    ./stats_analysis.sh -i data.csv -H descriptive "age"

功能特点#

  • 描述性统计(均值、中位数、众数、方差、标准差等)
  • 频率分布(直方图、分类统计)
  • 相关性分析(皮尔逊相关系数)
  • 多种数据格式支持
  • 详细的统计报告

依赖项#

  • bc: 用于数值计算
  • python3: 用于高级统计计算
  • numpy: 用于统计分析(可选)

注意事项#

  1. 确保数据格式正确
  2. 相关性分析需要数值数据
  3. 大数据集分析可能需要较长时间
  4. 某些统计功能需要numpy
  5. 分箱数影响频率分布的粒度