这个基于Python的求职信息数据分析与可视化系统,是我在指导计算机专业学生毕业设计时开发的一个综合性项目。系统采用Django+Vue全栈架构,整合了机器学习中的随机森林算法,实现了从数据采集、清洗到薪资预测的全流程功能。对于想要学习全栈开发和机器学习应用的同学来说,这个项目涵盖了从后端数据处理到前端可视化展示的完整技术链。
系统最核心的价值在于将机器学习模型真正落地到Web应用中。不同于常见的Jupyter Notebook演示,我们实现了完整的生产级应用,包括用户管理、数据可视化大屏、实时预测等功能模块。特别是在薪资预测模块中,我们不仅提供了预测结果,还给出了置信区间,这对求职者评估薪资范围具有实际参考意义。
系统采用经典的三层架构:
选择这样的技术栈主要基于以下考虑:
系统数据处理流程分为四个关键阶段:
特别注意:在实际项目中,薪资数据的获取和处理需要特别注意合规性。我们采用的技术方案只处理公开的职位薪资范围数据,不涉及个人隐私信息。
随机森林算法的实现是我们项目的核心。以下是模型训练的关键步骤:
python复制# 数据预处理
def prepare_data(self, data):
# 薪资字符串解析(如"15k-20k"转17.5)
def extract_salary(salary_str):
try:
if isinstance(salary_str, str) and '-' in salary_str:
low, high = salary_str.lower().replace('k','').split('-')
return (float(low) + float(high)) / 2
return float(salary_str)
except:
return None
data['salary'] = data['salary'].apply(extract_salary)
data = data.dropna()
# 类别变量编码
data['position_encoded'] = self.le_position.fit_transform(data['position'])
data['city_encoded'] = self.le_city.fit_transform(data['city'])
data['education_encoded'] = self.le_education.fit_transform(data['education'])
return data
# 模型训练
def train(self, data):
data = self.prepare_data(data)
X = data[['position_encoded', 'city_encoded', 'education_encoded']]
y = data['salary']
# 使用100棵决策树构建随机森林
self.model = RandomForestRegressor(
n_estimators=100,
random_state=42,
max_depth=10,
min_samples_split=5
)
self.model.fit(X, y)
模型优化时我们特别注意了以下几点:
不同于简单的点预测,我们提供了预测结果的置信区间:
python复制def predict(self, position, city, education):
position_encoded = self.le_position.transform([position])
city_encoded = self.le_city.transform([city])
education_encoded = self.le_education.transform([education])
X_pred = np.array([position_encoded[0], city_encoded[0],
education_encoded[0]]).reshape(1, -1)
# 获取所有树的预测结果
predictions = []
for estimator in self.model.estimators_:
predictions.append(estimator.predict(X_pred)[0])
# 计算90%置信区间
confidence_interval = np.percentile(predictions, [5, 95])
return {
'avg_salary': round(np.mean(predictions), 2),
'salary_range': f"{round(confidence_interval[0], 2)}k-{round(confidence_interval[1], 2)}k",
'confidence': 90
}
这种方法利用了随机森林的集成特性,通过统计所有决策树的预测结果分布,给出更可靠的薪资范围参考。
前端使用Vue.js结合ECharts实现动态数据可视化。关键实现步骤:
bash复制npm install echarts vue-echarts
javascript复制<template>
<div ref="chart" style="width: 100%; height: 400px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
props: ['option'],
mounted() {
this.initChart();
},
methods: {
initChart() {
const chart = echarts.init(this.$refs.chart);
chart.setOption(this.option);
// 响应式调整
window.addEventListener('resize', () => {
chart.resize();
});
}
}
};
</script>
python复制class SalaryTrendView(APIView):
def get(self, request):
# 从数据库获取各城市薪资趋势数据
trends = SalaryData.objects.values('city') \
.annotate(avg_salary=Avg('salary')) \
.order_by('-avg_salary')[:10]
# 格式化ECharts所需数据
cities = [item['city'] for item in trends]
salaries = [float(item['avg_salary']) for item in trends]
return Response({
'cities': cities,
'salaries': salaries
})
数据可视化大屏采用灵活的网格布局,关键CSS技巧:
css复制.dashboard {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
padding: 20px;
}
.chart-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
padding: 15px;
}
.main-chart {
grid-column: span 2;
grid-row: span 2;
}
针对高并发的预测请求,我们实施了以下优化措施:
python复制# 在Django启动时加载模型
class ModelLoader:
predictor = None
@classmethod
def load_model(cls):
if not cls.predictor:
cls.predictor = SalaryPredictor()
data = pd.read_csv('salary_data.csv')
cls.predictor.train(data)
# 在apps.py中调用
class SalaryConfig(AppConfig):
def ready(self):
ModelLoader.load_model()
python复制from django.core.cache import cache
class SalaryPredictionView(APIView):
def get(self, request):
params = request.query_params
cache_key = f"prediction_{params['position']}_{params['city']}_{params['education']}"
# 尝试从缓存获取
result = cache.get(cache_key)
if not result:
predictor = ModelLoader.predictor
result = predictor.predict(
params['position'],
params['city'],
params['education']
)
# 设置2小时缓存
cache.set(cache_key, result, 7200)
return Response(result)
javascript复制const ChartComponent = () => {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(!showChart)}>
{showChart ? '隐藏图表' : '显示图表'}
</button>
{showChart && (
<LazyChart option={chartOption} />
)}
</div>
);
};
// 使用React.lazy动态加载
const LazyChart = React.lazy(() => import('./ChartComponent'));
javascript复制import { debounce } from 'lodash';
const SearchInput = () => {
const [results, setResults] = useState([]);
const handleSearch = debounce(async (query) => {
const res = await axios.get(`/api/search?q=${query}`);
setResults(res.data);
}, 500);
return (
<div>
<input
type="text"
onChange={(e) => handleSearch(e.target.value)}
/>
<SearchResults data={results} />
</div>
);
};
在实际教学和项目迭代过程中,我们发现以下几个有价值的扩展方向:
对于想要深入学习的朋友,建议从数据采集环节开始完整实现一遍。一个常见的改进点是薪资数据的处理方式 - 我们目前采用简单的范围平均值,更精确的做法可以分别保存最低和最高薪资,在预测时输出两个值。
在部署方面,可以考虑使用Docker容器化部署,特别是模型服务可以单独部署为微服务。以下是一个简单的Dockerfile示例:
dockerfile复制FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "core.wsgi"]
这个项目在技术选型和实现过程中,我们特别注重了以下几点:
对于计算机专业的学生来说,实现这样一个完整项目可以全面锻炼全栈开发能力、数据处理能力和机器学习应用能力,是毕业设计的优质选题。