【问题标题】:How to find number of similar addresses for each customer?如何为每个客户查找相似地址的数量?
【发布时间】:2018-12-02 08:37:01
【问题描述】:

我有一个包含两列的数据集:客户idaddresses

id      addresses
1111    asturias 32, benito juarez, CDMX
1111    JOSE MARIA VELASCO, CDMX
1111    asturias 32 DEPT 401, INSURGENTES, CDMX
1111    deportes
1111    asturias 32, benito juarez, MIXCOAC, CDMX
1111    cd. de los deportes
1111    deportes, wisconsin
2222    TORRE REFORMA LATINO, CDMX
2222    PERISUR 2890
2222    WE WORK, CDMX
2222    WEWORK, TORRE REFORMA LATINO, CDMX
2222    PERISUR: 2690, COYOCAN
2222    TORRE REFORMA LATINO

我有兴趣为每个客户找到不同地址的数量。比如客户id1111,有3个不同的地址:

  1. [asturias 32, benito juarez, CDMX, asturias 32 DEPT 401, INSURGENTES, CDMX, asturias 32, benito juarez, MIXCOAC, CDMX]

  2. [JOSE MARIA VELASCO, CDMX]

  3. [deportes, cd. de los deportes, deportes, wisconsin]

我用python写了一段代码,只能显示两个连续行之间的相似性:行i和行i+1(0分表示完全不同,1表示完全相似)。

id      addresses                                  score
1111    asturias 32, benito juarez, CDMX             0
1111    JOSE MARIA VELASCO, CDMX                     0
1111    asturias 32 DEPT 401, INSURGENTES, CDMX      0
1111    deportes                                     0
1111    asturias 32, benito juarez, MIXCOAC, CDMX    0
1111    cd. de los deportes                          0.21
1111    deportes, wisconsin                          0
2222    TORRE REFORMA LATINO, CDMX                   0
2222    PERISUR 2890                                 0
2222    WE WORK, CDMX                                0.69
2222    WEWORK, TORRE REFORMA LATINO, CDMX           0
2222    PERISUR: 2690, COYOCAN                       0
2222    TORRE REFORMA LATINO

如果分数 > 0.20,我认为它们是两个不同的地址。以下是我的代码:

import nltk
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import re
import unicodedata
import unidecode
import string
from sklearn.feature_extraction.text import TfidfVectorizer

data=pd.read_csv('address.csv')
nltk.download('punkt')
stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)

def stem_tokens(tokens):
    return [stemmer.stem(item) for item in tokens]

'''remove punctuation, lowercase, stem'''
def normalize(text):
    return stem_tokens(
        nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))

vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')

def cosine_sim(text1, text2):
    tfidf = vectorizer.fit_transform([text1, text2])
    return ((tfidf * tfidf.T).A)[0, 1]

cnt = np.array(np.arange(0, 5183))
indx = []

for i in cnt:
    print cosine_sim(data['address'][i], data['address'][i + 1])

但上面的代码无法比较特定客户id 的每个可能行。我想要如下输出:

id     unique address
1111    3
2222    3
3333    2

【问题讨论】:

  • 对于地址,首先确定地址的标准元素会更好吗?城镇、邮政编码、街道等,然后将它们分类到单独的列中:例如为街道名称编写正则表达式:re.compile(r'([0-9]+[A-z]?) ([-\.A-z0-9 ]+ (?:street|place|close))'),如果您使用来自有限数量国家的数据等,请使用所有城市名称的列表
  • 您的问题是什么?您是要解析数据还是要询问多维余弦差异?
  • 我想做多维余弦差分。例如,如果客户进行了 4 笔交易,有 4 个地址,比如 add1、add2、add3、add4,我想计算 4C2 = 6 个组合的余弦差:(add1, ad2), (add1, add3), (add1 , add4), (add2, add3), (add2, add4), (add3, add4)。我想为每一位客户做这件事。
  • 你试过我的答案了吗?

标签: python numpy scikit-learn nlp nltk


【解决方案1】:

您可以为此目的在 itertools 中使用组合。请参阅下面的板代码。

请注意,我使用了分号分隔的 CSV 文件

此外,您可以根据需要使用SPACY 中的similarity 函数来查找两个短语之间的相似性。这里我使用了你提供的相同功能。

import nltk
import numpy as np
import pandas as pd
import itertools
import string
from sklearn.feature_extraction.text import TfidfVectorizer


def stem_tokens(tokens):
    return [stemmer.stem(item) for item in tokens]

'''remove punctuation, lowercase, stem'''
def normalize(text):
    return stem_tokens(
        nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))

def cosine_sim(text1, text2):
    tfidf = vectorizer.fit_transform([text1, text2])
    return ((tfidf * tfidf.T).A)[0, 1]

def group_addresses(addresses):
    '''merge the lists if they have an element in common'''
    out = []
    while len(addresses)>0:
        # first, *rest = addresses  # for python 3
        first, rest = addresses[0], addresses[1:]  # for python2
        first = set(first)
        lf = -1
        while len(first)>lf:
            lf = len(first)

            rest2 = []
            for r in rest:
                if len(first.intersection(set(r)))>0:
                    first |= set(r)
                else:
                    rest2.append(r)     
            rest = rest2

        out.append(first)
        addresses = rest
    return out


df=pd.read_csv("address.csv", sep=";")
stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)

vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')

sim_df = pd.DataFrame(columns=['id', 'unique address'])

for customer in set(df['id']):
    customer_addresses = (df.loc[df['id'] == customer]['addresses'])    #Get the addresses of each customer
    all_entries = [[adr] for adr in customer_addresses]    #Make list of lists
    sim_pairs = [list((text1, text2)) for text1, text2 in itertools.combinations(customer_addresses, 2) if cosine_sim(text1, text2) >0.2 ]      # Find all pairs whose similiarty is greater than 0.2
    all_entries.extend(sim_pairs)
    sim_pairs = group_addresses(all_entries)
    print(customer , len(sim_pairs))

输出看起来像

2222 2
1111 3

组成的组是

2222
['WE WORK, CDMX', 'WEWORK, TORRE REFORMA LATINO, CDMX', 'TORRE REFORMA LATINO, CDMX', 'TORRE REFORMA LATINO']
['PERISUR 2890', 'PERISUR: 2690, COYOCAN']

1111
['asturias 32 DEPT 401, INSURGENTES, CDMX', 'asturias 32, benito juarez, MIXCOAC, CDMX', 'asturias 32, benito juarez, CDMX']
['JOSE MARIA VELASCO, CDMX']
['deportes, wisconsin', 'cd. de los deportes', 'deportes']

【讨论】:

  • 嗨,是的,我尝试运行您的代码,但出现错误:文件“”,第 25 行,*rest = 地址 ^ SyntaxError: invalid语法
  • 好的。我假设您使用的是 python 2.7,我的代码在 python 3 中运行。对于 python 2,请将行 first, *rest = addresses 更改为 first, rest = addresses[0], addresses[1:] 。修改了代码。
  • 非常感谢。您的代码工作正常。是的,我使用的是 python 2.7。
猜你喜欢
  • 2015-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-10
相关资源
最近更新 更多