活动介绍

for(auto&kv:str)

时间: 2024-05-28 15:14:06 浏览: 74
这是一个 C++11 中的 for-each 循环,其中 `str` 是一个容器(比如 `vector`、`array`、`map`、`set` 等)或者是一个数组。`auto&` 表示对于容器中的每个元素,使用引用的方式进行遍历,`kv` 则是每个元素的名称。该循环会遍历容器中的每个元素,并对每个元素执行相应的操作。
相关问题

for(auto kv: map)是什么含义

<think>好的,我现在要解释用户的问题:“for(auto kv: map)是什么含义”。用户之前问过关于unordered_map的问题,现在可能在学习C++中的循环结构,特别是范围for循环。用户可能刚接触C++,需要基础的解释。 首先,我需要确认用户提到的map是什么类型。根据之前的对话,用户使用的是unordered_map<int, string>,所以这里的map应该是一个关联容器。范围for循环在C++11及以后版本中引入,用于遍历容器中的元素。 接下来,分解语句的各个部分。auto关键字用于自动类型推导,kv是循环变量的名称,map是要遍历的容器。需要解释每个部分的作用,以及整个循环的执行过程。 用户可能对auto不熟悉,需要说明auto在这里推导出的类型是什么。对于unordered_map的迭代,每个元素是pair<const Key, Value>,所以kv的类型应该是pair<const int, string>。这时候要提醒用户,如果使用auto&或const auto&可能更高效,避免不必要的拷贝。 然后,解释范围for循环的工作原理,它等同于使用begin()和end()迭代器的传统循环。需要提到在遍历map时,元素的顺序问题,特别是unordered_map是无序的,而普通map是有序的。 用户可能想知道如何访问键和值,所以需要指出使用kv.first和kv.second。同时,提醒在C++17中结构化绑定的用法,可以简化代码,比如for(auto [key, value] : map),这样更清晰。 还要注意可能的常见错误,比如在循环中修改容器导致迭代器失效,或者误以为kv是引用类型。需要建议使用引用或const引用来避免拷贝,特别是当值类型较大时。 最后,总结这个循环结构的优点:简洁、易读,减少错误。同时,结合用户之前的错误,强调作用域和变量声明的位置,确保循环写在函数体内。 可能用户的问题背后是想了解如何遍历map中的元素,所以需要提供完整的示例代码,展示如何正确使用范围for循环,并访问键值对。同时,检查是否需要包含必要的头文件和使用正确的命名空间。 还要注意用户可能混淆了map和unordered_map,虽然语法相似,但遍历时的顺序不同,这点也需要提及。最后,确保解释清晰,逐步引导,帮助用户建立正确的理解。</think>让我们逐步解析`for(auto kv: map)`这个循环结构的含义和用法: --- ### 一、基本结构拆解 ```cpp for(auto kv : map) { // 循环体 } ``` #### 1. 组成部分 - **`auto`**:自动类型推导(C++11特性) - **`kv`**:循环变量名称(自定义标识符) - **`map`**:被遍历的容器(需支持迭代器) #### 2. 等效传统写法 ```cpp for(auto it = map.begin(); it != map.end(); ++it) { auto kv = *it; // 解引用迭代器 // 循环体 } ``` --- ### 二、针对map容器的具体解析 #### 当`map`是`std::map`或`std::unordered_map`时: 1. **元素类型**:`std::pair<const Key, Value>` - `kv.first`:**键**(不可修改) - `kv.second`:**值**(可修改) 2. **示例用法**: ```cpp std::map<int, std::string> m = {{1, "A"}, {2, "B"}}; for(auto kv : m) { std::cout << "Key:" << kv.first << " Value:" << kv.second << std::endl; } ``` --- ### 三、关键细节说明 #### 1. 自动类型推导的实质 - **推导结果**:`kv`的类型是`std::pair<const Key, Value>` - **拷贝问题**:默认会创建副本(可能影响性能) - **优化方案**: ```cpp // 使用引用避免拷贝(推荐) for(auto& kv : map) // 可修改value for(const auto& kv : map) // 不可修改 ``` #### 2. 遍历顺序特征 | 容器类型 | 顺序特性 | |----------|----------| | `std::map` | 按键升序排列 | | `std::unordered_map` | 无固定顺序(哈希表实现) | --- ### 四、C++17增强写法(结构化绑定) 更直观的键值访问方式: ```cpp for(auto& [key, value] : map) { // C++17特性 std::cout << key << " => " << value << std::endl; } ``` --- ### 五、常见错误示例 #### 错误1:尝试修改键 ```cpp for(auto& kv : map) { kv.first = 5; // 错误!key是const的 } ``` #### 错误2:遍历时删除元素 ```cpp for(auto& kv : map) { if(kv.first == 123) map.erase(kv.first); // 导致迭代器失效 } ``` --- ### 六、最佳实践建议 1. **优先使用引用**: ```cpp for(const auto& kv : map) // 只读访问 for(auto& kv : map) // 需要修改value时 ``` 2. **复杂值类型的优化**: ```cpp std::map<int, std::vector<std::string>> bigData; // 避免拷贝vector for(const auto& [key, vec] : bigData) { for(const auto& str : vec) { // 处理字符串 } } ``` --- ### 七、不同类型容器的遍历对比 | 容器类型 | 循环变量类型 | 访问方式 | |----------|--------------|----------| | `vector<T>` | `T` | 直接使用元素 | | `map<K,V>` | `pair<const K,V>` | `kv.first`, `kv.second` | | `set<T>` | `const T` | 直接使用元素(只读) | --- 通过这种范围for循环,开发者可以用更简洁的语法安全地遍历容器,代码可读性显著提高。理解其背后的迭代器机制,能帮助更好地应对复杂场景。

``` #include <iostream> #include <string> #include <map> // 创建函数用于读取输入 std::string read_input() { std::string input; std::getline(std::cin, input); return input; } int main() { // 读取顾客数量 int n; std::cin >> n; std::cin.ignore(); // 使用 std::map 存储订单时间,保证有序 std::map<std::string, int> order_time; // 读取顾客订单时间并存入 std::map for (int i = 0; i < n; ++i) { std::string time = read_input(); // 将订单时间作为键,值为该时间出现的次数 order_time[time]++; } // 初始化免单顾客数量 int free_orders = 0; // 用于存储上一个订单的秒数 std::string prev_second = ""; // 遍历 std::map 中的订单时间 for (const auto& kv : order_time) { std::string time = kv.first; int count = kv.second; // 获取当前订单时间的秒数 std::string current_second = time.substr(0, 19); // 如果当前订单秒数与上一个订单秒数不同,则将当前订单的数量加入免单顾客数 if (current_second != prev_second) { free_orders += count; prev_second = current_second; } } // 输出免单顾客数量 std::cout << free_orders << std::endl; return 0; }```分析代码的功能,并用C语言重新实现

<think>好的,我现在需要分析用户提供的C++代码的功能,并用C语言重新实现它。首先,我得仔细阅读和理解这个C++代码的结构和逻辑。 首先,代码的大致流程是这样的:读取顾客数量n,然后读取n个订单时间,存储到一个std::map中,统计每个时间出现的次数。接着遍历这个map,每当遇到一个新的秒数(即订单时间的前19位),就将该秒数对应的所有订单数量加到免单顾客数量中。最后输出免单总数。 那这个代码的主要功能应该是,对于每个不同的订单时间(精确到秒),统计该秒内的所有订单数量,并将每个秒的第一个出现的订单数量累加,作为免单的数量?或者可能我的理解有误。需要再仔细看循环部分。 在C++代码中,遍历order_time的每个键值对,current_second是取time的前19位,因为时间格式比如可能是"YYYY-MM-DD HH:MM:SS",共19个字符。然后比较current_second和prev_second,如果不同,就将count(即该时间出现的次数)加到free_orders,并更新prev_second。这里的关键是,同一个秒内的多个订单会被视为同一组,每个不同的秒对应的第一个出现的订单数量会被累加。例如,如果某个秒内有多个订单(比如同一秒的不同毫秒,但时间字符串可能不同),那么这些订单会被map自动排序,并且当处理到该秒的第一个条目时,该秒的所有订单数目都会被计入免单。但可能我的理解有误,因为map的键是完整的时间字符串,包括毫秒的话,比如可能有"2023-10-01 12:00:00.123"和"2023-10-01 12:00:00.456",那么它们的current_second是前19位,也就是到秒的部分,即"2023-10-01 12:00:00"。如果这些不同的时间属于同一秒,那么当遍历到第一个这样的时间时,会将该时间的出现次数加到free_orders中,而后续同一秒的其他时间不会被处理,因为它们的current_second与prev_second相同吗? 比如假设订单时间如下: "2023-10-01 12:00:00.123" "2023-10-01 12:00:00.456" "2023-10-01 12:00:01.000" 那么map中的键是按字符串顺序排列的,所以第一个处理的是"2023-10-01 12:00:00.123",此时current_second是"2023-10-01 12:00:00",prev_second是空,所以会加上这个时间的count(假设每个时间出现一次),此时free_orders变为1,prev_second设为该current_second。接下来处理下一个时间"2023-10-01 12:00:00.456",current_second还是同一个,因此不会加到free_orders。然后处理第三个时间,current_second变为下一个秒,所以会加1,总共有2个免单顾客。所以代码的逻辑是,对于每个不同的秒,统计该秒内的第一个时间点出现的订单数量。这可能有问题,或者用户的需求是,每个秒内所有订单中,只免单该秒的第一个订单,还是该秒内的所有订单? 或者原题的逻辑可能不同。例如,题目可能规定,如果同一秒内有多个订单,则这些订单中的第一个可以免单,或者所有同一秒的订单都免单。但根据代码中的处理方式,当遍历到同一秒的第一个时间键时,会将该时间对应的count全部计入免单,而同一秒的后续时间键会被跳过。例如,假设同一秒有三个不同的时间键(比如不同的毫秒),那么在map中这三个时间键会被排序,而处理第一个时,current_second与前一个不同,所以将这三个中的第一个的count加上,而后面两个时间键的current_second和prev_second相同,所以不会被处理。这可能意味着,代码实际上只统计每个秒的第一个时间点的订单数量,而同一秒内的其他时间点的订单不被计入免单。这可能与题意不符,除非题目中的时间精度到秒,而毫秒部分被忽略,但用户输入的时间可能包含更精确的部分,而代码将前19个字符视为秒,即认为同一秒内的所有订单都属于同一秒,不管毫秒。但是,代码中的处理方式是否真的将所有同一秒的订单统计到同一个组里? 例如,假设订单时间都是同一秒的不同毫秒,例如: 时间字符串是"2023-10-01 00:00:00.001","2023-10-01 00:00:00.002"等。这些时间的前19个字符是"2023-10-01 00:00:00",所以current_second相同。此时,在map中,这些时间会被作为不同的键存储。当遍历到第一个这样的时间时,current_second与前一个(初始为空)不同,所以将count(假设为1)加到free_orders。然后prev_second设为该current_second。接下来处理下一个时间,其current_second与prev_second相同,所以不会加到free_orders。这样,同一秒内的多个订单,只有第一个订单的时间被计入免单,其他同一秒的不同时间的订单不会被统计。这可能不是正确的逻辑,但代码确实如此。这可能意味着,题目中的免单条件是同一秒内的第一个订单时间对应的所有订单数量?或者可能是题目中的时间字符串中的前19位是秒级,同一秒内的所有订单都算同一秒,但不管时间后面的部分。而代码中使用map,按时间字符串排序,所以同一秒内的不同时间字符串会被视为不同的键。但处理时,只要current_second与前一个不同,才会计入免单。因此,每个秒的第一个出现的订单时间会被计入免单数量,而同一秒内的其他订单时间(如果有的话)不会被计入。这可能意味着,代码的逻辑存在错误,或者题目中的订单时间可能已经精确到秒,没有更细的精度? 或者可能题目中的订单时间的格式是精确到秒的,即没有毫秒部分,例如时间字符串是19位,例如"2023-10-01 12:00:00",那么每个时间字符串的current_second就是它自己,此时同一秒的订单时间都是相同的,此时map中的每个键对应的count是该秒的订单总数。此时,遍历时每个键的current_second与前一个不同,所以会累加每个秒的订单总数,这样free_orders就等于所有订单的总数,显然不可能。这说明我的理解可能有错误。 或者,可能原题中的订单时间的格式是包含毫秒的,例如"YYYY-MM-DD HH:MM:SS.sss",总长度23位。此时前19位是到秒的部分。代码中将同一秒内的所有订单视为同一组,但map的键是完整的时间字符串,包括毫秒。因此,同一秒内的不同订单可能被存储为不同的键,例如不同的毫秒。此时,遍历这些键时,当处理到第一个属于该秒的订单时间时,会将该时间的出现次数加到free_orders中,而同一秒的其他订单时间会被忽略。这可能不是正确的逻辑,因为同一秒的所有订单应该被合并。例如,同一秒可能有多个订单时间,但它们的current_second相同,但不同的完整时间字符串。这时候代码只会统计第一个出现的订单时间的数量,而同一秒内的其他订单时间的数量不会被统计。这样就会导致免单顾客数量少于实际同一秒内的总订单数,这可能不符合题意。 这说明原C++代码可能存在逻辑错误。或者可能题目中的订单时间已经精确到秒,并且同一秒内的所有订单的时间字符串完全相同,这样map中的键对应的count是该秒的总订单数,遍历时每个秒只会被处理一次,从而free_orders等于所有秒的数量乘以各秒的订单数的总和?这显然不可能,因为每个秒只被处理一次。例如,如果有三个订单时间都是同一秒,那么map中的该键的count是3,处理时会被加到free_orders中,所以free_orders是3。这种情况下,免单顾客数量等于同一秒内订单的数量,这可能符合题意,比如同一秒内的所有订单都免单?或者可能题目中的规则是,每个秒的第一个订单免单,或者同一秒内的所有订单都免单? 现在回到问题,用户提供的代码的功能可能是什么? 假设题目中的规则是,对于每个订单,如果它是其所在秒内的第一个订单,则被免单。或者,同一秒内的所有订单都会被免单。或者,同一秒内的所有订单中,只有第一个出现的订单被免单。这需要根据代码逻辑来推断。 根据代码逻辑,当遍历map中的每个时间键时,只有当current_second(即该时间的前19位)与prev_second不同时,才将该时间的count加到free_orders中。由于map中的键是按时间字符串的顺序排列的,所以同一秒内的不同时间键会被排序在一起吗?例如,假设同一秒的不同毫秒的时间字符串会被排序为连续的条目吗?是的,因为字符串比较是按字典序进行的,例如"2023-10-01 00:00:00.001"和"2023-10-01 00:00:00.002"会被排序在相邻的位置。因此,当处理第一个该秒的时间键时,current_second与前一个不同,于是将其count加到free_orders中,而同一秒内的后续时间键的current_second与prev_second相同,所以不会被处理。因此,free_orders的值等于每个秒的第一个时间键的count之和。这可能意味着,每个秒只免单该秒的第一个时间点的订单数量,而同一秒内的其他时间点的订单数量不被计入。这可能与题意不符,而正确的逻辑应该是同一秒内的所有订单都免单。这种情况下,原代码的逻辑是错误的,或者题目中的订单时间格式到秒,没有更细的精度。 或者,可能订单时间的格式是精确到秒的,没有毫秒部分,此时每个时间字符串的前19位是唯一的,同一秒的所有订单时间字符串完全相同。此时,map中的每个键对应的count是该秒的订单总数,遍历时每个键的current_second与前一个不同,所以每个秒的订单总数都会被加到free_orders中,所以free_orders等于所有订单的总数,这显然不合理。所以这说明可能我的理解有误,或者原代码可能有其他问题。 或者,原题中的规则是,同一秒内的所有订单中,只有第一个订单被免单。例如,如果有三个订单在同一秒,则免单数量是1。而原代码中的处理方式是将同一秒内的所有订单数量加起来,例如,如果同一秒的三个订单的时间相同(比如精确到秒),那么map中的该时间的count是3,此时free_orders +=3。这可能表示该秒内的所有订单都免单。因此,原代码的功能是统计每个不同秒内的订单总数,并将这些总数相加,得到总免单数。这可能意味着,每个秒内的所有订单都被免单,而不管具体时间点。这需要原题中的规则是这样的,比如每天每个秒的第一个订单免单,或者每个秒内的所有订单都免单。 现在,我需要明确原代码的功能。假设订单时间格式是包含毫秒的,例如"YYYY-MM-DD HH:MM:SS.sss",共23个字符。那么前19个字符是到秒的部分。map中的键是按整个字符串排序的。遍历时,当处理到某个时间的current_second与前一个不同时,就将该时间的count加到free_orders中。例如,同一秒内的所有时间键会被处理多次吗?比如,同一秒的不同毫秒的时间会被视为不同的键,但current_second相同。当第一个该秒的时间键被处理时,count被加到free_orders,prev_second被设置为该current_second。之后同一秒内的其他时间键在处理时,current_second等于prev_second,所以不会加。因此,free_orders等于每个秒的第一个时间键的出现次数之和。这可能与题意不符,因为同一秒内的所有订单应该被合并,只加一次,但代码中只加了该秒内第一个时间键的count,而其他时间键的count未被统计。这可能导致免单数量计算错误。 例如,假设同一秒有三个不同的时间键,每个的count都是1。那么这三个时间键会被视为不同的键,但它们的current_second相同。处理第一个时,current_second与前一个不同,加1;处理第二个时,current_second相同,不加;处理第三个时,同样不加。所以该秒的总免单数是1,但实际该秒内的总订单数是3。这说明代码的逻辑存在错误,或者题目中的时间格式没有毫秒部分,从而同一秒内的所有订单时间字符串相同,此时每个秒的count是该秒的总订单数,处理时每个秒只被处理一次,总免单数是所有秒的订单数总和。例如,如果有三个订单在同一个秒,它们的time字符串相同,map中的该键的count是3,处理时会被加3,所以该秒的免单数是3,这可能是题目要求的。 因此,关键问题在于时间字符串的格式。如果时间字符串精确到秒,那么同一秒内的所有订单时间相同,map中该键的count是该秒的总订单数,此时代码中的逻辑是正确的:每个秒对应的键的count会被加到free_orders中。总免单数等于所有秒的订单数总和,即每个秒的所有订单都免单。这可能对应题目中的规则:每个秒内的所有订单都可以免单。例如,某个促销活动,每个秒内的第一个订单免单,但代码中的处理方式似乎不是这样。或者题目中的规则是,每个秒的所有订单都免单,但代码计算的是所有订单的总数,这似乎不太可能。或者可能题目中的规则是,同一秒内如果有多个订单,则这些订单中的第一个可以免单,而代码中的处理方式不正确。或者可能是题目中的规则是,同一秒内的所有订单都可以免单,因此免单数目等于所有订单数目。此时,代码中的处理方式可能有问题,因为当时间精确到秒时,每个秒的订单数目被累加,所以总免单数目等于总订单数目,这可能不合理。但可能题目中的规则是,同一秒内的所有订单都可以免单,因此代码正确。 综上,原C++代码的功能是:统计每个不同的秒(根据时间字符串的前19个字符)的第一个出现的时间点的订单数目之和。这可能与实际需求存在差异,但根据代码,这是它的逻辑。 现在,我需要用C语言重新实现这个逻辑。首先,需要明确C++代码中的各个部分如何转换为C语言。 C++代码中的关键部分: 1. 读取n,然后读取n行时间字符串,存储到std::map<std::string, int> order_time中。这里map的键是时间字符串,值是该字符串出现的次数。由于map是有序的,所以时间字符串按字典序排列。 2. 遍历map中的每个键值对,记录前一个的current_second(时间的前19位),如果当前键的current_second与前一个不同,则将当前键的count加到free_orders中。 在C语言中,没有std::map这样的有序容器,所以需要用其他数据结构。常用的方法是使用动态数组来存储所有不同的时间字符串及其出现次数,然后对数组进行排序,之后遍历处理。 具体步骤: - 读取n,然后读取n个时间字符串,统计每个字符串出现的次数。可以用一个结构体数组来保存时间和次数,或者使用链表。但为了排序,可能需要动态数组,并在读取所有字符串后排序。 - 统计每个时间出现的次数:这可以通过先排序,然后遍历数组,合并相同的字符串,统计次数。或者,在插入时维护一个有序数组,并合并重复项。 但这样效率较低。另一个方法是使用哈希表,但C语言标准库中没有哈希表,需要自己实现或者使用第三方库。但用户可能希望用标准C库实现。 所以可能的步骤: 1. 读取所有时间字符串,存储到一个动态数组中。 2. 对数组进行排序,按照字符串的字典序。 3. 遍历已排序的数组,统计每个字符串的出现次数,并记录到一个结构体数组中,其中每个元素包含时间字符串和出现次数。或者,在遍历时可以即时处理,不需要另外存储次数,只需要在遇到不同字符串时处理前一个字符串。 或者,在遍历已排序的数组时,每当字符串变化时,处理前一个字符串的次数,并比较其前19位是否与前一个处理过的秒数不同,以决定是否累加。 这可能比较复杂,但可行。 例如: - 动态数组times保存所有时间字符串。 - 排序times数组,按strcmp的顺序。 - 初始化prev_second和prev_time为第一个元素的前19位,并统计相同的连续元素的数量。 但这样可能无法直接统计每个时间字符串的总出现次数,因为可能相同的字符串分散在数组中。所以排序后,相同的字符串会相邻,所以可以遍历数组,统计每个相同字符串的出现次数。 然后,将这些统计后的结果按时间顺序处理,并比较前19位的变化。 或者,在排序后的数组中,每次处理一个时间字符串,与前一个比较前19位。如果不同,则累加当前字符串的出现次数到free_orders;如果相同,则不累加。但这样只有在同一秒内的不同时间字符串才会被处理。例如,假设同一秒内有多个不同的时间字符串(如包含毫秒),则它们的排序后的数组中,前19位相同,但字符串不同。当遍历时,第一个字符串的前19位与前一个处理的秒不同,因此累加其出现次数。然后,后面的字符串的前19位与当前秒相同,所以不累加。这样,同一秒内的所有不同的时间字符串中,只有第一个出现的会被计入,这可能与C++代码的逻辑一致。 但这里的问题在于,C++代码中的std::map会将所有时间字符串按顺序排列,每个唯一的时间字符串作为键,统计出现次数。然后遍历时,处理每个键,检查其前19位是否与前一个处理的秒不同。如果是,则累加该键的出现次数。 因此,在C语言中,要做到同样的事情,需要: 1. 将所有时间字符串存储,并统计每个唯一字符串的出现次数,同时按字符串顺序存储这些唯一的字符串。这类似于构建一个有序的map。 这可以通过以下步骤: a. 读取所有时间字符串到数组。 b. 对数组进行排序,按字典序。 c. 遍历排序后的数组,统计每个唯一字符串的出现次数,并记录到一个结构体数组中。结构体包含时间字符串和次数。 这样,结构体数组中的元素相当于std::map中的键值对,按顺序排列。 之后,遍历这个结构体数组,比较每个元素的时间的前19位与前一个处理的秒数,决定是否累加次数到free_orders。 因此,C语言实现的步骤: 1. 读取n,然后读取n个时间字符串,保存到动态数组。 2. 对数组进行排序,使用qsort,比较函数为strcmp。 3. 遍历排序后的数组,统计每个唯一字符串的出现次数,存入另一个结构体数组。例如: struct time_count { char time[100]; // 假设时间字符串不超过100字符 int count; }; 动态分配这个结构体数组,或者在遍历时统计。 例如,初始化current_time为第一个字符串,count=1。然后从第二个开始,比较当前字符串与前一个。如果相同,count增加;否则,添加前一个到结构体数组,并重置current_time和count。最后添加最后一个元素。 4. 然后,遍历结构体数组,维护prev_second字符串。对于每个结构体元素,提取current_second(前19个字符),比较与prev_second。如果不同,则累加count到free_orders,并更新prev_second。 这样,就能模拟C++代码的逻辑。 现在,具体实现需要考虑内存管理,字符串处理等。 可能的代码结构: #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义结构体保存时间和出现次数 typedef struct { char time[100]; // 假设时间字符串足够长 int count; } TimeCount; // 比较函数用于排序时间字符串 int compare_time(const void *a, const void *b) { return strcmp(*(const char **)a, *(const char **)b); } int main() { int n; scanf("%d", &n); getchar(); // 消耗换行符 // 读取所有时间字符串 char **times = malloc(n * sizeof(char *)); for (int i = 0; i < n; i++) { char *time = malloc(100 * sizeof(char)); fgets(time, 100, stdin); time[strcspn(time, "\n")] = '\0'; // 去掉换行符 times[i] = time; } // 排序时间字符串 qsort(times, n, sizeof(char *), compare_time); // 统计每个唯一时间的出现次数 TimeCount *time_counts = malloc(n * sizeof(TimeCount)); int num_unique = 0; if (n > 0) { strcpy(time_counts[0].time, times[0]); time_counts[0].count = 1; num_unique = 1; } for (int i = 1; i < n; i++) { if (strcmp(times[i], times[i-1]) == 0) { // 相同时间,增加计数 time_counts[num_unique-1].count++; } else { // 新时间,添加到结构体数组 strcpy(time_counts[num_unique].time, times[i]); time_counts[num_unique].count = 1; num_unique++; } } // 计算免单数量 int free_orders = 0; char prev_second[20] = ""; // 前19字符+'\0' for (int i = 0; i < num_unique; i++) { char current_second[20]; strncpy(current_second, time_counts[i].time, 19); current_second[19] = '\0'; if (strcmp(current_second, prev_second) != 0) { free_orders += time_counts[i].count; strcpy(prev_second, current_second); } } printf("%d\n", free_orders); // 释放内存 for (int i = 0; i < n; i++) { free(times[i]); } free(times); free(time_counts); return 0; } 需要注意的问题: 1. 时间字符串的长度:假设输入的时间字符串不超过99个字符(包括换行符),所以每个字符串分配100字符。这可能不够,需根据实际情况调整。 2. 读取输入的方式:原C++代码使用std::cin >> n后,用std::cin.ignore()来消耗换行符,然后用std::getline读取每行时间。在C中,使用scanf("%d", &n)后,需要用getchar()来消耗换行符,否则后面的fgets会读取到该换行符。 3. 在统计TimeCount结构体数组时,初始处理需要检查n>0,否则可能出现错误。 4. 提取current_second时,要确保复制19个字符,并添加终止符。 5. 内存管理:需要释放每个times数组中的字符串指针,以及times数组本身,还有time_counts数组。 可能存在的错误或改进点: - 时间字符串可能包含空格或其他字符,但假设输入是正确的,每行一个时间字符串。 - 当时间字符串不足19字符时,strncpy可能导致current_second不正确。但根据原问题,时间字符串的格式应至少包含19个字符(如"YYYY-MM-DD HH:MM:SS"),否则截断会导致问题。原C++代码中的substr(0,19)假设时间字符串足够长,所以C代码中同样假设输入的时间字符串至少19字符,否则可能越界访问。 综上,C语言版本的代码需要按上述逻辑实现,模拟C++中map的有序存储和按顺序处理的过程。</think>以下是代码功能分析及对应的C语言实现: ### 代码功能分析 该代码实现了一个订单免单统计系统,核心逻辑如下: 1. **输入处理**:读取总订单数$n$和所有订单时间 2. **时间统计**:使用有序容器统计每个精确时间出现的次数 3. **免单计算**:遍历统计结果,以秒为粒度进行免单计数(精确到秒的时间段内第一个出现的所有订单免单) 数学表达式描述: $$ free\_orders = \sum_{s \in unique\_seconds} count(first\_timestamp\_in\_s) $$ ### C语言实现 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char timestamp[100]; int count; } TimeEntry; int cmp_str(const void* a, const void* b) { return strcmp(*(const char**)a, *(const char**)b); } int main() { int n; scanf("%d\n", &n); // 读取所有时间戳 char** times = malloc(n * sizeof(char*)); for (int i = 0; i < n; i++) { times[i] = malloc(100 * sizeof(char)); fgets(times[i], 100, stdin); times[i][strcspn(times[i], "\n")] = '\0'; } // 排序时间戳 qsort(times, n, sizeof(char*), cmp_str); // 统计唯一时间戳 TimeEntry* entries = malloc(n * sizeof(TimeEntry)); int entry_cnt = 0; if (n > 0) { strcpy(entries[0].timestamp, times[0]); entries[0].count = 1; entry_cnt = 1; } for (int i = 1; i < n; i++) { if (strcmp(times[i], times[i-1]) == 0) { entries[entry_cnt-1].count++; } else { strcpy(entries[entry_cnt].timestamp, times[i]); entries[entry_cnt].count = 1; entry_cnt++; } } // 计算免单数 int free = 0; char last_sec[20] = ""; for (int i = 0; i < entry_cnt; i++) { char cur_sec[20]; strncpy(cur_sec, entries[i].timestamp, 19); cur_sec[19] = '\0'; if (strcmp(cur_sec, last_sec) != 0) { free += entries[i].count; strcpy(last_sec, cur_sec); } } printf("%d\n", free); // 释放内存 for (int i = 0; i < n; i++) free(times[i]); free(times); free(entries); return 0; } ``` ### 实现说明 1. **内存管理**: - 使用二级指针动态存储时间字符串 - 最后统一释放所有分配的内存 2. **时间复杂度**: - 排序:$O(n \log n)$ - 统计唯一值:$O(n)$ - 总复杂度:$O(n \log n)$ 3. **关键优化**: - 利用排序后的字符串相邻特性进行重复统计 - 通过预处理秒级时间字符串实现快速比对 该实现完整复现了原始C++版本的核心功能,同时遵循C语言的内存管理规范。
阅读全文

相关推荐

这个是源代码:class EnhancedPowerSystem: """增强型电力系统建模类""" def __init__(self): self.net = pp.create_empty_network() self._initialize_network() self._validate_parameters() def _initialize_network(self): """初始化电网组件""" # 创建母线 for i in range(14): pp.create_bus(self.net, vn_kv=BASE_VOLTAGE, name=f"Bus_{i}", index=int(i)) # 创建线路(从配置文件读取线路参数) lines = config["power_system"]["lines"] for idx, line in enumerate(lines): f, t, r, x = line print(f"线路 {idx}: from_bus={f}(类型:{type(f)}), to_bus={t}(类型:{type(t)})") pp.create_line_from_parameters( self.net, from_bus=int(f), # 改为int to_bus=int(t), length_km=1, r_ohm_per_km=r, x_ohm_per_km=x, c_nf_per_km=0, max_i_ka=1.0, name=f"Line_{idx}" ) # 创建发电机和负载 for gen in config["power_system"]["generators"]: gen["bus"] = int(gen["bus"]) # 改为int pp.create_gen(self.net, **gen) for load in config["power_system"]["loads"]: load["bus"] = int(load["bus"]) # 改为int pp.create_load(self.net, **load) def _validate_parameters(self): print("验证元件索引类型:") print(f"- 母线索引类型: {self.net.bus.index.dtype}") print(f"- 线路索引类型: {self.net.line.index.dtype}") print(f"- 负载索引类型: {self.net.load.index.dtype}") print(f"- 发电机索引类型: {self.net.gen.index.dtype}") if any(self.net.line["r_ohm_per_km"] <= 0): raise ValueError("线路电阻必须为正数") # 检查所有元件的索引是否为整数类型 for col in ["bus", "from_bus", "to_bus"]: if col in self.net.line: dtype = self.net.line[col].dtype if "int" not in str(dtype): raise TypeError(f"线路的{col}列类型错误: {dtype}") de

import streamlit as st import pandas as pd import numpy as np import joblib import os import time import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl import matplotlib.font_manager as fm import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, roc_auc_score, confusion_matrix from sklearn.preprocessing import StandardScaler from imblearn.over_sampling import SMOTE from sklearn.impute import SimpleImputer import warnings 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 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: #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 preprocess_loan_data(data_old): “”" 训练时数据预处理函数,返回处理后的数据和推理时需要的参数 参数: data_old: 原始训练数据 (DataFrame) 返回: processed_data: 预处理后的训练数据 (DataFrame) preprocessor_params: 推理时需要的预处理参数 (dict) “”" # 1. 创建原始数据副本 loan_data = data_old.copy() # 2. 保存要删除的列列表 drop_list = [‘id’,‘member_id’, ‘term’, ‘pymnt_plan’, ‘initial_list_status’, ‘sub_grade’, ‘emp_title’, ‘issue_d’, ‘title’, ‘zip_code’, ‘addr_state’, ‘earliest_cr_line’, ‘last_pymnt_d’, ‘last_credit_pull_d’, ‘url’,‘desc’,‘next_pymnt_d’] loan_data.drop([col for col in drop_list if col in loan_data.columns], axis=1, inplace=True, errors=‘ignore’) # 3. 删除缺失值超过90%的列 #todo 自己补齐删除代码 missing_ratio = loan_data.isnull().sum() / len(loan_data) loan_data.drop(missing_ratio[missing_ratio > 0.9].index, axis=1, inplace=True, errors=‘ignore’) # 4. 删除值全部相同的列 #todo 自己补齐删除代码 constant_cols = loan_data.columns[loan_data.nunique() <= 1] loan_data.drop(constant_cols, axis=1, inplace=True, errors=‘ignore’) # 5. 处理特殊数值列 loans = loan_data # 修正变量名 loans[“int_rate”] = loans[“int_rate”].astype(str).str.rstrip(‘%’).astype(“float”) loans[“revol_util”] = loans[“revol_util”].astype(str).str.rstrip(‘%’).astype(“float”) # 6. 缺失值处理 ## 识别分类列和数值列 objectColumns = loans.select_dtypes(include=[“object”]).columns.tolist() numColumns = loans.select_dtypes(include=[np.number]).columns.tolist() ## 保存分类列的列名 categorical_columns = objectColumns.copy() ## 填充分类变量缺失值 loans[objectColumns] = loans[objectColumns].fillna(“Unknown”) ## 填充数值变量缺失值并保存均值 imr = SimpleImputer(missing_values=np.nan, strategy=“mean”) loans[numColumns] = imr.fit_transform(loans[numColumns]) # 保存数值列的均值 numerical_means = {col: imr.statistics_[i] for i, col in enumerate(numColumns)} # 8. 特征衍生 loans[“installment_feat”] = loans[“installment”] / ((loans[“annual_inc”] + 1) / 12) # 9. 目标变量编码 status_mapping = { “Current”: 0, “Issued”: 0, “Fully Paid”: 0, “In Grace Period”: 1, “Late (31-120 days)”: 1, “Late (16-30 days)”: 1, “Charged Off”: 1, “Does not meet the credit policy. Status:Charged Off”: 1, “Does not meet the credit policy. Status:Fully Paid”: 0, “Default”: 0 } loans[“loan_status”] = loans[“loan_status”].map(status_mapping) # 10. 有序特征映射 mapping_dict = { “emp_length”: { “10+ years”: 10, “9 years”: 9, “8 years”: 8, “7 years”: 7, “6 years”: 6, “5 years”: 5, “4 years”: 4, “3 years”: 3, “2 years”: 2, “1 year”: 1, “< 1 year”: 0, “Unknown”: 0 }, “grade”: { “A”: 1, “B”: 2, “C”: 3, “D”: 4, “E”: 5, “F”: 6, “G”: 7 } } loans = loans.replace(mapping_dict) # 11. One-hot编码 n_columns = [“home_ownership”, “verification_status”, “purpose”, “application_type”] dummy_df = pd.get_dummies(loans[n_columns], drop_first=False) loans = pd.concat([loans, dummy_df], axis=1) loans.drop(n_columns, axis=1, inplace=True) # 保存One-hot编码后的列名 onehot_columns = n_columns onehot_encoder_columns = dummy_df.columns.tolist() # 12. 特征缩放 # 识别需要缩放的数值列 numeric_cols = loans.select_dtypes(include=[“int”, “float”]).columns.tolist() if ‘loan_status’ in numeric_cols: numeric_cols.remove(‘loan_status’) # 创建并拟合缩放器 sc = StandardScaler() if numeric_cols: loans[numeric_cols] = sc.fit_transform(loans[numeric_cols]) # 保存缩放列名 scaled_columns = numeric_cols # 13. 保存最终列结构(在SMOTE之前) #final_columns = loans.columns.tolist().remove(‘loan_status’) final_columns = loans.columns[loans.columns != ‘loan_status’].tolist() # 14. 处理不平衡数据(SMOTE过采样) X = loans.drop(“loan_status”, axis=1) y = loans[“loan_status”] os = SMOTE(random_state=42) X_res, y_res = os.fit_resample(X, y) # 15. 合并为最终DataFrame processed_data = pd.concat([X_res, y_res], axis=1) processed_data.columns = list(X.columns) + [“loan_status”] # 16. 创建推理时需要的参数字典 preprocessor_params = { # 1. 删除的列 ‘drop_list’: drop_list, # 2. 分类列缺失值填充 ‘categorical_columns’: categorical_columns, # 3. 数值列填充均值 ‘numerical_means’: numerical_means, # 4. 有序特征映射 ‘mapping_dict’: mapping_dict, # 5. One-hot配置 ‘onehot_columns’: onehot_columns, ‘onehot_encoder_columns’: onehot_encoder_columns, # 6. 缩放器及缩放列 ‘scaler’: sc, # 已拟合的StandardScaler实例 ‘scaled_columns’: scaled_columns, # 7. 最终列结构(训练后的列顺序) ‘final_columns’: final_columns } return processed_data, preprocessor_params def preprocess_loan_data_inference(data_old, preprocessor_params): “”" 推理时数据处理函数 参数: data_old: 原始推理数据 (DataFrame) preprocessor_params: 从训练过程保存的预处理参数 (dict) 返回: processed_data: 预处理后的推理数据 (DataFrame) “”" # 1. 复制数据避免污染原始数据 loanss = data_old.copy() # 2. 删除训练时确定的列 drop_list = preprocessor_params[‘drop_list’] loans = loanss.drop(columns=[col for col in drop_list if col in loanss.columns], axis=1, errors=‘ignore’) # 3. 处理特殊数值列(百分比转换) if ‘int_rate’ in loans: loans[“int_rate”] = loans[“int_rate”].astype(str).str.rstrip(‘%’).astype(“float”) if ‘revol_util’ in loans: loans[“revol_util”] = loans[“revol_util”].astype(str).str.rstrip(‘%’).astype(“float”) # 4. 特征衍生(使用训练时相同公式) if ‘installment’ in loans and ‘annual_inc’ in loans: loans[“installment_feat”] = loans[“installment”] / ((loans[“annual_inc”] + 1) / 12) # 5. 有序特征映射(使用训练时的映射字典) mapping_dict = preprocessor_params[‘mapping_dict’] for col, mapping in mapping_dict.items(): if col in loans: # 处理未知值,默认为0 loans[col] = loans[col].map(mapping).fillna(0).astype(int) # 6. 缺失值处理(使用训练时保存的策略) # 分类变量 cat_cols = preprocessor_params[‘categorical_columns’] for col in cat_cols: if col in loans: loans[col] = loans[col].fillna(“Unknown”) # 数值变量(使用训练时保存的均值) num_means = preprocessor_params[‘numerical_means’] for col, mean_value in num_means.items(): if col in loans: loans[col] = loans[col].fillna(mean_value) # 7. One-hot编码(对齐训练时的列结构) n_columns = preprocessor_params[‘onehot_columns’] expected_dummy_columns = preprocessor_params[‘onehot_encoder_columns’] # 创建空DataFrame用于存储结果 dummy_df = pd.DataFrame(columns=expected_dummy_columns) # 为每个分类列生成dummy变量 for col in n_columns: if col in loans: # 为当前列生成dummies col_dummies = pd.get_dummies(loans[col], prefix=col) # 对齐训练时的列结构 for expected_col in expected_dummy_columns: if expected_col in col_dummies: dummy_df[expected_col] = col_dummies[expected_col] else: # 如果该列不存在,则创建全0列 dummy_df[expected_col] = 0 # 合并dummy变量 loans = pd.concat([loans, dummy_df], axis=1) # 删除原始分类列 loans.drop(columns=[col for col in n_columns if col in loans.columns], inplace=True, errors=‘ignore’) # 8. 特征缩放(使用训练时的缩放器参数) sc = preprocessor_params[‘scaler’] scaled_cols = [col for col in preprocessor_params[‘scaled_columns’] if col in loans.columns] if scaled_cols: loans[scaled_cols] = sc.transform(loans[scaled_cols]) # 9. 对齐最终特征列(确保与训练数据相同) final_columns = preprocessor_params[‘final_columns’] # 添加缺失列(用0填充) for col in final_columns: if col not in loans.columns: loans[col] = 0 # 移除多余列并保持顺序 processed_data = loans[final_columns] print(loans.columns) return processed_data 标题区域 st.markdown(“”" 风控违约预测系统 基于机器学习的信贷风险评估与预测 """, unsafe_allow_html=True) 页面布局 col1, col2 = st.columns([1, 1.5]) 左侧区域 - 图片和简介 with col1: st.markdown(“”" 智能风控系统 利用先进机器学习技术预测信贷违约风险 “”", unsafe_allow_html=True) 使用在线图片作为占位符 st.image(“https://images.unsplash.com/photo-1553877522-43269d4ea984?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80”, caption=“智能风控系统示意图”, use_column_width=True) st.markdown(“”" 📈 系统功能 客户违约风险预测 高精度机器学习模型 可视化风险评估 批量数据处理 “”", unsafe_allow_html=True) 右侧区域 - 功能选择 with col2: st.markdown(“”" 请选择操作类型 您可以选择训练新模型或使用现有模型进行预测 “”", unsafe_allow_html=True) 功能选择 option = st.radio(“”, [“🚀 训练新模型 - 使用新数据训练预测模型”, “🔍 推理预测 - 使用模型预测违约风险”], index=0, label_visibility=“hidden”) # 模型训练部分 if “训练新模型” in option: st.markdown(“”" 模型训练 上传训练数据并训练新的预测模型 “”“, unsafe_allow_html=True) # 上传训练数据 train_file = st.file_uploader(“上传训练数据 (CSV格式)”, type=[“csv”]) if train_file is not None: try: # 读取数据 train_data_old = pd.read_csv(train_file) # 显示数据预览 with st.expander(“数据预览”, expanded=True): st.dataframe(train_data_old.head()) col1, col2, col3 = st.columns(3) col1.metric(“总样本数”, train_data_old.shape[0]) col2.metric(“特征数量”, train_data_old.shape[1] - 1) # 训练参数设置 st.subheader(“训练参数”) col1, col2 = st.columns(2) test_size = col1.slider(“测试集比例”, 0.1, 0.4, 0.2, 0.1) n_estimators = col2.slider(“树的数量”, 10, 500, 100, 10) max_depth = col1.slider(“最大深度”, 2, 30, 10, 1) random_state = col2.number_input(“随机种子”, 0, 100, 42) # 开始训练按钮 if st.button(“开始训练模型”, use_container_width=True): with st.spinner(“模型训练中,请稍候…”): # 模拟数据处理 progress_bar = st.progress(0) train_data,preprocessor_params = preprocess_loan_data(train_data_old) joblib.dump(preprocessor_params, ‘loan_preprocessor_params.pkl’) # 步骤1: 数据预处理 time.sleep(1) progress_bar.progress(25) st.success(”✅ 数据预处理完成") # 步骤2: 特征工程 time.sleep(1) progress_bar.progress(50) st.success(“✅ 特征工程完成”) # 步骤3: 模型训练 time.sleep(2) progress_bar.progress(75) # 实际训练代码 (简化版) X = train_data.drop(“loan_status”, axis=1) y = train_data[“loan_status”] # 划分训练测试集 #todo 自己补齐数据划分代码 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state, stratify=y) # 训练模型 #todo 自己补齐调用随机森林算法完成模型的训练 model = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth, random_state=random_state, n_jobs=-1) model.fit(X_train, y_train) # 保存模型 joblib.dump(model, “risk_model.pkl”) # 步骤4: 模型评估 time.sleep(1) progress_bar.progress(100) # 评估模型 #todo 自己补齐调用预测函数完成测试集推理预测 y_pred = model.predict(X_test) y_proba = model.predict_proba(X_test)[:, 1] accuracy = accuracy_score(y_test, y_pred) auc = roc_auc_score(y_test, y_proba) # 保存评估结果 st.session_state.model_trained = True st.session_state.accuracy = accuracy st.session_state.auc = auc st.session_state.y_test = y_test st.session_state.y_pred = y_pred st.success(“🎉 模型训练完成!”) # 显示模型性能 st.subheader(“模型性能评估”) col1, col2 = st.columns(2) col1.markdown(f"“” {accuracy*100:.1f}% 准确率 “”“, unsafe_allow_html=True) col2.markdown(f”“” {auc:.3f} AUC 分数 “”“, unsafe_allow_html=True) # 混淆矩阵 st.subheader(“混淆矩阵”) cm = confusion_matrix(y_test, y_pred) fig, ax = plt.subplots(figsize=(6, 4)) sns.heatmap(cm, annot=True, fmt=“d”, cmap=“Blues”, ax=ax) ax.set_xlabel(“预测标签”) ax.set_ylabel(“真实标签”) ax.set_title(“混淆矩阵”) st.pyplot(fig) # 特征重要性 st.subheader(“特征重要性”) feature_importance = pd.DataFrame({ “特征”: X.columns, “重要性”: model.feature_importances_ }).sort_values(“重要性”, ascending=False).head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x=“重要性”, y=“特征”, data=feature_importance, palette=“viridis”, ax=ax) ax.set_title(“Top 10 重要特征”) st.pyplot(fig) except Exception as e: st.error(f"数据处理错误: {str(e)}”) # 推理预测部分 else: st.markdown(“”" 风险预测 上传需要预测的数据,生成违约风险评估报告 “”“, unsafe_allow_html=True) # 上传预测数据 predict_file = st.file_uploader(“上传预测数据 (CSV格式)”, type=[“csv”]) if predict_file is not None: try: # 读取数据 predict_data = pd.read_csv(predict_file) # 显示数据预览 with st.expander(“数据预览”, expanded=True): st.dataframe(predict_data.head()) st.info(f"数据集包含 {predict_data.shape[0]} 个样本,{predict_data.shape[1]} 个特征”) # 检查是否有模型 if not os.path.exists(“risk_model.pkl”): st.warning(“⚠️ 未找到训练好的模型,请先训练模型或使用示例数据”) # 使用示例模型 if st.button(“使用示例模型进行预测”, use_container_width=True): st.info(“正在使用预训练的示例模型进行预测…”) # 创建示例模型 X = np.random.rand(100, 10) y = np.random.randint(0, 2, 100) model = RandomForestClassifier(n_estimators=50, random_state=42) model.fit(X, y) # 生成预测结果 predictions = model.predict(predict_data.values) probas = model.predict_proba(predict_data.values)[:, 1] # 创建结果DataFrame result_df = pd.DataFrame({ “客户ID”: predict_data[“member_id”], “违约概率”: probas, “预测标签”: predictions }) # 添加风险等级 result_df[“风险等级”] = pd.cut( result_df[“违约概率”], bins=[0, 0.2, 0.5, 1], labels=[“低风险”, “中风险”, “高风险”], include_lowest=True ) # 保存结果 st.session_state.prediction_results = result_df else: # 加载模型 model = joblib.load(“risk_model.pkl”) preprocessor_params = joblib.load(‘loan_preprocessor_params.pkl’) # 开始预测按钮 if st.button(“开始风险预测”, use_container_width=True): with st.spinner(“预测进行中,请稍候…”): # 模拟预测过程 progress_bar = st.progress(0) # 预处理推理数据 #todo 自己补齐调用推理数据处理函数完成推理数据的清洗 processed_inference = preprocess_loan_data_inference(predict_data, preprocessor_params) # 步骤1: 数据预处理 time.sleep(1) progress_bar.progress(25) # 步骤2: 特征工程 time.sleep(1) progress_bar.progress(50) # 步骤3: 模型预测 time.sleep(1) progress_bar.progress(75) # 生成预测结果 predictions = model.predict(processed_inference.values) probas = model.predict_proba(processed_inference.values)[:, 1] # 创建结果DataFrame result_df = pd.DataFrame({ “客户ID”: predict_data[“member_id”], “违约概率”: probas, “预测标签”: predictions }) # 添加风险等级 result_df[“风险等级”] = pd.cut( result_df[“违约概率”], bins=[0, 0.2, 0.5, 1], labels=[“低风险”, “中风险”, “高风险”], include_lowest=True ) # 步骤4: 生成报告 time.sleep(1) progress_bar.progress(100) # 保存结果 st.session_state.prediction_results = result_df st.success(“✅ 预测完成!”) except Exception as e: st.error(f"预测错误: {str(e)}“) # 显示预测结果 if “prediction_results” in st.session_state: st.markdown(”“” 预测结果 客户违约风险评估报告 “”“, unsafe_allow_html=True) result_df = st.session_state.prediction_results # 风险分布 st.subheader(“风险分布概览”) col1, col2, col3 = st.columns(3) high_risk = (result_df[“风险等级”] == “高风险”).sum() med_risk = (result_df[“风险等级”] == “中风险”).sum() low_risk = (result_df[“风险等级”] == “低风险”).sum() col1.markdown(f”“” {high_risk} 高风险客户 “”“, unsafe_allow_html=True) col2.markdown(f”“” {med_risk} 中风险客户 “”“, unsafe_allow_html=True) col3.markdown(f”“” {low_risk} 低风险客户 “”“, unsafe_allow_html=True) # 风险分布图 fig, ax = plt.subplots(figsize=(8, 4)) risk_counts = result_df[“风险等级”].value_counts() risk_counts.plot(kind=“bar”, color=[”#4CAF50", “#FFC107”, “#F44336”], ax=ax) ax.set_title(“客户风险等级分布”) ax.set_xlabel(“风险等级”) ax.set_ylabel(“客户数量”) st.pyplot(fig) # 详细预测结果 st.subheader(“详细预测结果”) # 样式函数 def color_risk(val): if val == “高风险”: return “background-color: #ffcdd2; color: #c62828;” elif val == “中风险”: return “background-color: #fff9c4; color: #f57f17;” else: return “background-color: #c8e6c9; color: #388e3c;” # 格式化显示 styled_df = result_df.style.applymap(color_risk, subset=[“风险等级”]) st.dataframe(styled_df.format({ “违约概率”: “{:.2%}” }), height=400) # 下载结果 csv = result_df.to_csv(index=False).encode(“utf-8”) st.download_button( label=“下载预测结果”, data=csv, file_name=“风险预测结果.csv”, mime=“text/csv”, use_container_width=True ) 页脚 st.markdown(“—”) st.markdown(“”" © 2023 风控违约预测系统 | 基于Streamlit开发 """, unsafe_allow_html=True) 根据如上代码,仿照如下要求,给出结果完整代码 大数据挖掘:精准营销 一、题目背景 某电信运营商为提升用户 ARPU(每用户平均收入),计划对单宽带用户推广 “单宽转融” 业务(即单宽带用户加装移动网业务,形成融合套餐)。为实现精准营销,需通过数据挖掘技术预测单宽带用户转化为融合套餐用户的可能性,从而针对性制定营销策略。现有一批单宽带用户的行为数据,要求通过数据分析和建模,构建高效的预测模型,辅助运营决策。 二、数据集介绍 1、数据来源:某运营商单宽转融用户的历史数据,包含用户基础信息、资费信息、电信行为数据、客户标签及 DPI 上网行为数据。 2、数据规模:50万+条记录,100+个字段。 3、关键字段说明: 1)用户属性:AGE(年龄),GENDER(性别),ONLINE_DAY(在网天数) 2)消费行为:STMT_AMT(出账金额),PROM_AMT(套餐价格),AVG_STMT_AMT(月均消费) 3)网络使用:DUR(上网时长),DWN_VOL(下载流量),TERM_CNT(接入终端数) 4)业务标签:IF_YHTS(是否投诉),MKT_STAR_GRADE_NAME(用户星级) 5)目标变量(标签):is_rh_next,表示用户是否转为融合套餐(1 为转化,0 为未转化)。 三、题目要求 1、使用 Python 进行数据分析与预处理: 1)加载数据并检查数据质量(缺失值、异常值)。 2)进行特征工程:删除无意义特征、处理缺失值、离散特征编码、标准化 / 归一化。 3)可视化数据分布,分析关键特征与目标变量的相关性。 2、使用 Spark 进行模型训练与测试: 1)构建逻辑回归、决策树、随机森林三种模型。 2)调优模型参数,对比评估指标(准确率、召回率、F1 值、AUC)。 3)选择最优模型,并解释特征重要性。 3、输出要求: 1)给出数据预处理的关键步骤及代码。 2)展示各模型的训练结果与对比分析。 3)说明最终选择的模型及理由。 数据集文件名为Single_breadth_to_melt.csv 文件为gbk编码前一百行数据为 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 … AVG_STMT_AMT_LV is_kdts is_itv_up is_mobile_up if_zzzw_up itv_cnt itv_day serv_in_time PROM_AMT_MONTH is_rh_next 0 201706 1-1E6Z49HF 1-UTSNWVU 杭州 NaN 其它部门-未知部门细分-未知 … 0 20140126 现行 普通宽带 … c30-59 0 0 0 0 0 0 41 44.44 0.0 1 201706 3-J591KYI 1-LKFKET 杭州 NaN 其它部门-未知部门细分-未知 … 0 20160406 现行 普通宽带 … e89-129 0 0 0 0 0 0 14 100.00 0.0 2 201706 1-F3YGP4D 1-6T16M75 杭州 营业厅 营业厅-营业服务中心-城市 … 0 20100112 现行 普通宽带 … c30-59 0 0 0 0 0 28 89 44.44 0.0 3 201706 1-1AITRLCN 1-1AB5KV9U 杭州 NaN 其它部门-未知部门细分-未知 … 0 20131017 现行 普通宽带 … c30-59 1 0 0 0 0 10 44 55.56 0.0 4 201706 1-132ZSIVX 1-LPVY5O 杭州 10000号 其它部门-10000客服部-城市 … 0 20130209 现行 普通宽带 … d59-89 0 0 0 0 0 0 52 0.00 0.0

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 matplotlib as mpl import matplotlib.font_manager as fm 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 pyspark.sql.functions import when, col 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 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 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: #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 preprocess_data(ddf): """ 使用Dask进行大数据预处理 参数: ddf (dask.DataFrame): 原始数据 返回: processed_ddf (dask.DataFrame): 处理后的数据 feature_cols (list): 特征列名列表 """ # 创建副本以避免修改原始数据 processed_ddf = ddf.copy() # 1. 删除无意义特征 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) # 2. 处理缺失值 - 使用Dask的延迟计算 # 数值型特征用均值填充 numeric_cols = processed_ddf.select_dtypes(include=[np.number]).columns.tolist() if 'is_rh_next' in numeric_cols: numeric_cols.remove('is_rh_next') # 计算均值 - 使用Dask的延迟计算 with ProgressBar(): means = processed_ddf[numeric_cols].mean().compute() # 填充缺失值 - 使用map_partitions分块处理 for col in numeric_cols: processed_ddf[col] = processed_ddf[col].fillna(means[col]) # 类型转换 - 将整数列转换为支持缺失值的Int64类型 for col in numeric_cols: if processed_ddf[col].dtype == 'float64' and processed_ddf[col].dropna().apply(float.is_integer).all(): # 如果所有值都是整数,转换为Int64(支持缺失值) processed_ddf[col] = processed_ddf[col].astype('float').fillna(0).astype('Int64') elif processed_ddf[col].dtype == 'float64': # 保留为float64 processed_ddf[col] = processed_ddf[col].astype('float64') # 类别型特征用"Unknown"填充 object_cols = processed_ddf.select_dtypes(include=['object']).columns.tolist() for col in object_cols: processed_ddf[col] = processed_ddf[col].fillna("Unknown") # 3. 离散特征编码 # 对二元特征进行简单映射 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}) # 对性别进行映射 if 'GENDER' in processed极ddf.columns: gender_mapping = {'男': 0, '女': 1, 'Unknown': -1} processed_ddf['GENDER'] = processed_ddf['GENDER'].map(gender_mapping) # 4. 用户星级映射 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) # 5. 特征工程 - 使用Dask的延迟计算 # 计算消费比率(套餐价格/出账金额) if 'PROM_AM极T' in processed_ddf.columns and 'STMT_AMT' in processed_ddf.columns: processed_ddf['CONSUMPTION_RATIO'] = processed_ddf['PROM_AMT'] / (processed_ddf['STMT_AMT'] + 1) # 计算流量使用密度(下载流量/在网天数) if 'DWN_VOL' in processed_ddf.columns and 'ONLINE_DAY' in processed_ddf.columns: processed_ddf['TRAFFIC_DENSITY'] = processed_ddf['DWN_VOL'] / (processed_ddf['ONLINE_DAY'] + 1) # 是否有终端设备 if 'TERM_CNT' in processed_ddf.columns: processed_ddf['HAS_TERMINAL'] = (processed_ddf['TERM_CNT'] > 0).astype(int) # 6. 标准化处理 - 使用Dask的StandardScaler scaler = DaskStandardScaler() numeric_cols_for_scaling = list(set(numeric_cols) - set(['is_rh_next'])) if len(numeric_cols_for_scaling) > 0: # 使用Dask的scaler进行分块标准化 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_df (pyspark.sql.DataFrame): 处理后的数据 feature_cols (list): 特征列名列表 返回: results (dict): 包含训练好的模型及其性能指标 """ # 初始化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) # 定义评估器 lr = LogisticRegression(featuresCol="features", labelCol="is_rh_next") dt = DecisionTreeClassifier(featuresCol="features", labelCol="is_rh_next") rf = RandomForestClassifier(featuresCol="features", labelCol="is_rh_next", numTrees=10) # 定义参数网格 lr_param_grid = ParamGridBuilder() \ .addGrid(lr.regParam, [0.01, 0.1]) \ .addGrid(lr.elasticNetParam, [0.0, 0.5]) \ .build() dt_param_grid = ParamGridBuilder() \ .addGrid(dt.maxDepth, [5, 10]) \ .addGrid(dt.minInstancesPerNode, [10, 20]) \ .build() rf_param_grid = ParamGridBuilder() \ .addGrid(rf.numTrees, [10, 20]) \ .addGrid(rf.maxDepth, [5, 10]) \ .build() # 定义交叉验证器 evaluator = BinaryClassificationEvaluator(labelCol="is_rh_next", metricName="areaUnderROC") lr_cv = CrossValidator(estimator=lr, estimatorParamMaps=lr_param_grid, evaluator=evaluator, numFolds=3) dt_cv = CrossValidator(estimator=dt, estimatorParamMaps=dt_param_grid, evaluator=evaluator, numFolds=3) rf_cv = CrossValidator(estimator=rf, estimatorParamMaps=rf_param_grid, evaluator=evaluator, numFolds=3) # 训练模型 results = {} # 逻辑回归 with st.spinner("正在训练逻辑回归模型..."): lr_model = lr_cv.fit(train_df) lr_predictions = lr_model.transform(test_df) lr_auc = evaluator.evaluate(lr_predictions) lr_accuracy = lr_predictions.filter(lr_predictions.is_rh_next == lr_predictions.prediction).count() / test_df.count() results["logistic_regression"] = { "model": lr_model, "auc": lr_auc, "accuracy": lr_accuracy, "best_params": lr_model.bestModel._java_obj.parent().extractParamMap() } # 决策树 with st.spinner("正在训练决策树模型..."): dt_model = dt_cv.fit(train_df) dt_predictions = dt_model.transform(test_df) dt_auc = evaluator.evaluate(dt_predictions) dt_accuracy = dt_predictions.filter(dt_predictions.is_rh_next == dt_predictions.prediction).count() / test_df.count() results["decision_tree"] = { "model": dt_model, "auc": dt_auc, "accuracy": dt_accuracy, "best_params": dt_model.bestModel._java_obj.parent().extractParamMap(), "feature_importances": dt_model.bestModel.featureImportances.toArray().tolist() } # 随机森林 with st.spinner("正在训练随机森林模型..."): rf_model = rf_cv.fit(train_df) rf_predictions = rf_model.transform(test_df) rf_auc = evaluator.evaluate(rf_predictions) rf_accuracy = rf_predictions.filter(rf_predictions.is_rh_next == rf_predictions.prediction).count() / test_df.count() results["random_forest"] = { "model": rf_model, "auc": rf_auc, "accuracy": rf_accuracy, "best_params": rf_model.bestModel._java_obj.parent().extractParamMap(), "feature_importances": rf_model.bestModel.featureImportances.toArray().tolist() } return results # 标题区域 st.markdown(""" 单宽转融用户预测系统 基于大数据挖掘的精准营销分析平台 """, unsafe_allow_html=True) # 页面布局 col1, col2 = st.columns([1, 1.5]) # 左侧区域 - 图片和简介 with col1: st.markdown(""" 精准营销系统 利用先进数据挖掘技术识别潜在融合套餐用户 """, 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) st.markdown(""" 📈 系统功能 用户转化预测 多模型对比分析 特征重要性分析 可视化数据洞察 """, unsafe_allow_html=True) # 右侧区域 - 功能选择 with col2: st.markdown(""" 请选择操作类型 您可以选择训练新模型或查看现有模型分析结果 """, unsafe_allow_html=True) # 功能选择 option = st.radio("", ["🚀 训练新模型 - 使用新数据训练预测模型", "🔍 模型分析 - 查看现有模型的分析结果"], index=0, label_visibility="hidden") # 模型训练部分 if "训练新模型" in option: st.markdown(""" 模型训练 上传训练数据并训练新的预测模型 """, 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}") # 使用Dask分块读取大文件 chunksize = 256 * 1024 * 1024 # 256MB每块 # 定义缺失值列表 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'] # 尝试读取文件 try: raw_ddf = dd.read_csv( tmp_path, blocksize=chunksize, dtype={'is_rh_next': 'float64'}, # 使用float64避免整数缺失问题 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={'is_rh_next': 'float64'}, 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={'is_rh_next': 'float64'}, encoding='latin-1', na_values=na_values_list, assume_missing=True, low_memory=False ) # 显示数据预览 with st.expander("数据预览", expanded=True): # 只显示前1000行 try: preview_data = raw_ddf.head(1000) st.dataframe(preview_data) col1, col2 = st.columns(2) col1.metric("总样本数", f"{raw_ddf.shape[0].compute():,}") col2.metric("特征数量", len(raw_ddf.columns)) # 检查目标变量是否存在 if 'is_rh_next' not in raw_ddf.columns: st.warning("⚠️ 注意:未找到目标变量 'is_rh_next'") except Exception as e: st.error(f"数据预览错误: {str(e)}") st.write("尝试显示前100行...") preview_data = raw_ddf.head(100) st.dataframe(preview_data) # 数据预处理按钮 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("数据质量检查") # 使用Dask计算缺失值 with st.spinner("计算缺失值统计..."): null_counts = processed_ddf.isnull().sum().compute() st.write("缺失值统计:") st.write(null_counts[null_counts > 0]) # 可视化关键特征分布 st.subheader("关键特征分布") # 使用Dask计算采样数据 sample_ddf = processed_ddf.sample(frac=0.1) # 采样10%的数据 sample_df = sample_ddf.compute() fig, axes = plt.subplots(2, 2, figsize=(12, 10)) sns.histplot(sample_df['AGE'], ax=axes[0, 0], kde=True) sns.histplot(sample_df['ONLINE_DAY'], ax=axes[0, 1], kde=True) sns.histplot(sample_df['PROM_AMT'], ax=axes[1, 0], kde=True) sns.histplot(sample_df['DWN_VOL'], ax=axes[1, 1], kde=True) plt.tight_layout() st.pyplot(fig) # 目标变量分布 st.subheader("目标变量分布") 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) # 特征与目标变量相关性 st.subheader("特征与转化的相关性") with st.spinner("计算特征相关性..."): # 使用采样数据计算相关性 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) # 模型训练按钮 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['logistic_regression']['accuracy'], results['decision_tree']['accuracy'], results['random_forest']['accuracy']], 'AUC': [results['logistic_regression']['auc'], results['decision_tree']['auc'], results['random_forest']['auc']] }).sort_values('AUC', ascending=False) st.table(model_performance.style.format({ '准确率': '{:.2%}', 'AUC': '{:.4f}' })) # 最佳模型特征重要性 best_model_name = model_performance.iloc[0]['模型'] model_map = { '逻辑回归': 'logistic_regression', '决策极': 'decision_tree', '随机森林': 'random_forest' } best_model_key = model_map[best_model_name] best_model = results[best_model_key]['model'].bestModel st.subheader(f"最佳模型 ({best_model_name}) 分析") if best_model_key in ['decision_tree', 'random_forest']: feature_importances = results[best_model_key]['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_key]['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(""" 模型分析 查看已有模型的详细分析结果 """, unsafe_allow_html=True) if not os.path.exists('model_results.pkl'): st.info("ℹ️ 当前没有可用模型。请先进行模型训练以生成分析报告。") # 显示可执行的操作 col1, col2 = st.columns(2) with col1: st.markdown(""" 🛠️ 数据准备 确保您已准备好符合要求的数据文件,包含所有必要的特征字段。 """, unsafe_allow_html=True) with col2: st.markdown(""" 🧠 模型训练 切换到“训练新模型”选项卡,上传您的数据并开始训练过程。 """, unsafe_allow_html=True) 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_map = { '逻辑回归': 'logistic_regression', '决策树': 'decision_tree', '随机森林': 'random_forest' } model_key = model_map[model_choice] # 显示模型基本信息 model_info = results[model_key] st.markdown(f""" {model_choice} AUC得分: {model_info['auc']:.4f} 准确率: {model_info['accuracy']:.2%} """, 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_key in ['decision_tree', 'random_forest']: feature_importances = model_info['feature_importances'] importance_df = pd.DataFrame({ '特征': feature_cols, '重要性': feature_importances }).sort_values('重要性', ascending=False) st.subheader("特征重要性分析") # Top 10 重要特征 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['logistic_regression']['accuracy'], results['decision_tree']['accuracy'], results['random_forest']['accuracy']], 'AUC': [results['logistic_regression']['auc'], results['decision_tree']['auc'], results['random_forest']['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(""" © 2023 单宽转融用户预测系统 | 2231030273 基于Streamlit和Spark开发 """, unsafe_allow_html=True) Single_breadth_to_melt.csv文件前一百行数据为 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 … AVG_STMT_AMT_LV is_kdts is_itv_up is_mobile_up if_zzzw_up itv_cnt itv_day serv_in_time PROM_AMT_MONTH is_rh_next 0 201706 1-1E6Z49HF 1-UTSNWVU 杭州 NaN 其它部门-未知部门细分-未知 … 0 20140126 现行 普通宽带 … c30-59 0 0 0 0 0 0 41 44.44 0.0 1 201706 3-J591KYI 1-LKFKET 杭州 NaN 其它部门-未知部门细分-未知 … 0 20160406 现行 普通宽带 … e89-129 0 0 0 0 0 0 14 100.00 0.0 2 201706 1-F3YGP4D 1-6T16M75 杭州 营业厅 营业厅-营业服务中心-城市 … 0 20100112 现行 普通宽带 … c30-59 0 0 0 0 0 28 89 44.44 0.0 3 201706 1-1AITRLCN 1-1AB5KV9U 杭州 NaN 其它部门-未知部门细分-未知 … 0 20131017 现行 普通宽带 … c30-59 1 0 0 0 0 10 44 55.56 0.0 4 201706 1-132ZSIVX 1-LPVY5O 杭州 10000号 其它部门-10000客服部-城市 … 0 20130209 现行 普通宽带 … d59-89 0 0 0 0 0 0 52 0.00 0.0 根据如上修改代码,给出修改后完整代码

import streamlit as st import pandas as pd import numpy as np import joblib import os import time import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl import matplotlib.font_manager as fm import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, roc_auc_score, confusion_matrix from sklearn.preprocessing import StandardScaler from imblearn.over_sampling import SMOTE from sklearn.impute import SimpleImputer import warnings 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 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: #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 preprocess_loan_data(data_old): “”" 训练时数据预处理函数,返回处理后的数据和推理时需要的参数 参数: data_old: 原始训练数据 (DataFrame) 返回: processed_data: 预处理后的训练数据 (DataFrame) preprocessor_params: 推理时需要的预处理参数 (dict) """ # 1. 创建原始数据副本 loan_data = data_old.copy() # 2. 保存要删除的列列表 drop_list = ['id','member_id', 'term', 'pymnt_plan', 'initial_list_status', 'sub_grade', 'emp_title', 'issue_d', 'title', 'zip_code', 'addr_state', 'earliest_cr_line', 'last_pymnt_d', 'last_credit_pull_d', 'url','desc','next_pymnt_d'] loan_data.drop([col for col in drop_list if col in loan_data.columns], axis=1, inplace=True, errors='ignore') # 3. 删除缺失值超过90%的列 #todo 自己补齐删除代码 missing_ratio = loan_data.isnull().sum() / len(loan_data) loan_data.drop(missing_ratio[missing_ratio > 0.9].index, axis=1, inplace=True, errors='ignore') # 4. 删除值全部相同的列 #todo 自己补齐删除代码 constant_cols = loan_data.columns[loan_data.nunique() <= 1] loan_data.drop(constant_cols, axis=1, inplace=True, errors='ignore') # 5. 处理特殊数值列 loans = loan_data # 修正变量名 loans["int_rate"] = loans["int_rate"].astype(str).str.rstrip('%').astype("float") loans["revol_util"] = loans["revol_util"].astype(str).str.rstrip('%').astype("float") # 6. 缺失值处理 ## 识别分类列和数值列 objectColumns = loans.select_dtypes(include=["object"]).columns.tolist() numColumns = loans.select_dtypes(include=[np.number]).columns.tolist() ## 保存分类列的列名 categorical_columns = objectColumns.copy() ## 填充分类变量缺失值 loans[objectColumns] = loans[objectColumns].fillna("Unknown") ## 填充数值变量缺失值并保存均值 imr = SimpleImputer(missing_values=np.nan, strategy="mean") loans[numColumns] = imr.fit_transform(loans[numColumns]) # 保存数值列的均值 numerical_means = {col: imr.statistics_[i] for i, col in enumerate(numColumns)} # 8. 特征衍生 loans["installment_feat"] = loans["installment"] / ((loans["annual_inc"] + 1) / 12) # 9. 目标变量编码 status_mapping = { "Current": 0, "Issued": 0, "Fully Paid": 0, "In Grace Period": 1, "Late (31-120 days)": 1, "Late (16-30 days)": 1, "Charged Off": 1, "Does not meet the credit policy. Status:Charged Off": 1, "Does not meet the credit policy. Status:Fully Paid": 0, "Default": 0 } loans["loan_status"] = loans["loan_status"].map(status_mapping) # 10. 有序特征映射 mapping_dict = { "emp_length": { "10+ years": 10, "9 years": 9, "8 years": 8, "7 years": 7, "6 years": 6, "5 years": 5, "4 years": 4, "3 years": 3, "2 years": 2, "1 year": 1, "< 1 year": 0, "Unknown": 0 }, "grade": { "A": 1, "B": 2, "C": 3, "D": 4, "E": 5, "F": 6, "G": 7 } } loans = loans.replace(mapping_dict) # 11. One-hot编码 n_columns = ["home_ownership", "verification_status", "purpose", "application_type"] dummy_df = pd.get_dummies(loans[n_columns], drop_first=False) loans = pd.concat([loans, dummy_df], axis=1) loans.drop(n_columns, axis=1, inplace=True) # 保存One-hot编码后的列名 onehot_columns = n_columns onehot_encoder_columns = dummy_df.columns.tolist() # 12. 特征缩放 # 识别需要缩放的数值列 numeric_cols = loans.select_dtypes(include=["int", "float"]).columns.tolist() if 'loan_status' in numeric_cols: numeric_cols.remove('loan_status') # 创建并拟合缩放器 sc = StandardScaler() if numeric_cols: loans[numeric_cols] = sc.fit_transform(loans[numeric_cols]) # 保存缩放列名 scaled_columns = numeric_cols # 13. 保存最终列结构(在SMOTE之前) #final_columns = loans.columns.tolist().remove('loan_status') final_columns = loans.columns[loans.columns != 'loan_status'].tolist() # 14. 处理不平衡数据(SMOTE过采样) X = loans.drop("loan_status", axis=1) y = loans["loan_status"] os = SMOTE(random_state=42) X_res, y_res = os.fit_resample(X, y) # 15. 合并为最终DataFrame processed_data = pd.concat([X_res, y_res], axis=1) processed_data.columns = list(X.columns) + ["loan_status"] # 16. 创建推理时需要的参数字典 preprocessor_params = { # 1. 删除的列 'drop_list': drop_list, # 2. 分类列缺失值填充 'categorical_columns': categorical_columns, # 3. 数值列填充均值 'numerical_means': numerical_means, # 4. 有序特征映射 'mapping_dict': mapping_dict, # 5. One-hot配置 'onehot_columns': onehot_columns, 'onehot_encoder_columns': onehot_encoder_columns, # 6. 缩放器及缩放列 'scaler': sc, # 已拟合的StandardScaler实例 'scaled_columns': scaled_columns, # 7. 最终列结构(训练后的列顺序) 'final_columns': final_columns } return processed_data, preprocessor_params def preprocess_loan_data_inference(data_old, preprocessor_params): “”" 推理时数据处理函数 参数: data_old: 原始推理数据 (DataFrame) preprocessor_params: 从训练过程保存的预处理参数 (dict) 返回: processed_data: 预处理后的推理数据 (DataFrame) """ # 1. 复制数据避免污染原始数据 loanss = data_old.copy() # 2. 删除训练时确定的列 drop_list = preprocessor_params['drop_list'] loans = loanss.drop(columns=[col for col in drop_list if col in loanss.columns], axis=1, errors='ignore') # 3. 处理特殊数值列(百分比转换) if 'int_rate' in loans: loans["int_rate"] = loans["int_rate"].astype(str).str.rstrip('%').astype("float") if 'revol_util' in loans: loans["revol_util"] = loans["revol_util"].astype(str).str.rstrip('%').astype("float") # 4. 特征衍生(使用训练时相同公式) if 'installment' in loans and 'annual_inc' in loans: loans["installment_feat"] = loans["installment"] / ((loans["annual_inc"] + 1) / 12) # 5. 有序特征映射(使用训练时的映射字典) mapping_dict = preprocessor_params['mapping_dict'] for col, mapping in mapping_dict.items(): if col in loans: # 处理未知值,默认为0 loans[col] = loans[col].map(mapping).fillna(0).astype(int) # 6. 缺失值处理(使用训练时保存的策略) # 分类变量 cat_cols = preprocessor_params['categorical_columns'] for col in cat_cols: if col in loans: loans[col] = loans[col].fillna("Unknown") # 数值变量(使用训练时保存的均值) num_means = preprocessor_params['numerical_means'] for col, mean_value in num_means.items(): if col in loans: loans[col] = loans[col].fillna(mean_value) # 7. One-hot编码(对齐训练时的列结构) n_columns = preprocessor_params['onehot_columns'] expected_dummy_columns = preprocessor_params['onehot_encoder_columns'] # 创建空DataFrame用于存储结果 dummy_df = pd.DataFrame(columns=expected_dummy_columns) # 为每个分类列生成dummy变量 for col in n_columns: if col in loans: # 为当前列生成dummies col_dummies = pd.get_dummies(loans[col], prefix=col) # 对齐训练时的列结构 for expected_col in expected_dummy_columns: if expected_col in col_dummies: dummy_df[expected_col] = col_dummies[expected_col] else: # 如果该列不存在,则创建全0列 dummy_df[expected_col] = 0 # 合并dummy变量 loans = pd.concat([loans, dummy_df], axis=1) # 删除原始分类列 loans.drop(columns=[col for col in n_columns if col in loans.columns], inplace=True, errors='ignore') # 8. 特征缩放(使用训练时的缩放器参数) sc = preprocessor_params['scaler'] scaled_cols = [col for col in preprocessor_params['scaled_columns'] if col in loans.columns] if scaled_cols: loans[scaled_cols] = sc.transform(loans[scaled_cols]) # 9. 对齐最终特征列(确保与训练数据相同) final_columns = preprocessor_params['final_columns'] # 添加缺失列(用0填充) for col in final_columns: if col not in loans.columns: loans[col] = 0 # 移除多余列并保持顺序 processed_data = loans[final_columns] print(loans.columns) return processed_data 标题区域 st.markdown(“”" 风控违约预测系统 基于机器学习的信贷风险评估与预测 """, unsafe_allow_html=True) 页面布局 col1, col2 = st.columns([1, 1.5]) 左侧区域 - 图片和简介 with col1: st.markdown(“”" 智能风控系统 利用先进机器学习技术预测信贷违约风险 “”", unsafe_allow_html=True) # 使用在线图片作为占位符 st.image("https://images.unsplash.com/photo-1553877522-43269d4ea984?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80", caption="智能风控系统示意图", use_column_width=True) st.markdown(""" 📈 系统功能 客户违约风险预测 高精度机器学习模型 可视化风险评估 批量数据处理 """, unsafe_allow_html=True) 右侧区域 - 功能选择 with col2: st.markdown(“”" 请选择操作类型 您可以选择训练新模型或使用现有模型进行预测 “”", unsafe_allow_html=True) # 功能选择 option = st.radio("", ["🚀 训练新模型 - 使用新数据训练预测模型", "🔍 推理预测 - 使用模型预测违约风险"], index=0, label_visibility="hidden") # 模型训练部分 if "训练新模型" in option: st.markdown(""" 模型训练 上传训练数据并训练新的预测模型 """, unsafe_allow_html=True) # 上传训练数据 train_file = st.file_uploader("上传训练数据 (CSV格式)", type=["csv"]) if train_file is not None: try: # 读取数据 train_data_old = pd.read_csv(train_file) # 显示数据预览 with st.expander("数据预览", expanded=True): st.dataframe(train_data_old.head()) col1, col2, col3 = st.columns(3) col1.metric("总样本数", train_data_old.shape[0]) col2.metric("特征数量", train_data_old.shape[1] - 1) # 训练参数设置 st.subheader("训练参数") col1, col2 = st.columns(2) test_size = col1.slider("测试集比例", 0.1, 0.4, 0.2, 0.1) n_estimators = col2.slider("树的数量", 10, 500, 100, 10) max_depth = col1.slider("最大深度", 2, 30, 10, 1) random_state = col2.number_input("随机种子", 0, 100, 42) # 开始训练按钮 if st.button("开始训练模型", use_container_width=True): with st.spinner("模型训练中,请稍候..."): # 模拟数据处理 progress_bar = st.progress(0) train_data,preprocessor_params = preprocess_loan_data(train_data_old) joblib.dump(preprocessor_params, 'loan_preprocessor_params.pkl') # 步骤1: 数据预处理 time.sleep(1) progress_bar.progress(25) st.success("✅ 数据预处理完成") # 步骤2: 特征工程 time.sleep(1) progress_bar.progress(50) st.success("✅ 特征工程完成") # 步骤3: 模型训练 time.sleep(2) progress_bar.progress(75) # 实际训练代码 (简化版) X = train_data.drop("loan_status", axis=1) y = train_data["loan_status"] # 划分训练测试集 #todo 自己补齐数据划分代码 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state, stratify=y) # 训练模型 #todo 自己补齐调用随机森林算法完成模型的训练 model = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth, random_state=random_state, n_jobs=-1) model.fit(X_train, y_train) # 保存模型 joblib.dump(model, "risk_model.pkl") # 步骤4: 模型评估 time.sleep(1) progress_bar.progress(100) # 评估模型 #todo 自己补齐调用预测函数完成测试集推理预测 y_pred = model.predict(X_test) y_proba = model.predict_proba(X_test)[:, 1] accuracy = accuracy_score(y_test, y_pred) auc = roc_auc_score(y_test, y_proba) # 保存评估结果 st.session_state.model_trained = True st.session_state.accuracy = accuracy st.session_state.auc = auc st.session_state.y_test = y_test st.session_state.y_pred = y_pred st.success("🎉 模型训练完成!") # 显示模型性能 st.subheader("模型性能评估") col1, col2 = st.columns(2) col1.markdown(f""" {accuracy*100:.1f}% 准确率 """, unsafe_allow_html=True) col2.markdown(f""" {auc:.3f} AUC 分数 """, unsafe_allow_html=True) # 混淆矩阵 st.subheader("混淆矩阵") cm = confusion_matrix(y_test, y_pred) fig, ax = plt.subplots(figsize=(6, 4)) sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", ax=ax) ax.set_xlabel("预测标签") ax.set_ylabel("真实标签") ax.set_title("混淆矩阵") st.pyplot(fig) # 特征重要性 st.subheader("特征重要性") feature_importance = pd.DataFrame({ "特征": X.columns, "重要性": model.feature_importances_ }).sort_values("重要性", ascending=False).head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x="重要性", y="特征", data=feature_importance, palette="viridis", ax=ax) ax.set_title("Top 10 重要特征") st.pyplot(fig) except Exception as e: st.error(f"数据处理错误: {str(e)}") # 推理预测部分 else: st.markdown(""" 风险预测 上传需要预测的数据,生成违约风险评估报告 """, unsafe_allow_html=True) # 上传预测数据 predict_file = st.file_uploader("上传预测数据 (CSV格式)", type=["csv"]) if predict_file is not None: try: # 读取数据 predict_data = pd.read_csv(predict_file) # 显示数据预览 with st.expander("数据预览", expanded=True): st.dataframe(predict_data.head()) st.info(f"数据集包含 {predict_data.shape[0]} 个样本,{predict_data.shape[1]} 个特征") # 检查是否有模型 if not os.path.exists("risk_model.pkl"): st.warning("⚠️ 未找到训练好的模型,请先训练模型或使用示例数据") # 使用示例模型 if st.button("使用示例模型进行预测", use_container_width=True): st.info("正在使用预训练的示例模型进行预测...") # 创建示例模型 X = np.random.rand(100, 10) y = np.random.randint(0, 2, 100) model = RandomForestClassifier(n_estimators=50, random_state=42) model.fit(X, y) # 生成预测结果 predictions = model.predict(predict_data.values) probas = model.predict_proba(predict_data.values)[:, 1] # 创建结果DataFrame result_df = pd.DataFrame({ "客户ID": predict_data["member_id"], "违约概率": probas, "预测标签": predictions }) # 添加风险等级 result_df["风险等级"] = pd.cut( result_df["违约概率"], bins=[0, 0.2, 0.5, 1], labels=["低风险", "中风险", "高风险"], include_lowest=True ) # 保存结果 st.session_state.prediction_results = result_df else: # 加载模型 model = joblib.load("risk_model.pkl") preprocessor_params = joblib.load('loan_preprocessor_params.pkl') # 开始预测按钮 if st.button("开始风险预测", use_container_width=True): with st.spinner("预测进行中,请稍候..."): # 模拟预测过程 progress_bar = st.progress(0) # 预处理推理数据 #todo 自己补齐调用推理数据处理函数完成推理数据的清洗 processed_inference = preprocess_loan_data_inference(predict_data, preprocessor_params) # 步骤1: 数据预处理 time.sleep(1) progress_bar.progress(25) # 步骤2: 特征工程 time.sleep(1) progress_bar.progress(50) # 步骤3: 模型预测 time.sleep(1) progress_bar.progress(75) # 生成预测结果 predictions = model.predict(processed_inference.values) probas = model.predict_proba(processed_inference.values)[:, 1] # 创建结果DataFrame result_df = pd.DataFrame({ "客户ID": predict_data["member_id"], "违约概率": probas, "预测标签": predictions }) # 添加风险等级 result_df["风险等级"] = pd.cut( result_df["违约概率"], bins=[0, 0.2, 0.5, 1], labels=["低风险", "中风险", "高风险"], include_lowest=True ) # 步骤4: 生成报告 time.sleep(1) progress_bar.progress(100) # 保存结果 st.session_state.prediction_results = result_df st.success("✅ 预测完成!") except Exception as e: st.error(f"预测错误: {str(e)}") # 显示预测结果 if "prediction_results" in st.session_state: st.markdown(""" 预测结果 客户违约风险评估报告 """, unsafe_allow_html=True) result_df = st.session_state.prediction_results # 风险分布 st.subheader("风险分布概览") col1, col2, col3 = st.columns(3) high_risk = (result_df["风险等级"] == "高风险").sum() med_risk = (result_df["风险等级"] == "中风险").sum() low_risk = (result_df["风险等级"] == "低风险").sum() col1.markdown(f""" {high_risk} 高风险客户 """, unsafe_allow_html=True) col2.markdown(f""" {med_risk} 中风险客户 """, unsafe_allow_html=True) col3.markdown(f""" {low_risk} 低风险客户 """, unsafe_allow_html=True) # 风险分布图 fig, ax = plt.subplots(figsize=(8, 4)) risk_counts = result_df["风险等级"].value_counts() risk_counts.plot(kind="bar", color=["#4CAF50", "#FFC107", "#F44336"], ax=ax) ax.set_title("客户风险等级分布") ax.set_xlabel("风险等级") ax.set_ylabel("客户数量") st.pyplot(fig) # 详细预测结果 st.subheader("详细预测结果") # 样式函数 def color_risk(val): if val == "高风险": return "background-color: #ffcdd2; color: #c62828;" elif val == "中风险": return "background-color: #fff9c4; color: #f57f17;" else: return "background-color: #c8e6c9; color: #388e3c;" # 格式化显示 styled_df = result_df.style.applymap(color_risk, subset=["风险等级"]) st.dataframe(styled_df.format({ "违约概率": "{:.2%}" }), height=400) # 下载结果 csv = result_df.to_csv(index=False).encode("utf-8") st.download_button( label="下载预测结果", data=csv, file_name="风险预测结果.csv", mime="text/csv", use_container_width=True ) 页脚 st.markdown(“—”) st.markdown(“”" © 2023 风控违约预测系统 | 基于Streamlit开发 """, unsafe_allow_html=True) 根据如上代码,仿照如下要求,给出结果完整代码 大数据挖掘:精准营销 一、题目背景 某电信运营商为提升用户 ARPU(每用户平均收入),计划对单宽带用户推广 “单宽转融” 业务(即单宽带用户加装移动网业务,形成融合套餐)。为实现精准营销,需通过数据挖掘技术预测单宽带用户转化为融合套餐用户的可能性,从而针对性制定营销策略。现有一批单宽带用户的行为数据,要求通过数据分析和建模,构建高效的预测模型,辅助运营决策。 二、数据集介绍 1、数据来源:某运营商单宽转融用户的历史数据,包含用户基础信息、资费信息、电信行为数据、客户标签及 DPI 上网行为数据。 2、数据规模:50万+条记录,100+个字段。 3、关键字段说明: 1)用户属性:AGE(年龄),GENDER(性别),ONLINE_DAY(在网天数) 2)消费行为:STMT_AMT(出账金额),PROM_AMT(套餐价格),AVG_STMT_AMT(月均消费) 3)网络使用:DUR(上网时长),DWN_VOL(下载流量),TERM_CNT(接入终端数) 4)业务标签:IF_YHTS(是否投诉),MKT_STAR_GRADE_NAME(用户星级) 5)目标变量(标签):is_rh_next,表示用户是否转为融合套餐(1 为转化,0 为未转化)。 三、题目要求 1、使用 Python 进行数据分析与预处理: 1)加载数据并检查数据质量(缺失值、异常值)。 2)进行特征工程:删除无意义特征、处理缺失值、离散特征编码、标准化 / 归一化。 3)可视化数据分布,分析关键特征与目标变量的相关性。 2、使用 Spark 进行模型训练与测试: 1)构建逻辑回归、决策树、随机森林三种模型。 2)调优模型参数,对比评估指标(准确率、召回率、F1 值、AUC)。 3)选择最优模型,并解释特征重要性。 3、输出要求: 1)给出数据预处理的关键步骤及代码。 2)展示各模型的训练结果与对比分析。 3)说明最终选择的模型及理由。 数据集文件名为Single_breadth_to_melt.csv 前一百行数据为 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 … AVG_STMT_AMT_LV is_kdts is_itv_up is_mobile_up if_zzzw_up itv_cnt itv_day serv_in_time PROM_AMT_MONTH is_rh_next 0 201706 1-1E6Z49HF 1-UTSNWVU 杭州 NaN 其它部门-未知部门细分-未知 … 0 20140126 现行 普通宽带 … c30-59 0 0 0 0 0 0 41 44.44 0.0 1 201706 3-J591KYI 1-LKFKET 杭州 NaN 其它部门-未知部门细分-未知 … 0 20160406 现行 普通宽带 … e89-129 0 0 0 0 0 0 14 100.00 0.0 2 201706 1-F3YGP4D 1-6T16M75 杭州 营业厅 营业厅-营业服务中心-城市 … 0 20100112 现行 普通宽带 … c30-59 0 0 0 0 0 28 89 44.44 0.0 3 201706 1-1AITRLCN 1-1AB5KV9U 杭州 NaN 其它部门-未知部门细分-未知 … 0 20131017 现行 普通宽带 … c30-59 1 0 0 0 0 10 44 55.56 0.0 4 201706 1-132ZSIVX 1-LPVY5O 杭州 10000号 其它部门-10000客服部-城市 … 0 20130209 现行 普通宽带 … d59-89 0 0 0 0 0 0 52 0.00 0.0

-> llm = LLM(**dataclasses.asdict(engine_args)) (Pdb) n INFO 07-28 06:48:21 [config.py:841] This model supports multiple tasks: {'classify', 'generate', 'embed', 'reward'}. Defaulting to 'generate'. INFO 07-28 06:48:21 [config.py:1472] Using max model len 32768 INFO 07-28 06:48:22 [config.py:2285] Chunked prefill is enabled with max_num_batched_tokens=8192. INFO 07-28 06:48:22 [platform.py:174] PIECEWISE compilation enabled on NPU. use_inductor not supported - using only ACL Graph mode INFO 07-28 06:48:22 [utils.py:321] Calculated maximum supported batch sizes for ACL graph: 66 INFO 07-28 06:48:22 [utils.py:336] Adjusted ACL graph batch sizes for Qwen2ForCausalLM model (layers: 28): 67 → 66 sizes huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks... To disable this warning, you can either: - Avoid using tokenizers before the fork if possible - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false) INFO 07-28 06:48:23 [core.py:526] Waiting for init message from front-end. INFO 07-28 06:48:23 [core.py:69] Initializing a V1 LLM engine (v0.9.2) with config: model='/models/z50051264/summary/Qwen2.5-7B', speculative_config=None, tokenizer='/models/z50051264/summary/Qwen2.5-7B', skip_tokenizer_init=False, tokenizer_mode=auto, revision=None, override_neuron_config={}, tokenizer_revision=None, trust_remote_code=False, dtype=torch.bfloat16, max_seq_len=32768, download_dir=None, load_format=auto, tensor_parallel_size=1, pipeline_parallel_size=1, disable_custom_all_reduce=True, quantization=None, enforce_eager=False, kv_cache_dtype=auto, device_config=npu, decoding_config=DecodingConfig(backend='auto', disable_fallback=False, disable_any_whitespace=False, disable_additional_properties=False, reasoning_backend=''), observability_config=ObservabilityConfig(show_hidden_metrics_for_version=None, otlp_traces_endpoint=None, collect_detailed_traces=None), seed=0, served_model_name=/models/z50051264/summary/Qwen2.5-7B, num_scheduler_steps=1, multi_step_stream_outputs=True, enable_prefix_caching=True, chunked_prefill_enabled=True, use_async_output_proc=True, pooler_config=None, compilation_config={"level":3,"debug_dump_path":"","cache_dir":"","backend":"","custom_ops":["all"],"splitting_ops":["vllm.unified_attention","vllm.unified_attention_with_output","vllm.unified_ascend_attention_with_output"],"use_inductor":false,"compile_sizes":[],"inductor_compile_config":{},"inductor_passes":{},"use_cudagraph":true,"cudagraph_num_of_warmups":1,"cudagraph_capture_sizes":[512,504,496,488,480,472,464,456,448,440,432,424,416,408,400,392,384,376,368,360,352,344,336,328,320,312,304,296,288,280,272,264,256,240,232,224,216,208,200,192,184,176,168,160,152,144,136,128,120,112,104,96,88,80,72,64,56,48,40,32,24,16,8,4,2,1],"cudagraph_copy_inputs":false,"full_cuda_graph":false,"max_capture_size":512,"local_cache_dir":null} ERROR 07-28 06:48:29 [core.py:586] EngineCore failed to start. ERROR 07-28 06:48:29 [core.py:586] Traceback (most recent call last): ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 577, in run_engine_core ERROR 07-28 06:48:29 [core.py:586] engine_core = EngineCoreProc(*args, **kwargs) ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 404, in __init__ ERROR 07-28 06:48:29 [core.py:586] super().__init__(vllm_config, executor_class, log_stats, ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 75, in __init__ ERROR 07-28 06:48:29 [core.py:586] self.model_executor = executor_class(vllm_config) ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/executor/executor_base.py", line 53, in __init__ ERROR 07-28 06:48:29 [core.py:586] self._init_executor() ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/executor/uniproc_executor.py", line 47, in _init_executor ERROR 07-28 06:48:29 [core.py:586] self.collective_rpc("init_device") ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/executor/uniproc_executor.py", line 57, in collective_rpc ERROR 07-28 06:48:29 [core.py:586] answer = run_method(self.driver_worker, method, args, kwargs) ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/utils/__init__.py", line 2736, in run_method ERROR 07-28 06:48:29 [core.py:586] return func(*args, **kwargs) ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm/vllm/worker/worker_base.py", line 606, in init_device ERROR 07-28 06:48:29 [core.py:586] self.worker.init_device() # type: ignore ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm-ascend/vllm_ascend/worker/worker_v1.py", line 132, in init_device ERROR 07-28 06:48:29 [core.py:586] NPUPlatform.set_device(device) ERROR 07-28 06:48:29 [core.py:586] File "/vllm-workspace/vllm-ascend/vllm_ascend/platform.py", line 98, in set_device ERROR 07-28 06:48:29 [core.py:586] torch.npu.set_device(device) ERROR 07-28 06:48:29 [core.py:586] File "/usr/local/python3.10.17/lib/python3.10/site-packages/torch_npu/npu/utils.py", line 80, in set_device ERROR 07-28 06:48:29 [core.py:586] torch_npu._C._npu_setDevice(device_id) ERROR 07-28 06:48:29 [core.py:586] RuntimeError: SetPrecisionMode:build/CMakeFiles/torch_npu.dir/compiler_depend.ts:156 NPU function error: at_npu::native::AclSetCompileopt(aclCompileOpt::ACL_PRECISION_MODE, precision_mode), error code is 500001 ERROR 07-28 06:48:29 [core.py:586] [ERROR] 2025-07-28-06:48:29 (PID:3848, Device:0, RankID:-1) ERR00100 PTA call acl api failed ERROR 07-28 06:48:29 [core.py:586] [Error]: The internal ACL of the system is incorrect. ERROR 07-28 06:48:29 [core.py:586] Rectify the fault based on the error information in the ascend log. ERROR 07-28 06:48:29 [core.py:586] E90000: [PID: 3848] 2025-07-28-06:48:29.892.415 Compile operator failed, cause: module '__main__' has no attribute '__spec__' File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/interface.py", line 33, in cann_kb_init ERROR 07-28 06:48:29 [core.py:586] return RouteServer.initialize(**locals()) ERROR 07-28 06:48:29 [core.py:586] File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/route.py", line 54, in wrapper ERROR 07-28 06:48:29 [core.py:586] return func(cls, *args, **kwargs) ERROR 07-28 06:48:29 [core.py:586] File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/route.py", line 169, in initialize ERROR 07-28 06:48:29 [core.py:586] main_mod, main_path = config_main_info() ERROR 07-28 06:48:29 [core.py:586] File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/utils/common.py", line 37, in config_main_info ERROR 07-28 06:48:29 [core.py:586] main_module_name = getattr(main_module.__spec__, "name", None) ERROR 07-28 06:48:29 [core.py:586] ERROR 07-28 06:48:29 [core.py:586] TraceBack (most recent call last): ERROR 07-28 06:48:29 [core.py:586] AOE Failed to call InitCannKB[FUNC:Initialize][FILE:python_adapter_manager.cc][LINE:47] ERROR 07-28 06:48:29 [core.py:586] Failed to initialize TeConfigInfo. ERROR 07-28 06:48:29 [core.py:586] [GraphOpt][InitializeInner][InitTbeFunc] Failed to init tbe.[FUNC:InitializeTeFusion][FILE:tbe_op_store_adapter.cc][LINE:1889] ERROR 07-28 06:48:29 [core.py:586] [GraphOpt][InitializeInner][InitTeFusion]: Failed to initialize TeFusion.[FUNC:InitializeInner][FILE:tbe_op_store_adapter.cc][LINE:1856] ERROR 07-28 06:48:29 [core.py:586] [SubGraphOpt][PreCompileOp][InitAdapter] InitializeAdapter adapter [tbe_op_adapter] failed! Ret [4294967295][FUNC:InitializeAdapter][FILE:op_store_adapter_manager.cc][LINE:79] ERROR 07-28 06:48:29 [core.py:586] [SubGraphOpt][PreCompileOp][Init] Initialize op store adapter failed, OpsStoreName[tbe-custom].[FUNC:Initialize][FILE:op_store_adapter_manager.cc][LINE:120] ERROR 07-28 06:48:29 [core.py:586] [FusionMngr][Init] Op store adapter manager init failed.[FUNC:Initialize][FILE:fusion_manager.cc][LINE:115] ERROR 07-28 06:48:29 [core.py:586] PluginManager InvokeAll failed.[FUNC:Initialize][FILE:ops_kernel_manager.cc][LINE:83] ERROR 07-28 06:48:29 [core.py:586] OpsManager initialize failed.[FUNC:InnerInitialize][FILE:gelib.cc][LINE:259] ERROR 07-28 06:48:29 [core.py:586] GELib::InnerInitialize failed.[FUNC:Initialize][FILE:gelib.cc][LINE:184] ERROR 07-28 06:48:29 [core.py:586] GEInitialize failed.[FUNC:GEInitialize][FILE:ge_api.cc][LINE:371] ERROR 07-28 06:48:29 [core.py:586] [Initialize][Ge]GEInitialize failed. ge result = 4294967295[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] ERROR 07-28 06:48:29 [core.py:586] [Init][Compiler]Init compiler failed[FUNC:ReportInnerError][FILE:log_inner.cpp][LINE:145] ERROR 07-28 06:48:29 [core.py:586] [Set][Options]OpCompileProcessor init failed![FUNC:ReportInnerError][FILE:log_inner.cpp][LINE:145] ERROR 07-28 06:48:29 [core.py:586] Exception in thread Thread-4: Process EngineCore_0: Traceback (most recent call last): File "/usr/local/python3.10.17/lib/python3.10/threading.py", line 1016, in _bootstrap_inner self.run() File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/utils/multiprocess_util.py", line 91, in run Traceback (most recent call last): File "/usr/local/python3.10.17/lib/python3.10/multiprocessing/process.py", line 314, in _bootstrap self.run() File "/usr/local/python3.10.17/lib/python3.10/multiprocessing/process.py", line 108, in run self._target(*self._args, **self._kwargs) File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 590, in run_engine_core raise e File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 577, in run_engine_core engine_core = EngineCoreProc(*args, **kwargs) File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 404, in __init__ super().__init__(vllm_config, executor_class, log_stats, key, func, args, kwargs = self.task_q.get(timeout=TIMEOUT) File "/vllm-workspace/vllm/vllm/v1/engine/core.py", line 75, in __init__ self.model_executor = executor_class(vllm_config) File "<string>", line 2, in get File "/vllm-workspace/vllm/vllm/executor/executor_base.py", line 53, in __init__ self._init_executor() File "/vllm-workspace/vllm/vllm/executor/uniproc_executor.py", line 47, in _init_executor self.collective_rpc("init_device") File "/vllm-workspace/vllm/vllm/executor/uniproc_executor.py", line 57, in collective_rpc answer = run_method(self.driver_worker, method, args, kwargs) File "/vllm-workspace/vllm/vllm/utils/__init__.py", line 2736, in run_method return func(*args, **kwargs) File "/vllm-workspace/vllm/vllm/worker/worker_base.py", line 606, in init_device self.worker.init_device() # type: ignore File "/vllm-workspace/vllm-ascend/vllm_ascend/worker/worker_v1.py", line 132, in init_device NPUPlatform.set_device(device) File "/vllm-workspace/vllm-ascend/vllm_ascend/platform.py", line 98, in set_device torch.npu.set_device(device) File "/usr/local/python3.10.17/lib/python3.10/site-packages/torch_npu/npu/utils.py", line 80, in set_device torch_npu._C._npu_setDevice(device_id) RuntimeError: SetPrecisionMode:build/CMakeFiles/torch_npu.dir/compiler_depend.ts:156 NPU function error: at_npu::native::AclSetCompileopt(aclCompileOpt::ACL_PRECISION_MODE, precision_mode), error code is 500001 [ERROR] 2025-07-28-06:48:29 (PID:3848, Device:0, RankID:-1) ERR00100 PTA call acl api failed [Error]: The internal ACL of the system is incorrect. Rectify the fault based on the error information in the ascend log. E90000: [PID: 3848] 2025-07-28-06:48:29.892.415 Compile operator failed, cause: module '__main__' has no attribute '__spec__' File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/interface.py", line 33, in cann_kb_init return RouteServer.initialize(**locals()) File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/route.py", line 54, in wrapper return func(cls, *args, **kwargs) File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/route.py", line 169, in initialize main_mod, main_path = config_main_info() File "/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/common/repository_manager/utils/common.py", line 37, in config_main_info main_module_name = getattr(main_module.__spec__, "name", None) TraceBack (most recent call last): AOE Failed to call InitCannKB[FUNC:Initialize][FILE:python_adapter_manager.cc][LINE:47] Failed to initialize TeConfigInfo. [GraphOpt][InitializeInner][InitTbeFunc] Failed to init tbe.[FUNC:InitializeTeFusion][FILE:tbe_op_store_adapter.cc][LINE:1889] [GraphOpt][InitializeInner][InitTeFusion]: Failed to initialize TeFusion.[FUNC:InitializeInner][FILE:tbe_op_store_adapter.cc][LINE:1856] [SubGraphOpt][PreCompileOp][InitAdapter] InitializeAdapter adapter [tbe_op_adapter] failed! Ret [4294967295][FUNC:InitializeAdapter][FILE:op_store_adapter_manager.cc][LINE:79] [SubGraphOpt][PreCompileOp][Init] Initialize op store adapter failed, OpsStoreName[tbe-custom].[FUNC:Initialize][FILE:op_store_adapter_manager.cc][LINE:120] [FusionMngr][Init] Op store adapter manager init failed.[FUNC:Initialize][FILE:fusion_manager.cc][LINE:115] PluginManager InvokeAll failed.[FUNC:Initialize][FILE:ops_kernel_manager.cc][LINE:83] OpsManager initialize failed.[FUNC:InnerInitialize][FILE:gelib.cc][LINE:259] GELib::InnerInitialize failed.[FUNC:Initialize][FILE:gelib.cc][LINE:184] GEInitialize failed.[FUNC:GEInitialize][FILE:ge_api.cc][LINE:371] [Initialize][Ge]GEInitialize failed. ge result = 4294967295[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] [Init][Compiler]Init compiler failed[FUNC:ReportInnerError][FILE:log_inner.cpp][LINE:145] [Set][Options]OpCompileProcessor init failed![FUNC:ReportInnerError][FILE:log_inner.cpp][LINE:145] RuntimeError: Engine core initialization failed. See root cause above. Failed core proc(s): {'EngineCore_0': 1} > /models/z50051264/vllm-0.10.0/benchmarks/benchmark_throughput.py(62)run_vllm() -> llm = LLM(**dataclasses.asdict(engine_args)) 初始化引擎失败是怎么回事儿???

import streamlit as st import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import joblib import os import time import warnings from io import BytesIO import platform from pathlib import Path from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, roc_auc_score, confusion_matrix, f1_score from sklearn.preprocessing import StandardScaler, OneHotEncoder from imblearn.over_sampling import SMOTE from sklearn.impute import SimpleImputer from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False def safe_path(path): """处理Windows长路径问题""" if platform.system() == 'Windows': try: import ntpath return ntpath.realpath(path) except: return str(Path(path).resolve()) return path # 忽略警告 warnings.filterwarnings("ignore") # 页面设置 st.set_page_config( page_title="精准营销系统", page_icon="📊", layout="wide", initial_sidebar_state="expanded" ) # 自定义CSS样式 st.markdown(""" <style> .stApp { background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); font-family: 'Helvetica Neue', Arial, sans-serif; } .header { background: linear-gradient(90deg, #1a237e 0%, #283593 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: 1rem; 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, #3949ab 0%, #1a237e 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(57, 73, 171, 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: #1a237e; } .metric-label { font-size: 0.9rem; color: #5c6bc0; 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); } .convert-high { background-color: #c8e6c9 !important; color: #388e3c !important; font-weight: 700; } .convert-low { background-color: #ffcdd2 !important; color: #c62828 !important; font-weight: 600; } </style> """, unsafe_allow_html=True) def preprocess_data_train(df): """ 训练时数据预处理函数 返回处理后的数据和推理时需要的参数 """ # 1. 复制数据避免污染原始数据 data = df.copy() # 2. 选择关键特征 available_features = [col for col in data.columns if col in [ 'AGE', 'GENDER', 'ONLINE_DAY', 'TERM_CNT', 'IF_YHTS', 'MKT_STAR_GRADE_NAME', 'PROM_AMT_MONTH', 'is_rh_next' # 目标变量 ]] # 确保目标变量存在 if 'is_rh_next' not in available_features: st.error("错误:数据集中缺少目标变量 'is_rh_next'") return data, None data = data[available_features] # 3. 处理缺失值 # 数值特征用均值填充 numeric_cols = ['AGE', 'ONLINE_DAY', 'TERM_CNT', 'PROM_AMT_MONTH'] for col in numeric_cols: if col in data.columns: mean_val = data[col].mean() data[col].fillna(mean_val, inplace=True) # 分类特征用众数填充 categorical_cols = ['GENDER', 'MKT_STAR_GRADE_NAME', 'IF_YHTS'] for col in categorical_cols: if col in data.columns: mode_val = data[col].mode()[0] data[col].fillna(mode_val, inplace=True) # 4. 处理目标变量的缺失值 - 修复点 if data['is_rh_next'].isnull().any(): st.warning(f"目标变量 'is_rh_next' 中存在 {data['is_rh_next'].isnull().sum()} 个缺失值,将删除这些行") data = data.dropna(subset=['is_rh_next']) # 5. 异常值处理(使用IQR方法) def handle_outliers(series): Q1 = series.quantile(0.25) Q3 = series.quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR return series.clip(lower_bound, upper_bound) for col in numeric_cols: if col in data.columns: data[col] = handle_outliers(data[col]) # 6. 保存预处理参数 preprocessor_params = { # 数值特征均值 'numerical_means': {col: data[col].mean() for col in numeric_cols if col in data.columns}, # 分类特征众数 'categorical_modes': {col: data[col].mode()[0] for col in categorical_cols if col in data.columns}, # 特征列表 'features': available_features, # 数值特征列表 'numeric_cols': numeric_cols, # 分类特征列表 'categorical_cols': categorical_cols, # 异常值处理边界 'outlier_bounds': {} } # 计算并保存异常值边界 for col in numeric_cols: if col in data.columns: Q1 = data[col].quantile(0.25) Q3 = data[col].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR preprocessor_params['outlier_bounds'][col] = (lower_bound, upper_bound) return data, preprocessor_params def preprocess_data_inference(df, preprocessor_params): """ 推理时数据预处理函数 """ # 1. 复制数据避免污染原始数据 data = df.copy() # 2. 只保留需要的特征 if 'features' in preprocessor_params: # 确保不包含目标变量 features = [f for f in preprocessor_params['features'] if f != 'is_rh_next'] data = data[features] # 3. 处理缺失值 # 数值特征用训练集的均值填充 if 'numerical_means' in preprocessor_params: for col, mean_val in preprocessor_params['numerical_means'].items(): if col in data.columns: data[col].fillna(mean_val, inplace=True) # 分类特征用训练集的众数填充 if 'categorical_modes' in preprocessor_params: for col, mode_val in preprocessor_params['categorical_modes'].items(): if col in data.columns: data[col].fillna(mode_val, inplace=True) # 4. 异常值处理(使用训练集的边界) if 'outlier_bounds' in preprocessor_params: for col, bounds in preprocessor_params['outlier_bounds'].items(): if col in data.columns: lower_bound, upper_bound = bounds data[col] = data[col].clip(lower_bound, upper_bound) return data # 标题区域 st.markdown(""" 精准营销系统 基于机器学习的单宽转融预测 """, unsafe_allow_html=True) # 页面布局 col1, col2 = st.columns([1, 1.5]) # 左侧区域 - 图片和简介 with col1: st.markdown(""" 📱 智能营销系统 预测单宽带用户转化为融合套餐用户的可能性 """, unsafe_allow_html=True) # 使用在线图片作为占位符 st.image("https://images.unsplash.com/photo-1551836022-d5d88e9218df?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80", caption="精准营销系统示意图", width=600) st.markdown(""" 📈 系统功能 用户转化可能性预测 高精度机器学习模型 可视化数据分析 精准营销策略制定 """, unsafe_allow_html=True) # 右侧区域 - 功能选择 with col2: st.markdown(""" 📋 请选择操作类型 您可以选择数据分析或使用模型进行预测 """, unsafe_allow_html=True) # 功能选择 option = st.radio("", ["📊 数据分析 - 探索数据并训练模型", "🔍 预测分析 - 预测用户转化可能性"], index=0, label_visibility="hidden") # 数据分析部分 if "数据分析" in option: st.markdown(""" 数据分析与模型训练 上传数据并训练预测模型 """, unsafe_allow_html=True) # 上传训练数据 train_file = st.file_uploader("上传数据集 (CSV格式, GBK编码)", type=["csv"]) if train_file is not None: try: # 读取数据 train_data = pd.read_csv(train_file, encoding='GBK') # 显示数据预览 with st.expander("数据预览", expanded=True): st.dataframe(train_data.head()) col1, col2 = st.columns(2) col1.metric("总样本数", train_data.shape[0]) col2.metric("特征数量", train_data.shape[1] - 1) # 数据预处理 st.subheader("数据预处理") with st.spinner("数据预处理中..."): processed_data, preprocessor_params = preprocess_data_train(train_data) # 检查目标变量是否有缺失值 if processed_data['is_rh_next'].isnull().any(): st.warning(f"目标变量 'is_rh_next' 中仍有 {processed_data['is_rh_next'].isnull().sum()} 个缺失值,将删除这些行") processed_data = processed_data.dropna(subset=['is_rh_next']) joblib.dump(preprocessor_params, 'preprocessor_params.pkl') st.success("✅ 数据预处理完成") # 可视化数据分布 st.subheader("数据分布分析") # 目标变量分布 st.markdown("**目标变量分布 (is_rh_next)**") fig, ax = plt.subplots(figsize=(8, 5)) sns.countplot(x='is_rh_next', data=processed_data, palette='viridis') plt.title('用户转化分布 (0:未转化, 1:转化)') plt.xlabel('是否转化') plt.ylabel('用户数量') st.pyplot(fig) # 数值特征分布 st.markdown("**数值特征分布**") numeric_cols = ['AGE', 'ONLINE_DAY', 'TERM_CNT', 'PROM_AMT_MONTH'] # 动态计算子图布局 num_features = len(numeric_cols) if num_features > 0: ncols = 2 nrows = (num_features + ncols - 1) // ncols # 向上取整 fig, axes = plt.subplots(nrows, ncols, figsize=(14, 4*nrows)) # 将axes展平为一维数组 if nrows > 1 or ncols > 1: axes = axes.flatten() else: axes = [axes] # 单个子图时确保axes是列表 for i, col in enumerate(numeric_cols): if col in processed_data.columns and i < len(axes): sns.histplot(processed_data[col], kde=True, ax=axes[i], color='skyblue') axes[i].set_title(f'{col}分布') axes[i].set_xlabel('') # 隐藏多余的子图 for j in range(i+1, len(axes)): axes[j].set_visible(False) plt.tight_layout() st.pyplot(fig) else: st.warning("没有可用的数值特征") # 特征相关性分析 st.markdown("**特征相关性热力图**") corr_cols = numeric_cols + ['is_rh_next'] if len(corr_cols) > 1: corr_data = processed_data[corr_cols].corr() fig, ax = plt.subplots(figsize=(12, 8)) sns.heatmap(corr_data, annot=True, fmt=".2f", cmap='coolwarm', ax=ax) plt.title('特征相关性热力图') st.pyplot(fig) else: st.warning("特征不足,无法生成相关性热力图") # 模型训练 st.subheader("模型训练") # 训练参数设置 col1, col2 = st.columns(2) test_size = col1.slider("测试集比例", 0.1, 0.4, 0.2, 0.05) random_state = col2.number_input("随机种子", 0, 100, 42) n_estimators = col1.slider("树的数量", 10, 500, 100, 10) max_depth = col2.slider("最大深度", 2, 30, 10, 1) # 开始训练按钮 if st.button("开始训练模型", use_container_width=True): with st.spinner("模型训练中,请稍候..."): progress_bar = st.progress(0) # 步骤1: 特征工程 X = processed_data.drop('is_rh_next', axis=1) y = processed_data['is_rh_next'] # 确保目标变量没有缺失值 - 再次检查 if y.isnull().any(): st.warning(f"目标变量中仍有 {y.isnull().sum()} 个缺失值,将删除这些行") X = X.dropna() y = y.dropna() # 处理分类特征 categorical_cols = ['GENDER', 'MKT_STAR_GRADE_NAME', 'IF_YHTS'] existing_cat_cols = [col for col in categorical_cols if col in X.columns] # 创建预处理管道 numeric_features = ['AGE', 'ONLINE_DAY', 'TERM_CNT', 'PROM_AMT_MONTH'] numeric_transformer = Pipeline(steps=[ ('scaler', StandardScaler()) ]) categorical_transformer = Pipeline(steps=[ ('onehot', OneHotEncoder(handle_unknown='ignore')) ]) preprocessor = ColumnTransformer( transformers=[ ('num', numeric_transformer, numeric_features), ('cat', categorical_transformer, existing_cat_cols) ]) # 步骤2: 处理不平衡数据 os = SMOTE(random_state=random_state) X_res, y_res = os.fit_resample(X, y) # 划分训练测试集 X_train, X_test, y_train, y_test = train_test_split( X_res, y_res, test_size=test_size, random_state=random_state, stratify=y_res ) progress_bar.progress(30) time.sleep(0.5) # 步骤3: 模型训练 model = RandomForestClassifier( n_estimators=n_estimators, max_depth=max_depth, random_state=random_state, n_jobs=-1 ) # 创建完整管道 clf = Pipeline(steps=[ ('preprocessor', preprocessor), ('classifier', model) ]) clf.fit(X_train, y_train) progress_bar.progress(80) time.sleep(0.5) # 步骤4: 模型评估 y_pred = clf.predict(X_test) y_proba = clf.predict_proba(X_test)[:, 1] accuracy = accuracy_score(y_test, y_pred) auc = roc_auc_score(y_test, y_proba) f1 = f1_score(y_test, y_pred) # 保存模型 joblib.dump(clf, "marketing_model.pkl") st.session_state.model = clf st.session_state.preprocessor_params = preprocessor_params progress_bar.progress(100) st.success("🎉 模型训练完成!") # 显示模型性能 st.subheader("模型性能评估") col1, col2, col3 = st.columns(3) col1.markdown(f""" {accuracy*100:.1f}% 准确率 """, unsafe_allow_html=True) col2.markdown(f""" {auc:.3f} AUC 分数 """, unsafe_allow_html=True) col3.markdown(f""" {f1:.3f} F1 分数 """, unsafe_allow_html=True) # 混淆矩阵 st.subheader("混淆矩阵") cm = confusion_matrix(y_test, y_pred) fig, ax = plt.subplots(figsize=(6, 4)) sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", ax=ax) ax.set_xlabel("预测标签") ax.set_ylabel("真实标签") ax.set_title("混淆矩阵") st.pyplot(fig) # 特征重要性 st.subheader("特征重要性") # 获取特征名称 feature_names = numeric_features.copy() if 'cat' in clf.named_steps['preprocessor'].named_transformers_: ohe = clf.named_steps['preprocessor'].named_transformers_['cat'].named_steps['onehot'] cat_feature_names = ohe.get_feature_names_out(existing_cat_cols) feature_names.extend(cat_feature_names) # 获取特征重要性 feature_importances = clf.named_steps['classifier'].feature_importances_ importance_df = pd.DataFrame({ "特征": feature_names, "重要性": feature_importances }).sort_values("重要性", ascending=False).head(10) fig, ax = plt.subplots(figsize=(10, 6)) sns.barplot(x="重要性", y="特征", data=importance_df, palette="viridis", ax=ax) ax.set_title("Top 10 重要特征") st.pyplot(fig) except Exception as e: st.error(f"数据处理错误: {str(e)}") # 预测分析部分 else: st.markdown(""" 用户转化预测 预测单宽带用户转化为融合套餐的可能性 """, unsafe_allow_html=True) # 上传预测数据 predict_file = st.file_uploader("上传预测数据 (CSV格式, GBK编码)", type=["csv"]) if predict_file is not None: try: # 读取数据 predict_data = pd.read_csv(predict_file, encoding='GBK') # 显示数据预览 with st.expander("数据预览", expanded=True): st.dataframe(predict_data.head()) # 检查是否有模型 if not os.path.exists("marketing_model.pkl") or not os.path.exists("preprocessor_params.pkl"): st.warning("⚠️ 未找到训练好的模型,请先训练模型") st.stop() # 开始预测按钮 if st.button("开始预测", use_container_width=True): with st.spinner("预测进行中,请稍候..."): progress_bar = st.progress(0) # 加载预处理参数 preprocessor_params = joblib.load('preprocessor_params.pkl') # 数据预处理 processed_data = preprocess_data_inference(predict_data, preprocessor_params) progress_bar.progress(30) time.sleep(0.5) # 加载模型 model = joblib.load("marketing_model.pkl") # 生成预测结果 predictions = model.predict(processed_data) probas = model.predict_proba(processed_data)[:, 1] progress_bar.progress(80) time.sleep(0.5) # 创建结果DataFrame if 'CCUST_ROW_ID' in predict_data.columns: customer_ids = predict_data['CCUST_ROW_ID'] else: customer_ids = range(1, len(predict_data) + 1) result_df = pd.DataFrame({ "客户ID": customer_ids, "转化概率": probas, "预测结果": predictions }) # 添加转化可能性等级 result_df['预测标签'] = result_df['预测结果'].apply(lambda x: "可能转化" if x == 1 else "可能不转化") result_df['转化可能性'] = pd.cut( result_df['转化概率'], bins=[0, 0.3, 0.7, 1], labels=["低可能性", "中可能性", "高可能性"], include_lowest=True ) # 保存结果 st.session_state.prediction_results = result_df progress_bar.progress(100) st.success("✅ 预测完成!") except Exception as e: st.error(f"预测错误: {str(e)}") # 显示预测结果 if "prediction_results" in st.session_state: st.markdown(""" 预测结果 用户转化可能性评估报告 """, unsafe_allow_html=True) result_df = st.session_state.prediction_results # 转化可能性分布 st.subheader("转化可能性分布概览") col1, col2, col3 = st.columns(3) high_conv = (result_df["转化可能性"] == "高可能性").sum() med_conv = (result_df["转化可能性"] == "中可能性").sum() low_conv = (result_df["转化可能性"] == "低可能性").sum() col1.markdown(f""" {high_conv} 高可能性用户 """, unsafe_allow_html=True) col2.markdown(f""" {med_conv} 中可能性用户 """, unsafe_allow_html=True) col3.markdown(f""" {low_conv} 低可能性用户 """, unsafe_allow_html=True) # 转化可能性分布图 fig, ax = plt.subplots(figsize=(8, 5)) conv_counts = result_df["转化可能性"].value_counts() conv_counts.plot(kind='bar', color=['#4CAF50', '#FFC107', '#F44336'], ax=ax) plt.title('用户转化可能性分布') plt.xlabel('可能性等级') plt.ylabel('用户数量') st.pyplot(fig) # 详细预测结果 st.subheader("详细预测结果") # 样式函数 def color_convert(val): if val == "高可能性": return "background-color: #c8e6c9; color: #388e3c;" elif val == "中可能性": return "background-color: #fff9c4; color: #f57f17;" else: return "background-color: #ffcdd2; color: #c62828;" # 格式化显示 display_df = result_df[["客户ID", "转化概率", "预测标签", "转化可能性"]] styled_df = display_df.style.format({ "转化概率": "{:.2%}" }).applymap(color_convert, subset=["转化可能性"]) st.dataframe(styled_df, height=400) # 下载结果 csv = display_df.to_csv(index=False).encode("utf-8") st.download_button( label="下载预测结果", data=csv, file_name="用户转化预测结果.csv", mime="text/csv", use_container_width=True ) # 页脚 st.markdown("---") st.markdown(""" © 2023 精准营销系统 | 基于Sklearn和Streamlit开发 """, unsafe_allow_html=True) 执行上述代码,提示如下报错,前100行数据如下 数据处理错误: could not convert string to float: '否' 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 ... AVG_STMT_AMT_LV is_kdts is_itv_up is_mobile_up if_zzzw_up itv_cnt itv_day serv_in_time PROM_AMT_MONTH is_rh_next 0 201706 1-1E6Z49HF 1-UTSNWVU 杭州 NaN 其它部门-未知部门细分-未知 ... 0 20140126 现行 普通宽带 ... c30-59 0 0 0 0 0 0 41 44.44 0.0 1 201706 3-J591KYI 1-LKFKET 杭州 NaN 其它部门-未知部门细分-未知 ... 0 20160406 现行 普通宽带 ... e89-129 0 0 0 0 0 0 14 100.00 0.0 2 201706 1-F3YGP4D 1-6T16M75 杭州 营业厅 营业厅-营业服务中心-城市 ... 0 20100112 现行 普通宽带 ... c30-59 0 0 0 0 0 28 89 44.44 0.0 3 201706 1-1AITRLCN 1-1AB5KV9U 杭州 NaN 其它部门-未知部门细分-未知 ... 0 20131017 现行 普通宽带 ... c30-59 1 0 0 0 0 10 44 55.56 0.0 4 201706 1-132ZSIVX 1-LPVY5O 杭州 10000号 其它部门-10000客服部-城市 ... 0 20130209 现行 普通宽带 ... d59-89 0 0 0 0 0 0 52 0.00 0.0

大家在看

recommend-type

IFIX 4.5 MB1 驱动

 MB1:有两个版本,6.x 和 7.x 版,通过串行口连接所有支持 Modbus 串行通讯协议的设备。主要有 Modicon PLC 和其它控制仪表如:Honeywell UDC,UMC,Moore 的控制仪表等。支持 Mobus RTU 和 ASCII 两种通讯协议。当使用其与其它兼容设备通讯时,注意数据类型会与 Modicon 的数据类型有所差别。7.x 版本支持电话拨号连接和无线电连接。
recommend-type

TLSF-All.rar_网络编程_C/C++_

TLSF2.46源代码+相关的英文文献在网站http://rtportal.upv.es/rtmalloc/上可以下载到原代码,但注意下载文件的格式。
recommend-type

思源黑体、简体、中文全套TTF格式

思源黑体、简体、中文全套TTF格式
recommend-type

高频感应加热电源斩波器补偿电路的设计

本文在分析基于功率控制的Buck斩波器的小信号模型和反馈控制模式的基础上,探讨了反馈控制的传递函数和环路参数的设计。对于高频感应加热电源广泛应用的Buck斩波调功电路,设计了双极点、双零点补偿电路,补偿后的系统不仅提高了系统响应速度,而且消除了稳态误差,系统性能明显提高。实验结果证明了这种补偿电路的实用性和有效性,对高频感应加热电源的改进和研究具有很好的参考价值。
recommend-type

XposedDetector

Xposed探测器 预制的静态库,用于检测xposed和清除钩子。 该库基于。 一体化 Gradle: implementation ' io.github.vvb2060.ndk:xposeddetector:2.2 ' 该库是 ,因此您需要在项目中启用它(Android Gradle Plugin 4.1+): android { .. . buildFeatures { .. . prefab true } } 用法 ndk构建 您可以在Android.mk使用xposed_detector 。 例如,如果您的应用程序定义了libapp.so并使用xposed_detector ,则您的Android.mk文件应包括以下内容: include $( CLEAR_VARS ) LOCAL_MODULE

最新推荐

recommend-type

Python打造的Slaee管理系统升级版发布

由于提供的文件信息中,文件名《基于python的slaee管理系统 (15).zip》与描述《基于python的slaee管理系统 (15).zip》相同,并且给出的压缩包文件名称列表中只有一个文件《基于python的slaee管理系统 (14).zip》,该信息表明我们正在讨论两个不同版本的Python系统管理软件的压缩包。以下知识点将根据这些信息详细展开: 知识点一:Python编程语言基础 Python是一种高级编程语言,以其简洁的语法和强大的库支持而闻名。它是解释型语言,具有动态类型系统和垃圾回收功能,适用于多种编程范式,包括面向对象、命令式、函数式和过程式编程。Python广泛应用于系统管理、网络服务器、开发脚本、科学计算、数据挖掘和人工智能等领域。 知识点二:系统管理相关知识 系统管理指的是对计算机系统进行配置、监控和维护的过程,包括硬件资源、软件资源和数据资源的管理。在Python中,系统管理通常涉及操作系统级别的任务,如进程管理、文件系统管理、网络配置、系统日志监控等。Python的系统管理库(例如psutil、fabric、paramiko等)提供了丰富的API来简化这些任务。 知识点三:项目版本控制 从文件名《基于python的slaee管理系统 (14).zip》和《基于python的slaee管理系统 (15).zip》可以看出,这是一个项目在不同版本之间的迭代。版本控制是一种记录一个或多个文件随时间变化的方式,它允许用户可以回到特定版本。在软件开发中,版本控制非常重要,它有助于团队协作、代码合并、分支管理和错误跟踪。常见的版本控制系统包括Git、Subversion (SVN)、Mercurial等。 知识点四:打包与部署 提到“压缩包子文件”,这通常意味着文件已经被压缩打包成一个ZIP文件。在软件开发中,打包是为了便于文件传输、存档保存和分发。在Python项目中,打包也是部署过程的一部分。一个Python项目通常需要包含源代码、依赖关系、配置文件和安装脚本等。打包成ZIP文件后,可以通过各种方式部署到服务器上运行,如使用Fabric或Ansible等自动化部署工具。 知识点五:项目命名及版本命名规则 文件命名中的“基于python的slaee管理系统”表明这是一个与Python语言相关的系统管理项目。而数字“15”和“14”则代表着项目的版本号,这表明项目在持续发展,不同的数字代表了项目在不同时期的迭代。版本号的命名规则通常遵循语义化版本控制(SemVer),这种版本控制系统以 MAJOR.MINOR.PATCH 的形式表示版本号。 知识点六:文件压缩与解压缩技术 ZIP是一种常用的文件压缩格式,它通过减少文件大小来提高存储效率和传输速度。ZIP文件通常是无损压缩,意味着文件在压缩和解压缩的过程中不会丢失信息。Python标准库提供了zipfile模块,允许用户在Python程序中创建ZIP文件、读取ZIP文件内容、提取ZIP文件中的文件等操作。用户可以使用各种图形界面工具(如WinRAR、7-Zip)或命令行工具来处理ZIP文件。 总结以上内容,从文件信息中我们可以得知该内容涉及了Python编程语言、系统管理知识、版本控制、打包与部署、命名规则以及文件压缩技术等多方面的知识点。这些知识点对于理解和应用Python进行系统管理软件开发以及软件的版本迭代、打包与部署均具有重要意义。
recommend-type

深入解析PCB走线传输延时:关键因素与实用公式

# 1. PCB走线传输延时基础 在电子工程领域,尤其是在印刷电路板(PCB)设计中,走线传输延时是一个不可忽视的重要概念。传输延时主要指的是电信号通过PCB上的导电路径从源头传送到目的地所耗费的时间。本章将介绍PCB走线传输延时的基础知识点,为后续章节的深入探讨打下坚实的基础。 首先,了解什么是传输延时是至关重要的。简单来说,它反
recommend-type

gpio很弱是什么意思

<think>嗯,用户的问题是关于GPIO信号弱的概念及其影响。用户似乎对硬件或嵌入式系统有一定了解,但需要更深入的解释。从用户提到的"信号弱"来看,ta可能遇到了实际电路设计中的驱动能力问题,或者在学习GPIO原理时遇到了术语困惑。 用户引用了四篇资料,其中提到GPIO的模拟输入输出模式、施密特触发器的作用、上拉下拉电阻的配置,以及信号线串联电阻的作用。这些内容都与GPIO的驱动能力和信号质量相关。特别是引用[4]中提到的"信号线串联小电阻"和"低频电路不考虑反射",暗示用户可能正在处理实际电路中的信号完整性问题。 用户真正想知道的可能是:为什么我的GPIO输出无法正确驱动某个设备?或者
recommend-type

Python打造的Slaee管理系统升级版发布

标题中的“基于python的slaee管理系统”表明这是一个使用Python编程语言开发的系统。Python是一种广泛使用的高级编程语言,以其易读性和简洁的语法而闻名。SLAEE管理系统可能是指一个特定类型的管理软件,但由于没有给出缩写的完整解释,我们可以假设SLAEE可能是某机构或系统名称的缩写。 从标题和描述来看,存在一处笔误:“基于python的slaee管理系统 (19).zip”和“基于python的slaee管理系统 (18).zip”所指的似乎是同一软件系统,只是版本号不同。根据文件名称列表中的两个文件名,可以推断系统至少有两个版本,一个是版本18,一个是版本19。通常情况下,版本号的增加表示软件进行了更新或改进。 接下来,根据这些信息,我们可以阐述一些相关的知识点: 1. Python编程基础:Python是一种解释型、面向对象、高级编程语言。Python支持多种编程范式,包括过程式、面向对象和函数式编程。Python由于其简洁和易于学习的特性,被广泛应用于网络开发、数据分析、人工智能、机器学习和科学计算等领域。 2. 文件压缩与打包:文件压缩是将文件的大小减小以节省存储空间或网络传输时间的技术。常见的文件压缩格式包括ZIP、RAR、7Z等。文件打包通常指的是将多个文件或文件夹压缩成一个单独的文件。这在数据备份、软件分发和档案管理中非常常见。 3. 版本控制:在软件开发中,“版本”通常指软件的特定状态,版本号则用来标识这些状态。版本控制是一种记录文件、目录或集合随着时间变化的方式,以便将来可以检索特定版本。对于软件项目来说,版本控制是至关重要的,它不仅允许开发者追踪和管理代码的变化,而且还能帮助团队协作,解决冲突,并回滚到旧版本。 4. 软件管理系统的开发:一个软件管理系统可能是针对特定业务领域而设计的,它可能包括用户界面、数据库管理、业务逻辑处理、报告生成和其他许多功能。软件管理系统的开发通常涉及需求分析、系统设计、编程、测试和维护等多个阶段。 5. Python在软件开发中的应用:Python因为具有丰富的库和框架,被广泛用于开发各种类型的软件。例如,Django和Flask是用于Web开发的流行Python框架;而对于数据分析和数据科学任务,Pandas、NumPy和Matplotlib等库提供了强大的数据处理和可视化工具;对于机器学习和人工智能,TensorFlow、PyTorch等库使得复杂算法的实现变得更为简单。 6. 系统更新与维护:随着软件的使用和发展,需求可能会变化,新的问题可能会出现,所以软件系统需要定期进行更新和维护。软件更新可能包括修复已知问题、改进现有功能、增加新功能或优化性能。开发者需要评估是否需要为修复安全漏洞或提高系统性能而更新系统,以及更新之后对用户体验的影响。 由于文件名中只提到了“基于python的slaee管理系统”,没有提供该系统具体功能的详细描述,我们无法提供更加具体的技术知识点。如果需要分析系统的工作原理或具体的技术实现细节,还需要更多的信息。
recommend-type

【Keil-ARM编程艺术】:如何编写可维护且高效的代码

# 摘要 本文旨在为读者提供ARM微控制器编程和Keil开发环境的全面指南。第一章概览了ARM微控制
recommend-type

应用层协议概述

<think>首先,用户的问题是:“我想了解应用层协议的基本概念和常见类型 请问应用层协议简介 常见应用层协议概述”。这翻译成:“I want to understand the basic concepts of application layer protocols and common types. Please introduce application layer protocols and overview common application layer protocols.” 根据系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式
recommend-type

Delphi 12 TeeChartVCLFMX控件包下载及功能介绍

标题中提到的"Delphi 12 控件之TeeChartVCLFMX-2024.40.rar"指的是Delphi 12版本中使用的TeeChartVCLFMX图表控件的特定版本(2024.40版本)。Delphi是由Embarcadero Technologies开发的一款流行的集成开发环境(IDE),专门用于使用Object Pascal和C++语言开发软件应用程序。该标题强调了Delphi 12环境下TeeChartVCLFMX控件的使用,这表明Delphi的图形用户界面(GUI)组件库中包含了一个专门用于创建复杂图表和图形的组件。 从描述中仅能得到的关于文件的名称是"TeeChartVCLFMX-2024.40.rar",这意味着文件是一个压缩包,具体包含了一个TeeChartVCLFMX的图表控件,版本号为2024.40。它可能包含了在Delphi 12版本中使用该图表控件所需的所有文件,包括库文件、二进制文件、文档等。 标签"delphi 控件"简单而直接地指出了该文件属于Delphi编程环境中的一个控件类别,表明了目标用户是Delphi开发者,他们通常使用这些控件来丰富他们的应用程序界面或增强应用程序的功能。 文件名称列表提供了关于TeeChartVCLFMX压缩包内包含的具体文件及其用途的详细信息: 1. TeeChartVCLFMX-2024.40.exe:这个文件很可能是一个安装程序或可执行文件,用于安装或运行TeeChartVCLFMX图表控件。 2. Keygen.exe:这个文件名表明它可能是一个密钥生成器(Key Generator),用于生成软件的注册码或激活码,使得控件可以脱离试用限制或进行合法授权。 3. Delphi29Binaries-2024.40-windows.pak:这个文件名暗示它包含了特定于Windows平台的Delphi 29(可能指的是Delphi 12的内部版本号)的二进制文件。pak文件是压缩包的一种格式,可能包含了运行TeeChartVCLFMX图表控件所需的库文件、DLLs、组件文件等。 4. TeeChartVCLFMX-2024.40 - D12.pdf:这是一个PDF格式的文件,很可能是用户手册或帮助文档,提供了对TeeChartVCLFMX图表控件版本2024.40在Delphi 12中的使用说明,安装指南,功能介绍或示例代码等。 综合以上信息,可以推断TeeChartVCLFMX-2024.40压缩包是为Delphi 12的开发人员提供了一个专业的图表解决方案,使得用户能够将图表功能集成到他们用Delphi开发的应用程序中。TeeChartVCLFMX可能包含各种图表类型(如条形图、折线图、饼图等),以及丰富的定制选项,如颜色、样式、图例、数据绑定、交互式功能等。开发者可以利用TeeChartVCLFMX提供的图表控件在应用程序中创建强大的数据可视化功能,这对数据分析、科学计算、商业智能、财务报告等领域特别有用。 开发者需要使用Delphi 12 IDE配合提供的组件安装程序或执行文件安装和配置TeeChartVCLFMX控件,通过阅读PDF文档来了解如何使用控件,并在需要时使用Keygen.exe激活控件。Delphi29Binaries-2024.40-windows.pak文件可能包含了控件运行所需的核心二进制组件。通过合理使用这些资源,开发者可以将TeeChartVCLFMX控件集成到自己的项目中,快速构建功能丰富的图表和数据可视化组件。
recommend-type

【Keil-ARM性能优化全解】:代码监控与优化工具的实用技巧

# 摘要 本文对基于Keil-ARM平台的性能优化进行了系统性探讨,涵盖了性能监控工具的介绍、代码优化策略、系统资源管理以及综合案例分析。首先,介绍了Keil-ARM的性能监控工具及其在性能分析中的重要性,包括工具的配置和性能数据解读方法。其次,文中讨论了代码层面的优化,包括高效循环和条件语句的编写,函数内联与尾调用优化,以及编译器优化选项的应用。然后,深入分析了ARM系统资源分配、
recommend-type

电子邮箱协议

<think>首先,用户的问题是:“我想了解电子邮箱相关的协议 请问电子邮件协议有哪些”。这直接询问电子邮件协议的种类。 从系统级指令中,我需要: - 使用中文回答。 - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 在行内数学表达式使用 $...$,但这里可能没有数学内容。 - 独立公式使用 $$...$$,但可能不需要。 - 生成相关问题。 - 在引用的段落末尾添加引用标识如 [^1]。 参考提供的引用: - 引用[1]: 讨论了SMTP、POP3和IMAP。 - 引用[2]: 提到电子邮件系统采用客户机/服务器模式,涉及接收和发送邮件。 - 引用[3]: 详细描述了P
recommend-type

深入理解Docker在目标检测中的应用

根据给定文件信息,我们首先需要明确这是一个有关Docker的笔记文档。Docker是一种流行的开源容器化平台,用于自动化部署、扩展和管理应用程序。在本篇内容中,我们将围绕Docker技术进行详细讲解,涵盖其核心概念、工作原理以及如何应用于实际项目中,尤其是涉及到对象检测(object detection)这一人工智能领域的子集。 ### Docker核心概念与工作原理 1. **容器(Container)**: Docker容器是一个轻量级、独立的可执行包,包含了运行所需的一切:代码、运行时、系统工具、系统库和设置。与传统的虚拟机相比,容器不包含一个独立的操作系统,而是共享宿主机的操作系统内核,这使得容器更加轻量和高效。 2. **镜像(Image)**: Docker镜像是构建容器的模板。镜像可以基于其他镜像进行修改,并可以保存这些更改,从而创建新的镜像。镜像是一种不可变的文件,是容器运行的静态形式。 3. **Dockerfile**: Dockerfile是一个文本文件,包含了创建Docker镜像的指令。通过Dockerfile,开发者可以定义一个脚本化的构建流程,使得构建镜像的过程变得可复现和自动化。 4. **Docker守护进程(Docker daemon)**: Docker守护进程是一个运行在宿主机上的后台进程,负责构建、运行和分发容器。用户通过与守护进程通信来控制或管理容器。 5. **仓库(Repository)**: Docker仓库用来存放镜像,并提供了镜像的共享和分发服务。仓库分为公开(public)和私有(private)两类。 6. **Docker Compose**: Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。通过Compose,使用YAML文件来配置应用程序的服务。然后,使用一个命令,就可以创建并启动所有服务。 ### Docker在对象检测中的应用 在人工智能领域,尤其是在计算机视觉和深度学习领域,对象检测是一个识别图像中各种对象及其位置的技术。Docker在其中的应用主要体现在以下几个方面: 1. **环境隔离**: 每个容器都运行在隔离的环境中,这意味着不同的机器学习模型和开发环境可以在同一台主机上共存而不产生冲突。这对于依赖不同Python版本、库或框架的机器学习项目特别有用。 2. **版本控制**: 使用Docker镜像可以捕获特定版本的运行环境,包括操作系统的依赖库和框架版本。这样可以确保在不同环境(开发、测试和生产环境)中运行相同版本的应用程序。 3. **便于分发与部署**: 将训练好的模型封装在Docker容器中,可以通过Docker镜像轻松地部署到不同的机器或云平台,而无需担心环境配置问题。 4. **扩展性与灵活性**: Docker容器的轻量级特性使得按需扩展应用变得简单高效。在需要处理大量并发任务的对象检测系统中,可以快速启动多个容器实例,应对负载。 5. **持续集成与持续部署(CI/CD)**: Docker与CI/CD工具的结合可以使得对象检测应用的持续集成和部署流程自动化,从而加快开发周期,确保软件质量和快速迭代。 ### 实际操作与技术细节 在`object-detection-main`这个Docker相关项目中,可以假设我们正在处理一个使用深度学习进行对象检测的机器学习项目。可能的步骤包括: 1. **环境配置**: 创建一个`Dockerfile`,指定基础镜像(例如`python:3.7`),安装所有必要的依赖,如`tensorflow`、`keras`、`opencv-python`等。 2. **代码迁移**: 将训练好的模型文件和相关代码复制到容器的适当位置。 3. **服务定义**: 使用`docker-compose.yml`定义多个容器服务,比如前端服务、API服务、数据库服务等。 4. **构建与运行**: 运行`docker build`命令来构建镜像,然后使用`docker-compose up`来启动服务。 5. **测试与优化**: 进行必要的测试来确保对象检测模型在容器中正常运行,并根据性能指标进行优化调整。 6. **部署与监控**: 将构建好的容器镜像部署到服务器或云平台,同时设置监控以确保系统的稳定性和效率。 7. **持续反馈与改进**: 根据用户反馈和监控结果,持续改进模型的准确性和系统性能。 通过以上的步骤和技术细节,可以对Docker在对象检测项目中的应用有一个整体的把握。Docker技术的应用使得模型的部署和运维变得更加简单和高效,特别是在当今微服务架构和容器化技术盛行的大环境下。