Boa tarde. Meu nome é Nikita Bashun, trabalho como analista de dados no Lucky Group of Companies. Minha história será sobre como nós, com uma equipe de três pessoas, criamos do zero um sistema antifraude para o serviço de reservas de viagens.
Introdução
Aquele que uma vez sabe enganar, enganará muitas vezes.
Lope de Vega
Fraude, em nosso caso, é uma situação em que o motorista engana a empresa. Fraude para conseguir dinheiro.
, , 25, Delphi. . , . , . …
— MVP, .
, :
- . ( «-» ), , . ;
- . — , ( , ). -, 200 .
- , — « »:
- , (, 0% );
- , -.
:
- — , , , ;
- - ( ) — , , ;
- — , , ,
- False Positive, False Negative . :
- , ( , !);
- .
- . ;
- . , — . , , , . - , … .
SQL- DWH, . . , , «»:
WHERE susp = 1 --
AND finished_orders >= 3 --
AND cancelled >= 3 -- ,
AND dist_fin_drivers <= 2 --
AND ok <= 2 -- 2-
, .
. « » . , ? -. , .
python. pandas, postgres, Google ( ). , , Apache Airflow.
API .
:
credentials = ServiceAccountCredentials.from_json_keyfile_dict(
config.crd,
['https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive'])
httpAuth = credentials.authorize(httplib2.Http())
service = googleapiclient.discovery.build('sheets', 'v4', http=httpAuth)
sheet = service.spreadsheets()
:
base_range = f'{city_name}!A{ss_row + 1}:Z{ss_row + reserved_rows}'
sheet.values().append(spreadsheetId=spreadsheetid,
range=base_range,
body={"values": df_pos.values.tolist()},
valueInputOption='RAW').execute()
:
range_from_ss = f'{city_name}!A{ss_row}:S{ss_row + reserved_rows}'
data_from_ss = service.spreadsheets().values().get(
spreadsheetId=spreadsheetid,
range=range_from_ss).execute().get('values', [])
data_from_ss = pd.DataFrame(data_from_ss)
data_from_ss_cols = ['id', '', '']
data_from_ss = data_from_ss.loc[1:, data_from_ss_cols]
PG:
vls_ss = ','.join([f"""({', '.join([f(d[c]) for c in data_from_ss_cols])}
)""" for d in data_from_ss.to_dict('rows')])
sql_update = f"""
WITH updated as (
UPDATE fraud_billing
SET resolution = tb.resolution,
comment=tb.comment,
dt = NOW()
FROM (VALUES {vls_ss}) AS tb(fraud_billing_id, resolution, comment)
WHERE fraud_billing.fraud_billing_id = CAST(tb.fraud_billing_id AS INTEGER)
AND ((fraud_billing.resolution IS NULL AND tb.resolution IS NOT NULL)
OR (fraud_billing.comment IS NULL AND tb.comment IS NOT NULL)
OR (fraud_billing.comment IS NOT NULL AND tb.comment IS NOT NULL
AND fraud_billing.comment <> tb.comment)
OR (fraud_billing.resolution IS NOT NULL AND tb.resolution IS NOT NULL
AND fraud_billing.resolution <> tb.resolution)
)
RETURNING {alias_cols_text_with_id}
)
INSERT INTO fraud_billing_history ({cols_text_with_id})
SELECT {cols_text_with_id}
FROM updated;
"""
crs_postgres.execute(sql_update)
con_postgres.commit()
postgres :
- ;
- .
:
( , ).
, :
— .
: , , .
.
, . , , — . «» .
FP- , .
— , , . .
« ». , , :
- ;
- ;
- ;
- — .
.
, . , , , , . - :
, , — .
Google Spreadsheets. . , :
- , «» ;
- API — ;
- ;
- , .
. , — :
- . - , - , - . , . ;
- . , , :
- - , ;
- - ;
- - , ;
- - .
, , . , «».
— , . — , . !
, ?
- . . , «» , . ;
- . , . , .
( ) 35%. — 25%. , — — , . : , , , . .
:
- 15 ;
- 6800 ;
- 500 .
Mas o sucesso mais importante é a redução gradual dos casos de suspeita em muitas cidades. Afinal, idealmente, não queremos pegar mais , não queremos nada para pegar .
Conclusão
Tentei descrever as principais funcionalidades e princípios do sistema antifraude, bem como as dificuldades que encontramos. Os planos incluem: usar o ML para otimizar a pesquisa, criar um sistema de monitoramento de sanções (agora está em um estágio inicial), melhorar a interface para gerentes, criar relatórios dinâmicos, desenvolver novos padrões e muito mais.
Afinal, estamos apenas no início da jornada.