填坑 | 全国高校绿色计算大赛 模拟赛 第三阶段 第一关 标签推荐

 

挑战任务

在代码世界中遨游是件妙不可言的故事,给开发者推荐新项目、新用户、新世界,有助于增强开源精神,提高开发者乐趣。本关源于国内知名开源社区的一个真实的业务需求,需要基于“绿色计算产业联盟”和国内某知名开源技术平台真实开发者及开发者活动数据进行兴趣标签的推荐。

本关任务:

利用已有的同现标签数据以及给出的20位开发者技能标签数据,推荐兴趣标签给这20位开发者。

其中,同现标签指的是:“共同出现的标签”。比如,某技术问答帖子的标签为<java>,<spring>,<mybatis>,这三个标签在该帖子的标签集合中共同出现,则称其为同现标签

推荐标签指的是:“根据同现标签的数据和开发者自己的技能标签来推荐给该开发者的标签”,比如,某位开发者的技能标签为<java>,<spring>,根据同现标签数据,可以推荐<mybatis>给开发者。

数据说明

为了完成本次挑战,你需要处理两种类型的数据,第一:同现标签数据,第二:开发者技能标签数据,根据提供的两种数据生成推荐标签数据,并且存放在文件中,关于这三种数据的说明如下:

同现标签数据

我们已经将同现标签存放在tag_cooccurrence.csv文件中了,文件路径为:src/step1/tag_cooccurrence.csv,该文件数据结构如下:

idtags
1<objective-c>,<app>,<iphone>,<ios>
2<java>,<jfinal>,<数据库>

字段id为一个自增的整数,增量为1,除了唯一标识这一行的数据外没有其他含义,tags表示同现标签列表,其中包含多个同现标签。

开发者标签数据

我们已经将开发者标签数据存放在user_tag.csv文件中了,文件路径为src/step1/user_tag.csv,文件数据结构如下:

user_idorigin_tagstag_value
1<java>,<spring>,<websocket>,<mybatis>,<redis>[1.0, 0.851, 0.359, 0.336, 0.85]
2<c#>,<csharp>,<shell>,<html>,<css>[1.0, 0.929, 0.743, 0.74, 0.722]

user_id表示开发者的idorigin_tags表示用户的技能标签集合,tag_value表示用户技能标签对应的权重,权重你可以简单的理解为开发者对于这个技能的熟练度或者关注程度,一个技能标签的权重值越高(最大值为1.0),则表示该开发者对该技能越关注。在推荐时就应该优先推荐权重更高标签的相关标签。

该文件只有20条数据,即你只需要对20名开发者做标签推荐。

推荐标签数据

推荐标签数就是,你要根据同现标签和开发者标签数据生成的数据,也是本次挑战要提交的数据,你必须将计算得出的推荐标签数据存放在文件中,并且命名为:user_recommand.csv,需要存放在src/step1/下才可以评测,即完整路径为:src/step1/user_recommand.csv

其中存储格式举例如下:

idrecommand_tags
1<mvn>,<springmvc>,<numpy>,<pandas>,<Django>
2<monogodb>,<mysql>,<gradle>
......
20<tensorflow>
  • 字段id为一个自增的整形变量,增量为1
  • 字段recommad_tags为推荐给用户的标签列表,其中每个标签以英文逗号分隔,注意每个标签用尖括号修饰,即java标签是<java>,而不是java
  • 每一行数据代表给对应用户推荐的标签,如:上述表格中给用户id1的用户推荐的标签为:<mvn>,<springmvc>,<numpy>,<pandas>,<Django>
  • 给用户推荐的标签可以是任意数量。

编程要求

请补充完善右侧代码区域中的getRecommand()函数,构建标签推荐系统,实现根据开发者已有标签,完成对20位开发者的标签推荐,每个开发者的标准测试推荐标签为10个,你的推荐标签可以是任意个,参考步骤如下:

  • 读取src/step1/tag_cooccurrence.csv文件和src/step1/user_tag.csv文件;
  • 根据开发者标签和权重值来处理数据;
  • 生成src/step1/user_recommand.csv推荐标签数据文件;
  • 提交评测。

提示:

你可以通过如下链接下载本关涉及到的数据文件:
https://www.educoder.net/attachments/download/203519/csvFiles.zip

推荐用记事本或者编辑器打开csv文件,用excel打开可能会显示乱码。

你可以持续优化以获得更高的分数。

评测说明

评测方法

本关得分为:

 



其中N=20,表示共有20个开发者测试记录;Auc_i表示对每条记录标签推荐的准确率;Recall_i表示对每条记录标签推荐的召回率。下面为具体的准确率和召回率的描述。

 

参赛选手根据开发者i已有的标签列表{t1,t2,..,t5},推荐n个标签形成集合Ri

R_i=\left \{ r_1,r_2,...,r_n \right \}R​i​​={r​1​​,r​2​​,...,r​n​​}

Ti为测试集中开发者i10个标签组成的集合。

T_i=\left \{ t_6,t_7,...,t_{15} \right \}T​i​​={t​6​​,t​7​​,...,t​15​​}

则开发者i的标签推荐结果准确率Auc_i为:

Auc_i=\frac{\left | R_i\cap T_i \right |}{\left | R_i \right |}Auc​i​​=​∣R​i​​∣​​∣R​i​​∩T​i​​∣​​

召回率为:

Recall=\frac{\left | R_i\cap T_i \right |}{\left | T_i \right |}Recall=​∣T​i​​∣​​∣R​i​​∩T​i​​∣​​

下面对某条开发者记录得分规则进行举例说明。
若参赛选手算出的某一开发者的推荐标签列表R长度为11,其中有3个标签与开发者对应T中的标签一致。则该选手针对该开发者的推荐准确率为3/11,召回率为3/10,因此针对该条记录选手得分为:

\frac{2*3/10*3/11}{3/11+3/10}​3/11+3/10​​2∗3/10∗3/11​​

参考算法

本题在于针对每个开发者标签挖掘其相关的标签进行推荐,可以尝试利用关联规则算法挖掘标签之间的关联规则,将其推荐给目标开发者,在此基础上建议采用FPgrowth挖掘频繁项集加快计算速度,得到关联规则后,针对每个标签关联的其他标签的置信度与该标签对应的权重值计算关联标签的权重值,得到最终的推荐列表。


开始挑战吧,祝你成功!

 

学了很久的人工智能 非常想建个字典算关联规则 but感觉好麻烦就暴力了一下 居然有84

# coding=utf-8
##################第三方库及数据说明#################
# 本关可使用pandas、numpy
# 同现标签数据:src/step1/tag_cooccurrence.csv
# 开发者标签数据:src/step1/user_tag.csv
# 推荐结果文件:src/step1/user_recommand.csv
#####################################################
import pandas as pd
def create_init_set(data_set):
    """
    Function:
        计算项集在数据集的出现次数
    Parameters:
        data_set - 数据集
    Returns:
         ret_dict - 包含出现次数的项集字典
    Modify:
        2018-12-27
    """
    ret_dict = {}
    for trans in data_set:
        ret_dict[frozenset(trans)] = 1
    return ret_dict


def update_tree(items, in_tree, header_table, count):
    """
    Function:
        更新树节点,让FP树生长
    Parameters:
        items - 项集
        in_tree - 当前FP树
        header_table - 头指针表
        count - 次数
    Returns:
         无
    Modify:
        2018-12-27
    """
    # 判断排序后列表的第一个元素是否已经是根节点的子节点
    if items[0] in in_tree.children:
        # 添加出现次数
        # children = {}
        in_tree.children[items[0]].inc(count)
    else:
        # 创建根节点的子节点
        in_tree.children[items[0]] = TreeNode(items[0], count, in_tree)
        # 如果该元素的后继节点不存在,则直接加入。如果有后继节点,则遍历链表尾部将其加入
        if header_table[items[0]][1] == None:
            header_table[items[0]][1] = in_tree.children[items[0]]
        else:
            update_header(header_table[items[0]][1], in_tree.children[items[0]])
    # 列表元素长度大于1,递归调用更新FP树函数
    if len(items) > 1:
        update_tree(items[1::], in_tree.children[items[0]], header_table, count)


def update_header(node_to_test, target_node):
    """
    Function:
        更新头指针表的节点链接
    Parameters:
        node_to_test - 遍历节点
        target_node - 目标节点
    Returns:
         无
    Modify:
        2018-12-27
    """
    # 遍历到链表尾节点
    while (node_to_test.node_link != None):
        node_to_test = node_to_test.node_link
    # 将刚添加的树节点加入链表的尾部
    node_to_test.node_link = target_node


def create_tree(data_set, min_sup=1):
    """
    Function:
        遍历数据集两次构建FP树
    Parameters:
        data_set - 包含项集出现次数的数据集字典
        min_sup - 最小支持度
    Returns:
         ret_tree - FP树
         hearder_table - 头指针表
    Modify:
        2018-12-27
    """
    hearder_table = {}
    # 第一次遍历数据集,获取单个元素的次数
    for trans in data_set:
        for item in trans:
            hearder_table[item] = hearder_table.get(item, 0) + data_set[trans]
    # 去除不满足最小支持度的单个元素
    for k in list(hearder_table.keys()):
        if hearder_table[k] < min_sup:
            del (hearder_table[k])
    freq_item_set = set(hearder_table.keys())
    # 无频繁项就直接返回
    if len(freq_item_set) == 0:
        return None, None
    # 扩展头指针表,添加指向每种类型第一个元素的指针(节点链接)
    for k in hearder_table:
        hearder_table[k] = [hearder_table[k], None]
    # 创建根节点
    ret_tree = TreeNode('Null Set', 1, None)
    # 第二次遍历数据集,构建FP树
    for tran_set, count in data_set.items():
        # tran_set: frozenset({'h', 'p', 'z', 'j', 'r'})
        # count: 1
        local_d = {}
        # 如果单个元素是频繁项,则加入localD列表
        for item in tran_set:
            if item in freq_item_set:
                # hearder_table:{'b': [3, None]}
                local_d[item] = hearder_table[item][0]
        # localD: {'r': 3, 'j': 1, 'z': 5, 'h': 1, 'p': 2}
        if len(local_d) > 0:
            # 元素按出现次数排序
            ordered_items = [v[0] for v in sorted(local_d.items(), key=lambda p: p[1], reverse=True)]
            # 更新FP树,让FP树生长
            update_tree(ordered_items, ret_tree, hearder_table, count)
    return ret_tree, hearder_table


class TreeNode:
    # name:节点元素名称,在构造时初始化为给定值
    # count:出现次数,在构造时初始化为给定值
    # node_link:指向下一个相似节点的指针,默认为None
    # parent:指向父节点的指针,在构造时初始化为给定值
    # children:指向子节点的字典,以子节点的元素名称为键,指向子节点的指针为值,初始化为空字典
    def __init__(self, name_value, num_occur, parent_node):
        self.name = name_value
        self.count = num_occur
        self.node_link = None
        self.parent = parent_node
        self.children = {}

    def inc(self, num_occur):
        # 增加节点出现次数
        self.count += num_occur

    def disp(self, ind=1):
        # 用于将树以文本形式显示
        print('  ' * ind, self.name, ' ', self.count)
        for child in self.children.values():
            child.disp(ind + 1)


def ascend_tree(leaf_node, prefix_path):
    """
    Function:
        根据当前节点向前追溯至根节点,记录前缀路径
    Parameters:
        leaf_node - 给定元素项节点
        prefix_path - 前缀路径列表
    Returns:
         无
    Modify:
        2019-1-6
    """
    # 如果节点有父节点,则将当前节点添加至前缀路径中,之后再递归向上追溯
    if leaf_node.parent != None:
        prefix_path.append(leaf_node.name)
        ascend_tree(leaf_node.parent, prefix_path)


def find_prefix_path(base_pat, tree_node):
    """
    Function:
        发现以给定元素项结尾的所有前缀路径
    Parameters:
        base_pat - 元素项
        tree_node - 需遍历节点
    Returns:
         cond_pats - 所有条件模式基字典
    Modify:
        2019-1-6
    """
    # 所有条件模式基字典
    cond_pats = {}
    # 遍历该节点的整个链表节点,记录每个节点的前缀路径,并将其添加至条件模式基当中
    while tree_node != None:
        prefix_path = []
        ascend_tree(tree_node, prefix_path)
        # 因为该节点也被加进了路径当中,所以需要路径的长度大于1
        if len(prefix_path) > 1:
            # 如果有前缀路径,则将前缀路径加入条件模式基集合中,并且将该元素在该前缀路径中出现的次数也添加进去
            cond_pats[frozenset(prefix_path[1:])] = tree_node.count
        # 当前节点的条件模式基查找完毕后,继续查找头指针链表中下一个节点的条件模式基
        tree_node = tree_node.node_link
    return cond_pats


def mine_tree(in_tree, header_table, min_sup, pre_fix, freq_item_list):
    """
    Function:
        创建条件模式树
    Parameters:
        in_tree - FP树
        header_table - 头指针表
        min_sup - 最小支持度
        pre_fix - 上一次递归的频繁项集合的前缀
        freq_item_list - 频繁项集列表
    Returns:
        无
    Modify:
        2019-1-6
    """
    # 对头指针表中的元素项按照其出现频率从小到大进行排序
    big_l = [v[0] for v in sorted(header_table.items(), key=lambda p:p[1][0])]
    # 遍历单元素频繁集
    for base_pat in big_l:
        new_freq_set = pre_fix.copy()
        new_freq_set.add(base_pat)
        freq_item_list.append(new_freq_set)
        # 获得该元素的所有条件模式基,相当于一个事务集合
        cond_patt_bases = find_prefix_path(base_pat, header_table[base_pat][1])
        # 根据所有条件模式基集合来构建条件模式树
        my_cond_tree, my_head = create_tree(cond_patt_bases, min_sup)
        # 如果条件模式树的头指针表不空(每次建树时对元素支持度有要求
        # 如果小于支持度则该元素不参与建树过程,所以在建树时,条件模式基中的元素会越来越少,最后会是空树),则递归建树
        if my_head != None:
            print('conditional tree for:', new_freq_set)
            my_cond_tree.disp(1)
            mine_tree(my_cond_tree, my_head, min_sup, new_freq_set, freq_item_list)
def func():
    df_train=pd.read_csv('./tag_cooccurrence.csv')
    df_test=pd.read_csv('./user_tag.csv')
    #print(df_train['id'])
    #print(df_train['tags'])
    train_data=[]
    for tags in df_train['tags']:
        train_data.append(tags.split(','))
    init_data = create_init_set(train_data)
    my_fp_tree, my_header_tab = create_tree(init_data, 3)
    ans=[]
    for tags,tag_value in zip(df_test['origin_tags'],df_test['tag_value']):
        tag_value =eval(tag_value)
        tag_dict =dict(zip(tags.split(","),tag_value))
        tag_pair=sorted(tag_dict.items(),key = lambda x:x[1],reverse = True)
        ans_dict={}
        ans_list=[]
        for pair in tag_pair:
            tag,value=pair
            try:
                cond_pats_3 = find_prefix_path(tag, my_header_tab[tag][1])
            except:
                continue
            for k, v in cond_pats_3.items():
                if(v<=3):
                    continue
                for ddict in set(k):
                    if(ddict in tag_dict.keys()):
                        continue
                    if(ddict not in ans_dict):
                        ans_dict[ddict] = v*value
                    else:
                        ans_dict[ddict] += v*value

        ans_pair = sorted(ans_dict.items(), key=lambda x: x[1], reverse=True)
        for pair in ans_pair:
            a,_=pair
            ans_list.append(a)
        print(",".join(ans_list[:10]))
        ans.append(",".join(ans_list[:10]))


    id=range(1,21)
    data={}
    data['id']=id
    data['recommand_tags']=ans
    data_df = pd.DataFrame(data)
    #print(data_df)
    data_df.to_csv('./user_recommand.csv')
#func()

 

# coding=utf-8
##################第三方库及数据说明#################
# 本关可使用pandas、numpy
# 同现标签数据:src/step1/tag_cooccurrence.csv
# 开发者标签数据:src/step1/user_tag.csv
# 推荐结果文件:src/step1/user_recommand.csv
#####################################################
import pandas as pd
def func():
	df_train=pd.read_csv('src/step1/tag_cooccurrence.csv')
	df_test=pd.read_csv('src/step1/user_tag.csv')
    kk={}
    #print(df_train['id'])
    #print(df_train['tags'])
    i=0
    for ii in df_train['id']:
        kk[i]=df_train['tags'][i].replace("'","")
        #print(kk[i])
        kk[i]=kk[i].split(',')
        i+=1
    #print(kk)
    i=0
    pp=[]
    for ii in df_test['user_id']:
        a=df_test['origin_tags'][i].replace("'","").split(',')
        b=df_test['tag_value'][i].strip('[').strip(']').split(',')
        b=[float(x) for x in b]
        pro={}
        for c,d in zip(a,b):
            pro[c]=d
        pro=sorted(pro.items(),key = lambda x:x[1],reverse = True)
        pp.append(pro)
        i+=1
    ans=[]
    for x in pp:
        zz=[]
        for xx in x:
            tag,pro=xx
            #print(tag+' '+str(pro))
            for i in range(0,len(kk)):
                if(tag in kk[i]):
                    zz.extend(kk[i])
                zz = list(set(zz))
                if(len(zz)>10):
                    break
        zz=str(zz[:10]).strip('[').strip(']').replace("'","")
        ans.append(zz)
    id=range(1,21)
    data={}
    data['id']=id
    data['recommand_tags']=ans
    data_df = pd.DataFrame(data)
    #print(data_df)
    data_df.to_csv('src/step1/user_recommand.csv')
    ######################## BEGIN ######################




    ######################## END ########################

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值