Approach #1
We can use a combination of np.unique and np.bincount -
In [48]: unq, ids = np.unique(A, return_inverse=True)
In [49]: dict(zip(unq, np.bincount(ids, b)))
Out[49]:
{'a-1': 11.210000000000001,
'b-1': 4.4400000000000004,
'c-2': 11.199999999999999}
So, np.unique gives us unique integer mapping for each of the strings in A, which are then fed to np.bincount that uses those integers as bins for bin based weighted summations, with weights from b.
Approach #2 (Specific case)
Assuming that the strings in A are always of 3 characters, a faster way would be with converting those strings to numerals and then use those as the input to np.unique. The idea is that np.unique would work faster with numerals than strings.
Hence, the implementation would be -
In [141]: n = A.view(np.uint8).reshape(-1,3).dot(256**np.arange(3))
In [142]: unq, st, ids = np.unique(n, return_index=1, return_inverse=1)
In [143]: dict(zip(A[st], np.bincount(ids, b)))
Out[143]:
{'a-1': 11.210000000000001,
'b-1': 4.4400000000000004,
'c-2': 11.199999999999999}
The magical part is that the viewing after reshaping stays as a view and as such should be pretty efficient :
In [150]: np.shares_memory(A,A.view(np.uint8).reshape(-1,3))
Out[150]: True
Or we could use the axis parameter of np.unique (functionality added in 1.13.0) -
In [160]: A2D = A.view(np.uint8).reshape(-1,3)
In [161]: unq, st, ids = np.unique(A2D, axis=0, return_index=1, return_inverse=1)
In [162]: dict(zip(A[st], np.bincount(ids, b)))
Out[162]:
{'a-1': 11.210000000000001,
'b-1': 4.4400000000000004,
'c-2': 11.199999999999999}