<jupyter_start><jupyter_text>**Analyzing Police Activity with pandas**Data Camp Course---  **Preparando los datos para su análisis**Antes de iniciar nuestro análisis, es importante exáminar y limpiar los datos, para que trabajar con ellos sea un proceso más eficiente.  Agregando librerias<jupyter_code># importando librerias
import pandas as pd
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>Cargando los datos* Examinando los datos, cada registro representa una detención vehícular<jupyter_code># cargando información y explorando los primeros registros
ri = pd.read_csv('https://raw.githubusercontent.com/carmsanchezs/DataCamp-Courses/main/data/police.csv')
ri.head()<jupyter_output><empty_output><jupyter_text>Examinando el dataset* revisar datos nulos* revisar tipos de datos de las columnas y adecuar los tipos de datos si es necesario* elegir el índice<jupyter_code># analizando si existen datos nulos
ri.isnull().sum()
# analizando el total de registros 
ri.shape<jupyter_output><empty_output><jupyter_text>Eliminando columnas**county_name** sólo tiene datos nulos, así que será eliminada del DataFrame<jupyter_code># eliminamos la columna county_name
ri.drop('county_name', axis='columns', inplace=True)
ri.head(3)<jupyter_output><empty_output><jupyter_text>Eliminando filasstop_date y stop_time no contienen valores nulos, pero que pasaría si sí y si estos campos fueran clave para nuestro análisis. Una forma de eliminar registros con valores nulos en esos valores, porque de otra forma no nos servirían, es:<jupyter_code># borrando registros cuyo valor sea nulo en las columnas stop_date y stop_time
ri.dropna(subset=['stop_date','stop_time'], inplace=True)
ri.shape<jupyter_output><empty_output><jupyter_text>Usando el "data type" adecuado<jupyter_code># analiznado los tipos de datos
ri.dtypes<jupyter_output><empty_output><jupyter_text>* **object**: Python strings (u otros objetos de Python)* **bool**: valores True y False * Otros tipos: **int**, **float**, **datetime**, **category**Los tipos de datos importan porque afectan las operaciones que una Serie puede realizar<jupyter_code># examinando los primeros registros de 'is_arrested'
ri.is_arrested.head()
# analizando el tipo de dato de is_arrested
ri.is_arrested.dtype
# convertimos is_arrested como tipo de dato boolean
ri['is_arrested'] = ri.is_arrested.astype('bool')
ri.is_arrested.dtype<jupyter_output><empty_output><jupyter_text>Podemos ver que la fecha está en diferentes columnas: fecha y hora. Así que **combinaremos** las dos coulmnas en una columna y la convertiremos en **datetime**. Si el dato originalmente está en formato **month-day-year** no necesitamos epecificar nada.Está última columna la convertiremos en indice (**DatetimeIndex**)<jupyter_code># combinamos stop_date y stop_time para hacer una sola columna de fecha "stop_datetime"
# además convertimos el tipo de dato en datetime
combined = ri.stop_date.str.cat(ri.stop_time, sep=' ')
ri['stop_datetime'] = pd.to_datetime(combined)
ri.dtypes<jupyter_output><empty_output><jupyter_text>Creando un DatatimeIndex<jupyter_code># establecemos stop_datetime como nuevo índice
ri.set_index('stop_datetime', inplace=True)
ri.index
# examinamos las columnas y observamos que la columna índice ya no aparece como columna
ri.columns<jupyter_output><empty_output><jupyter_text>**Explorando la relación entre género y vigilancia**Examinaremos si el género del conductor tiene un impacto en el comportamiento del policia durante una parada de tráfico?  ¿Se comenten diferentes infracciones por género?<jupyter_code># contabilizando las paradas de tráfico
ri.stop_outcome.value_counts()
# contabilizando con porcentajes
ri.stop_outcome.value_counts(normalize=True)
# sumando los conteos
ri.stop_outcome.value_counts().sum()
# verificando nuevamente los datos nulos
ri.stop_outcome.isnull().sum()
# comparando con el todal de datos, 86539 + 5202 = 91741
ri.shape
# contabilizando ahora los conductores por raza
ri.driver_race.value_counts()
# sumando el total de datos
ri.driver_race.value_counts().sum()
# analizando sólo un conductores blancos
white = ri.query('driver_race == "White"')
white.shape
# contando las paradas de tráfico para conductores blancos
white.stop_outcome.value_counts(normalize=True)
# analizando conductores asiáticos
asian = ri.query('driver_race == "Asian"')
asian.shape
# contando las paradas de tráfico de conductores asiáticos
asian.stop_outcome.value_counts(normalize=True)
# contabilizando las infracciones
ri.violation.value_counts()
# contabilizando con porcentajes
ri.violation.value_counts(normalize=True)
# DataFrame de Mujeres
female = ri[ri.driver_gender == 'F']
female.shape
# DataFrame de Hombres
male = ri[ri.driver_gender == 'M']
male.shape
# total de infracciones de Mujeres
female.violation.value_counts().sum()
# total de infracciones de Hombres
male.violation.value_counts().sum()
# contabilizando infracciones de Mujeres
female.violation.value_counts(normalize=True)
# contabilizando infracciones de Hombres
male.violation.value_counts(normalize=True)<jupyter_output><empty_output><jupyter_text>¿Obtener un ticket de infracción es afectado por el género?<jupyter_code># DataFrame de Mujeres que han sido arrestadas
female_and_arrested = ri.query('driver_gender =="F" and is_arrested == True')
female_and_arrested.shape
male_and_arrested = ri.query('driver_gender =="M" and is_arrested == True')
male_and_arrested.shape
# DataFrame de Mujeres o de personas arrestadas
female_or_arrested = ri[(ri.driver_gender == 'F') | (ri.is_arrested == True)]
female_or_arrested.shape<jupyter_output><empty_output><jupyter_text>Comparando infracciones por exceso de velocidad por género<jupyter_code># DataFrame de mujeres conductoras detenidas por exceso de velocidad
female_and_speeding = ri[(ri.driver_gender == 'F') & (ri.violation == 'Speeding')]
female_and_speeding.shape
# DataFrame de hombres conductores detenidos por exceso de velocidad
male_and_speeding = ri[(ri.driver_gender == 'M') & (ri.violation == 'Speeding')]
male_and_speeding.shape
# contabilizando el porcentaje de "tickets" contra "warnings" en mujeres
female_and_speeding.stop_outcome.value_counts(normalize=True)
# contabilizando el porcentaje de "tickets" contra "warnings" en hombres
male_and_speeding.stop_outcome.value_counts(normalize=True)<jupyter_output><empty_output><jupyter_text>¿Afecta el género a quiénes se les registra el vehículo?<jupyter_code># porcentaje de paradas de tráfico que resultan en arresto
ri.is_arrested.value_counts(normalize=True)
# obtenemos el mismo resultado con mean()
ri.is_arrested.mean()
# esto funciona porque el tipo de dato es boolean
ri.is_arrested.dtype
# sacamos los distritos que existen en nuestro dataset
ri.district.unique()
# para sacar la tasa de arrestros por distrito, podemos sacarlo uno por uno
ri[ri.district == 'Zone K1'].is_arrested.mean()
ri[ri.district == 'Zone K2'].is_arrested.mean()
# o podemos usar la función groupby para obtener la tasa de arrestos de los 6 distritos
ri.groupby('district').is_arrested.mean()
# podemos agrupar diferentes categorias a la vez
ri.groupby(['district', 'driver_gender']).is_arrested.mean()
# aquí tenemos el mismo resultado con otra mejor presentación
ri.groupby(['driver_gender', 'district']).is_arrested.mean()
# tipo de dato de "search_conducted"
ri.search_conducted.dtype
# Tasa de revisión de vehiculos por value_counts
ri.search_conducted.value_counts(normalize=True)
# Tasa de revisión de vehículos a través del calculo del promedio
ri.search_conducted.mean()
# Tasa de búsqueda de vehículo de mujeres conductoras
ri[ri.driver_gender == 'F'].search_conducted.mean()
# Tasa de búsqueda de vehículo de hombres
ri[ri.driver_gender == 'M'].search_conducted.mean()
# Tasa de búsqueda de vehículo por género
ri.groupby('driver_gender').search_conducted.mean()
# agregando un segundo factor de análisis: el tipo de infracción
ri.groupby(['driver_gender', 'violation']).search_conducted.mean()
# o cambiando el orden de agrupación para comparar si tienen la misma tasa
# de revisión de vehículo por cada tipo de infracción
ri.groupby(['violation', 'driver_gender']).search_conducted.mean()<jupyter_output><empty_output><jupyter_text>¿Afecta el género a quién es cacheado durante una búsqueda?<jupyter_code># Cuántas revisiones hubo en una parada de tráfico?
ri.search_conducted.value_counts()
# el campo "search_type" contiene información adicional sobde la búsqueda
# cómo es de esperarse, hay datos sólo cuando sí hay revisión
ri.search_type.value_counts().sum()
# el parámetro dropna=False, nos da también la información de valores nulos
ri.search_type.value_counts(dropna=False)<jupyter_output><empty_output><jupyter_text>El **inventario** es cuando el policía hizo un inventario del vehículoCentrandonos en el inventario, qué pasa si queremos sacar el total de revisiones de vehículo de los cuales hubo inventario, incluyendo aquellas que tienen multiples valores<jupyter_code># contains es un método para buscar si un elemento está contenido en una cadena
# regresa un True si lo contiene, un False en caso contrario
# y con na=False regresa un False si search_type está vacio 
ri['inventory'] = ri.search_type.str.contains('Inventory', na=False)
ri.head(5)
# total de revisiones en las que hubo inventario del vehículo
ri.inventory.sum()
# si queremos el porcentaje de revisiones que tuvieron inventario del vehículo,
# podemos usar simplemente el mean() ?
ri.inventory.mean()
# el problema en el calculo anterior es que se incluyen dónde ni siquiera hubo revisión del vehículo
# haciendo un nuevo DataFrame que contenga las detenciones con revisión de vehículo, tenemos
searched = ri[ri.search_conducted == True]
searched.inventory.mean()<jupyter_output><empty_output><jupyter_text>13.3 % de detenciones con revisión de vehículo incluyeron un inventario del mismo.  **Contando cacheos**<jupyter_code># número de chacheos
ri.search_type.value_counts()
ri['frisk'] = ri.search_type.str.contains('Protective Frisk', na=False)
ri.head(5)
# DataFrame de detenciones con revisión vehicular
searched = ri[ri.search_conducted == True]
# tasa de detenciones vehículares con cacheo
searched.frisk.mean()
# tasa de cacheos por género
searched.groupby('driver_gender').frisk.mean()<jupyter_output><empty_output><jupyter_text>**Análisis exploratorio de datos**¿Es más probable ser arrestado a una determinada hora del día? ¿Están aumentando las detenciones relacionadas con las drogas? Los gráficos pueden ayudar a comprender las tendencias de una manera que examinando sólo los datos no se puede.  ¿La hora del día afecta la tasa de arrestos?Cuando un oficial de policia detiene a un conductor, un pequeño porcentaje de termina en arrestos. Esto es conocido como tasa de arrestos.<jupyter_code># tasa de arrestos
ri.is_arrested.mean()
# recordando que tenemos un DatetimeIndex
ri.index
# podemos sacar del índice el mes, el año, etc.
# no a través del "dt"
ri.index.year<jupyter_output><empty_output><jupyter_text>Line plot: Pone el indice de la serie en el eje x, pone los valores en el eje y<jupyter_code># tasa de arrestos por hora
# que pasa si queremos agrupar por "stop_datetime", que no es parte de las columnas, sino por el índice
hourly_arrest_rate = ri.groupby(ri.index.hour).is_arrested.mean()
hourly_arrest_rate
# graficando la tasa de arrestos por hora
hourly_arrest_rate.plot()

plt.xlabel('Hour')
plt.ylabel('Arrest Rate')
plt.title('Arrest Rate by Time of Day')

plt.show()<jupyter_output><empty_output><jupyter_text>Una pequeña parte de las detenciones en tráfico son encontradas drogas. Evaluemos las detenciones relacionadas con drogas son cada vez más comunes.<jupyter_code># tasa anual de detenciones relacionadas con drogas
annual_drug_rate = ri.drugs_related_stop.resample('A').mean()
# graficando
annual_drug_rate.plot()
plt.show()<jupyter_output><empty_output><jupyter_text>Podemos ver que las detenciones aumentaron considerablemente entre el 2005 y 2015. Podríamos generar una hipótesis de que la tasa de detenciones vehículares incremento también.<jupyter_code># tasa de busquedas anuales 
annual_search_rate = ri.search_conducted.resample('A').mean()
# concatenamos con la tasa anual de detenciones relacionadas con drogas
annual = pd.concat([annual_drug_rate, annual_search_rate], axis='columns')
annual.head(3)
# graficando
annual.plot()
plt.show()
# una mejor forma de graficar
annual.plot(subplots=True)
plt.show()<jupyter_output><empty_output><jupyter_text>¿Qué tipo de infracciones se detectan en cada distrito?<jupyter_code># cross-tabulation 
pd.crosstab(ri.driver_gender, ri.driver_race)
# cross-tabulation 
table = pd.crosstab(ri.driver_race, ri.driver_gender)
table
# verificando que es lo mismo que hacer esto para cada valor
ri[(ri.driver_race == 'Asian') & (ri.driver_gender == 'F')].shape
table = table.loc['Asian':'Hispanic']
table
# graficando
table.plot()
plt.show()
# una gráfica más apropiada, ya que se trata de categorias es
table.plot(kind='bar')
plt.show()
# stacked bar plot
table.plot(kind='bar', stacked=True)
plt.show()<jupyter_output><empty_output><jupyter_text>Contabilizando las infracciones por distrito<jupyter_code># tabla de frecuencias de distritos e infracciones 
all_zones = pd.crosstab(ri.district, ri.violation)
all_zones
k_zones = all_zones.loc['Zone K1': 'Zone K3']
k_zones
# graficamos
k_zones.plot(kind='bar')
plt.show()
# graficamos
k_zones.plot(kind='bar', stacked=True)
plt.show()<jupyter_output><empty_output><jupyter_text>¿Cuánto tiempo podrías ser detenido por una infracción?<jupyter_code># viaulizando la tasa de búequedas en vehículo por tipo de infracción
search_rate = ri.groupby('violation').search_conducted.mean()
search_rate
# visualizando
search_rate.plot(kind='bar')
plt.show()
# mejorando la visualización
search_rate.sort_values().plot(kind='bar')
plt.show()
# mejorando la visualización un poco mas
search_rate.sort_values().plot(kind='barh')
plt.show()<jupyter_output><empty_output><jupyter_text>Convirtiendo la duración de detenciones a númerosEjemplo utilizando el método ***map()***```mapping = {'up':True, 'down':False}apple['is_up'] = apple.change.map(mapping)apple```<jupyter_code># valores únicos de stop_duration
ri.stop_duration.value_counts()
mapping = {'0-15 Min':8, 
           '16-30 Min':23, 
           '30+ Min':45
          }
ri['stop_minutes'] = ri.stop_duration.map(mapping)
ri.head(5)
# únicos valores de "stop_minutes"
ri.stop_minutes.unique()<jupyter_output><empty_output><jupyter_text>graficando la duración de detenciones<jupyter_code>stop_length = ri.groupby('violation_raw').stop_minutes.mean()
stop_length
# ordenando y visualizando
stop_length.sort_values().plot(kind='barh')
plt.show()<jupyter_output><empty_output><jupyter_text>Analyzing the effect of weather on policing.We explore the impact of weather conditions on police behavior during traffic stops. We will practice merging and reshaping datasets, assessing weather a data source is trustworthy, and working with categorical data.  Exploring the weather dataset<jupyter_code>weather = pd.read_csv('https://raw.githubusercontent.com/carmsanchezs/DataCamp-Courses/main/data/weather.csv')
weather.head()<jupyter_output><empty_output><jupyter_text>TAVG, TMIN, TMAX : temperaturaAWND, AWSF2 : Wind speedWT01, WT22 : Bad weather conditions Antes de usar un nuevo set de datos, es una buena práctica explorar los datos y revisar que sean rasonables.<jupyter_code>weather[['AWND', 'WSF2']].head()
weather[['AWND', 'WSF2']].describe
weather[['AWND', 'WSF2']].plot(kind='box')
weather['WDIFF'] = weather.WSF2 - weather.AWND
weather.WDIFF.plot(kind='hist')
plt.show()
weather.WDIFF.plot(kind='hist', bins=20)
plt.show()
weather[['TMIN', 'TAVG', 'TMAX']].describe()
weather[['TMIN', 'TAVG', 'TMAX']].plot(kind='box')
plt.show()
weather['TDIFF'] = weather.TMAX - weather.TMIN
weather.TDIFF.describe()
weather.TDIFF.plot(kind='hist', bins=20)
plt.show()<jupyter_output><empty_output><jupyter_text>Categorizando el climael dataset tiene **1417** registros, con **29** columnas<jupyter_code>weather.shape
weather.columns
# para obtener solo una parte del dataset, usamos .loc
temp = weather.loc[:, 'TAVG':'TMAX']
temp.shape
# o bien podemos usar .iloc
temp = weather.iloc[:, 2:5]
temp.shape
temp.columns
temp.head()
temp.sum()
temp.sum(axis='columns').head()
# valores únicos de stop_duration
ri.stop_duration.unique()
mapping = {'0-15 Min': 'short',
           '16-30 Min': 'medium', 
           '30+ Min': 'long'}
ri['stop_length'] = ri.stop_duration.map(mapping)
ri.stop_length.head()
ri[ri.stop_length > 'short'].shape
ri.groupby('stop_length').is_arrested.mean()
WT = weather.loc[:, 'WT01':'WT22']
weather['bad_conditions'] = WT.sum(axis='columns')
weather['bad_conditions'] = weather.bad_conditions.fillna(0).astype('int')
weather.bad_conditions.hist()
plt.show()
weather.bad_conditions.value_counts().sort_index()
mapping = {0:'good', 1:'bad', 2:'bad', 3:'bad', 4:'bad',
           5:'worse', 6:'worse', 7:'worse', 8:'worse', 
           9:'worse'}
weather['rating'] = weather.bad_conditions.map(mapping)
weather.rating.value_counts()
cats = ['good', 'bad', 'worse']
weather['rating'] = weather.rating.astype('category')
weather.rating.head()<jupyter_output><empty_output><jupyter_text>Merging datasets<jupyter_code>ri.head()
ri.reset_index(inplace=True)
ri.head()
weather_rating = weather[['DATE','rating']]
weather_rating.head()
ri.shape
ri_weather = pd.merge(left=ri, right=weather_rating, left_on='stop_date', right_on='DATE', how='left')
ri_weather.shape
ri_weather.set_index('stop_datetime', inplace=True)
ri_weather.head()<jupyter_output><empty_output><jupyter_text>¿Cómo afecta las detenciones al clima?<jupyter_code># tasa de búsquedas
ri.search_conducted.mean()
# tasa de búsqueda vehícular por género
ri.groupby('driver_gender').search_conducted.mean()
# tasa por tipo de infracción y género
search_rate = ri.groupby(['violation', 'driver_gender']).search_conducted.mean()
search_rate
type(search_rate)
type(search_rate.index)
# podemos de esta manera usar el comando .loc
search_rate.loc['Equipment']
search_rate.loc['Equipment', 'M']
# convirtiendolo en un DataFrame
search_rate.unstack()
type(search_rate.unstack())
# se puede producir lo mismo con un pivot table
# la función por default es mean() pero puede configurarse otra función de agregación
ri.pivot_table(index='violation', columns='driver_gender', values='search_conducted')<jupyter_output><empty_output><jupyter_text>Comparando la tasa de arrestos contra el la clasificación del tiempo<jupyter_code># tasa de arrestos
ri_weather.is_arrested.mean()
# tasa de arrestos por cada clasificación de tiempo
ri_weather.groupby('rating').is_arrested.mean()
# tasa de arrestos por clasificacion de tiempo y tipo de infracción
arrest_rate = ri_weather.groupby(['violation', 'rating']).is_arrested.mean()
arrest_rate
arrest_rate.loc['Moving violation', 'bad']
arrest_rate.loc['Speeding']
arrest_rate.unstack()
ri_weather.pivot_table(index='violation', columns='rating', values='is_arrested')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10592195224571369, 'fraction_numerical': 0.004943725675817819, 'mean_word_length': 6.8965946843853825, 'pattern_counts': {'":': 0, '<': 125, '<?xml version=': 0, '>': 126, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '31', 'dir': 'jupyter-structured-clean-dedup', 'id': '274687', 'license': 'Apache-2.0', 'n_tokens_mistral': 6852, 'n_tokens_neox': 6641, 'n_words': 1744, 'path': 'Analyzing_police_activity_with_pandas/Analyzing_police_activity_with_pandas.ipynb', 'repo_name': 'carmsanchezs/DataCamp-Courses', 'size': '19014'}