#NEWLINE# @ grouping ( interaction = 'callandtext' ) #NEWLINE# def response_rate_text( records ) : #NEWLINE# #INDENT# "\n    The response rate of the user (between 0 and 1).\n\n    Considers text-conversations which began with an incoming text.  Response rate \n    is the fraction of such conversations in which the user sent a text (a response).  \n\n    The following sequence of messages defines four conversations (``I`` for an\n    incoming text, ``O`` for an outgoing text): ::\n\n        I-O-I-O => Started with an incoming text and at least one outgoing text\n        I-I-O-I => Started with an incoming text and at least one outgoing text\n        I-I-I-I => Started with an incoming text but doesn't have outgoing texts\n        O-O-I-O => Not starting with an incoming text\n\n    Here, the ratio would be 2/3 as we have 3 conversations starting with an incoming text and 2 of them have at least one outgoing text.\n\n    See :ref:`Using bandicoot <conversations-label>` for a definition of conversations.\n    " #NEWLINE# records = list ( records ) #NEWLINE# interactions = defaultdict ( list ) #NEWLINE# for r in records : #NEWLINE# #INDENT# interactions [ r . correspondent_id ] . append ( r ) #NEWLINE# #UNINDENT# def _response_rate( grouped ) : #NEWLINE# #INDENT# ( received , responded ) = ( 0 , 0 ) #NEWLINE# conversations = _conversations ( grouped ) #NEWLINE# for conv in conversations : #NEWLINE# #INDENT# if ( len ( conv ) != 0 ) : #NEWLINE# #INDENT# first = conv [ 0 ] #NEWLINE# if ( ( first . direction == 'in' ) and ( first . interaction == 'text' ) ) : #NEWLINE# #INDENT# received += 1 #NEWLINE# if any ( ( ( i . direction == 'out' ) for i in conv ) ) : #NEWLINE# #INDENT# responded += 1 #NEWLINE# #UNINDENT# #UNINDENT# #UNINDENT# #UNINDENT# return ( responded , received ) #NEWLINE# #UNINDENT# all_couples = map ( _response_rate , interactions . values ( ) ) #NEWLINE# ( responded , received ) = map ( sum , zip ( * all_couples ) ) #NEWLINE# return ( ( float ( responded ) / responded ) if ( received != 0 ) else 0 )