viernes, 10 de mayo de 2019

Generar CUIL Random


El siguiente script genera CUIL random válidos en Argentina. El parámetro g refiere al genero, donde m=masculino, f=femenino. Función desarrollada por Juan Cruz Sosa.


pyhton script:
import random
def choose_cuil(g='f'):
 g = 20 if g == 'm' else 27
 cuil = '{}{}'.format(g, random.randint(10000000, 59999999))
 cuil = list(map(int, cuil))
 assert(len(cuil) == 10)
 base = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]
 aux = (-sum(c*b for c, b in zip(cuil, base))) % 11
 if aux == 10:
  return choose_cuil(g)
 return '{}-{}-{}'.format(''.join(map(str, cuil[0:2])), ''.join(map(str, cuil[2:])), aux)

choose_cuil('m')
# Resultado:
# '27-17188949-4'


Referencia
https://es.wikipedia.org/wiki/Clave_%C3%9Anica_de_Identificaci%C3%B3n_Tributaria


martes, 4 de diciembre de 2018

Leer datos de Neo4j

El siguiente script lee datos de Neo4j  de los últimos 10 dias, y lo guarda en un CSV

script:
from py2neo import Graph
import pandas as pd

def neo_conect():
 srv = "http://mi_server.com:7474"
 auth = ("mi_user", "mi_pass")
 return srv, auth

def get_ndata(srv, auth, days=10):
 dt_from = (datetime.date.today() - datetime.timedelta(days=days))
 dt_from = round(datetime.datetime.fromordinal(dt_from.toordinal()).timestamp())
 graph = Graph(srv, auth=auth)
 q = "MATCH (u:user)-[r:RELACION]->(i:item) WHERE r.date >=" +str(dt_from)+ " RETURN u.id as user_id, i.id as item_id, r.date as fecha"
 df = graph.run(q).to_data_frame()
 return df

def main():
 srv, auth = neo_conect()
 df = get_ndata(srv, auth, days=10)
 df.to_csv('neo4j_data.csv', encoding='utf-8', index=False)

if __name__ == '__main__':
 main()

Referencia:
https://medium.com/neo4j/py2neo-v4-2bedc8afef2

sábado, 10 de noviembre de 2018

Identificar genero de un nombre

El siguiente script identifica el genero de un nombre dado, utilizando una lista de nombres+genero. Los valores que devuelve son: {m, f, a}, correspondientes a masculino, femenino o ambiguo.

La lista contiene más de 46mil nombres + genero, unificados de las siguientes fuentes:
  • Lista de nombres de la librería nltk
  • Lista nombres de la librería gender_guesser
  • Lista de nombres argentinos publicados aqui

También se hicieron otras modificaciones para nombres hispanos, como borrar apellidos, agregar diminutivos, nombres cortos, alias, entre otros.

Script:
import pandas as pd 
import operator
import re 

def clean_text(txt):
    txt = re.sub("[^a-záéíóúñüäë]", " ", txt.lower())
    txt = re.sub(' +',' ', txt)
    return txt.strip().split()

def df_to_dict(df, key_column, val_column):
    """convierte dos pandas series en un diccionario"""
    xkey = df[key_column].tolist()
    xval = df[val_column].tolist()
    return dict(zip(xkey,xval))

def get_gender2(names):
    names = clean_text(names)
    names = [x for x in names if gender_list.get(x,'a') != 'a']
    gender ={'m':0, 'f':0, 'a':0}
    for i, name in enumerate(names):
        g = gender_list.get(name,'a')
        gender[g] += 1
        gender[g] += 2 if len(names) > 1 and i == 0 and g != 'a' else 0 
    gender['a'] = 0 if (gender['f']+gender['m']) > 0 else 1
    return max(gender.items(), key=operator.itemgetter(1))[0]

if __name__ == '__main__':
    path = 'https://www.dropbox.com/s/15usc7vi1nlimfj/nombres.csv?dl=1'
    gender_list = pd.read_csv(path)
    gender_list = df_to_dict(gender_list, key_column='nombre', val_column='genero')
    print(get_gender2('lopez jose maria'))
    print(get_gender2('santos contreras'))
    print(get_gender2('maria isabel lopez garcia rodriguez'))
    print(get_gender2('cami lopez'))
    print(get_gender2('majo garcia'))
    print(get_gender2('colon cristobal'))
    print(get_gender2('Cristóbal Colón'))
    print(get_gender2('Cristoforo Colombo'))

end


Binarizar variables continuas

El siguiente script transforma las variables continuas en múltiples variables binarias (0,1) que indican a qué cuartil pertenece la observación para cada variable.

NOTA: En algunas situaciones esto puede significar una perdida de información, para detalle sobre esto, ver referencias 1 y 2.


Conceptualmente sería así:


















Calcular acumulados con Pandas


Si se tiene un dataset con transacciones históricas de usuarios, y se quiere hacer variables que midan acumulados en cada momento (eje: monto consumo en últimos 30dias, promedio consumo el últimos 90 días, etc.), puede usarse el siguiente script que usa funciones de pandas para calcularlo.

Conceptualmente seria así:














Script:
import pandas as pd

def cumulative(data, id2, fdate, not_feature, windows=[30, 90]):
    """calcula acumulados para describir tendencia"""
    data2 = data.copy()
    headers = data2.columns.values.tolist()
    headers = [x for x in headers if x not in not_feature]
    for col in headers:
        print('calculating windows for:',col)
        for d in windows:
            nd = str(d)+'d'
            data2[col+'_sum_'+nd] = data2.set_index('fecha').groupby(id2)[col].rolling(nd).sum().values
    return data2

if __name__ == '__main__':
    path = 'https://www.dropbox.com/s/3g4rz8a0lpvkag4/payment.csv?dl=1'
    data = pd.read_csv(path)
    data['fecha'] = pd.to_datetime(data['fecha'])
    data = data.sort_values(['user_id', 'fecha'], ascending=[True, True])
    not_feature = ['fecha', 'user_id']
    data2 = cumulative(data, 'user_id', 'fecha', not_feature, windows=[30, 90])
    data2.to_csv('data2.csv')



miércoles, 29 de agosto de 2018

Funcion fonetica para faltas ortograficas


La siguiente función reemplaza letras con similitud fonética en idioma español. Esto para reducir la vectorización de texto en escenarios de documentos con faltas ortográficas (como texto de redes sociales).

Para otras técnicas ver:
How to Write a Spelling Corrector, by Peter Norvin
Fuzzy matching (Soundex, NYSIIS, Metaphone)



script python:
import re 

def clean_fonetic(xtext, py='py3'):
  xtext = str(xtext)
  xtext = xtext.lower()
  
  xtext = re.sub('w', 'u', xtext)

  xtext = re.sub('np', 'mp', xtext) 
  xtext = re.sub('nb', 'mb', xtext)

  xtext = re.sub('ce', 'se', xtext)
  xtext = re.sub('ci', 'si', xtext)

  xtext = re.sub('v', 'b', xtext) 

  xtext = re.sub('z', 's', xtext) 

  xtext = re.sub('lla', 'ya', xtext)
  xtext = re.sub('lle', 'ye', xtext)
  xtext = re.sub('lli', 'yi', xtext)
  xtext = re.sub('llo', 'yo', xtext)
  xtext = re.sub('llu', 'yu', xtext)

  xtext = re.sub('ka', 'ca', xtext)
  xtext = re.sub(r'\bk\b', 'que', xtext)  
  xtext = re.sub(r'\bq\b', 'que', xtext)  
  xtext = re.sub('ke', 'que', xtext)
  xtext = re.sub('ki', 'qui', xtext)
  xtext = re.sub('ko', 'co', xtext)
  xtext = re.sub('ku', 'cu', xtext)

  xtext = re.sub('kra', 'cra', xtext)
  xtext = re.sub('kre', 'cre', xtext)
  xtext = re.sub('kri', 'cri', xtext)
  xtext = re.sub('kro', 'cro', xtext)
  xtext = re.sub('kru', 'cru', xtext)

  xtext = re.sub('ga', 'ga', xtext)
  xtext = re.sub('ge', 'je', xtext)
  xtext = re.sub('gi', 'ji', xtext)
  xtext = re.sub('go', 'go', xtext)
  xtext = re.sub('gu', 'gu', xtext)

  xtext = re.sub(r'\bha', 'a', xtext)
  xtext = re.sub(r'\bhe', 'e', xtext)
  xtext = re.sub(r'\bhi', 'i', xtext)
  xtext = re.sub(r'\bho', 'o', xtext)
  xtext = re.sub(r'\bhu', 'u', xtext)

  xtext = re.sub(r'\bexp', 'esp', xtext)
  xtext = re.sub(r'\bexc', 'ese', xtext)
  xtext = re.sub(r'\bexs', 'ese', xtext)
  xtext = re.sub(r'\bext', 'est', xtext)
  # empiezan por ex+[vocal]
  xtext = re.sub(r'\bexa', 'esa', xtext)
  xtext = re.sub(r'\bexe', 'ese', xtext)
  xtext = re.sub(r'\bexi', 'esi', xtext)
  xtext = re.sub(r'\bexo', 'eso', xtext)
  xtext = re.sub(r'\bexu', 'esu', xtext)
  # termian en exion ixion 
  xtext = re.sub(r'exion\b', 'esion', xtext)
  xtext = re.sub(r'ixion\b', 'ision', xtext)
  xtext = re.sub(r'axion\b', 'asion', xtext)

  xtext = re.sub(r'\be\b', 'y', xtext)
  xtext = re.sub(r'\bi\b', 'y', xtext)
  xtext = re.sub(r'\bu\b', 'o', xtext)

  xtext = re.sub(r'\bx\b', 'por', xtext)

  xtext = re.sub(r'([a-z])\1+', r'\1', xtext) 

  return unicode(xtext) if py=='py2' else str(xtext)

txt = 'verguenza, berguenza, berguensa, berrrrrguensaaa'
clean_fonetic(txt)
# berguensa, berguensa, berguensa, berguensa

sábado, 9 de diciembre de 2017

Discretizar variables continuas

Una forma de discretizar una variable continua es calcular los cuantiles y luego crear variable dummy con cada cuantil. Esta transformación es usada para entrenar modelos predictivos. Para detalle sobre cúando conviene discretizar, ver referencia 1 y 2, para otros métodos de discretización, ver referencia 3 y 4.

El siguiente script calcula los deciles de la variable total_day_calls y luego  imprime el script (sql o qlik) para crear variables dummy de cada rango.


Conceptualmente seria así:

















y la transformacion queda así: