I'm using the example given in the json_normalize documentation given here pandas.json_normalize — pandas 1.0.3 documentation, I can't unfortunately paste my actual JSON but this example works. Pasted from the documentation:
data = [{'state': 'Florida',
'shortname': 'FL',
'info': {'governor': 'Rick Scott'},
'counties': [{'name': 'Dade', 'population': 12345},
{'name': 'Broward', 'population': 40000},
{'name': 'Palm Beach', 'population': 60000}]},
{'state': 'Ohio',
'shortname': 'OH',
'info': {'governor': 'John Kasich'},
'counties': [{'name': 'Summit', 'population': 1234},
{'name': 'Cuyahoga', 'population': 1337}]}]
result = json_normalize(data, 'counties', ['state', 'shortname',
['info', 'governor']])
result
name population state shortname info.governor
0 Dade 12345 Florida FL Rick Scott
1 Broward 40000 Florida FL Rick Scott
2 Palm Beach 60000 Florida FL Rick Scott
3 Summit 1234 Ohio OH John Kasich
4 Cuyahoga 1337 Ohio OH John Kasich
What if the JSON was the one below instead where info is an array instead of a dict:
data = [{'state': 'Florida',
'shortname': 'FL',
'info': [{'governor': 'Rick Scott'},
{'governor': 'Rick Scott 2'}],
'counties': [{'name': 'Dade', 'population': 12345},
{'name': 'Broward', 'population': 40000},
{'name': 'Palm Beach', 'population': 60000}]},
{'state': 'Ohio',
'shortname': 'OH',
'info': [{'governor': 'John Kasich'},
{'governor': 'John Kasich 2'}],
'counties': [{'name': 'Summit', 'population': 1234},
{'name': 'Cuyahoga', 'population': 1337}]}]
How would you get the following output using json_normalize:
name population state shortname info.governor
0 Dade 12345 Florida FL Rick Scott
1 Dade 12345 Florida FL Rick Scott 2
2 Broward 40000 Florida FL Rick Scott
3 Broward 40000 Florida FL Rick Scott 2
4 Palm Beach 60000 Florida FL Rick Scott
5 Palm Beach 60000 Florida FL Rick Scott 2
6 Summit 1234 Ohio OH John Kasich
7 Summit 1234 Ohio OH John Kasich 2
8 Cuyahoga 1337 Ohio OH John Kasich
9 Cuyahoga 1337 Ohio OH John Kasich 2
Or if there is another way to do it, please do let me know.
json_normalize is designed for convenience rather than flexibility. It can't handle all forms of JSON out there (and JSON is just too flexible to write a universal parser for).
How about calling json_normalize twice and then merge. This assumes each state only appear once in your JSON:
counties = json_normalize(data, 'counties', ['state', 'shortname'])
governors = json_normalize(data, 'info', ['state'])
result = counties.merge(governors, on='state')
Related
Code sample :
def parse_first_name_female(name):
first = name.str.extract(r"Mrs\.\s+[^(]*\((\w+)", expand=False)
first.loc[first.isna()] = name.str.extract(r"\.\s+(\w+)", expand=False)
return first
female_names = parse_first_name_female(dataset.loc[dataset['Sex']=='female', 'Name'])
This code returns:
print(female_names)
1 Florence
2 Laina
3 Lily
8 Elisabeth
9 Adele
...
880 Imanita
882 Gerda
885 Margaret
887 Margaret
888 Catherine
I need this code just first name to return('Florence')
Are you looking for .iloc?
def parse_first_name_female(name):
first = name.str.extract(r"Mrs\.\s+[^(]*\((\w+)", expand=False)
first.loc[first.isna()] = name.str.extract(r"\.\s+(\w+)", expand=False)
return first
female_names = parse_first_name_female(dataset.loc[dataset['Sex']=='female', 'Name'])
print(female_names.iloc[0])
# Output
Florence
Setup:
df = pd.DataFrame({'Name': ['Braund, Mr. Owen Harris', 'Cumings, Mrs. John Bradley (Florence Briggs Thayer)', 'Heikkinen, Miss. Laina', 'Futrelle, Mrs. Jacques Heath (Lily May Peel)', 'Allen, Mr. William Henry', 'Moran, Mr. James', 'McCarthy, Mr. Timothy J', 'Palsson, Master. Gosta Leonard', 'Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg)', 'Nasser, Mrs. Nicholas (Adele Achem)'],
'Sex': ['male', 'female', 'female', 'female', 'male', 'male', 'male', 'male', 'female', 'female'],
})
I am working with JSON files from Foursquare. And I keep getting this error "string indices must be integers"
This is my dataset, county_merge
county density lat lng
0 Alameda 2532.292000 37.609029 -121.899142
1 Alpine 30.366667 38.589393 -119.834501
2 Amador 218.413333 38.449089 -120.591102
3 Butte 329.012500 39.651927 -121.585844
4 Calaveras 214.626316 38.255818 -120.498149
5 Colusa 393.388889 39.146558 -122.220956
6 Contra Costa 1526.334000 37.903481 -121.917535
7 Del Norte 328.485714 41.726177 -123.913280
8 El Dorado 444.043750 38.757414 -120.527613
9 Fresno 654.509259 36.729529 -119.708861
10 Glenn 477.985714 39.591277 -122.377866
11 Humboldt 392.427083 40.599742 -123.899773
12 Imperial 796.919048 33.030549 -115.359567
13 Inyo 127.561905 36.559533 -117.407471
14 Kern 608.326471 35.314570 -118.753822
15 Kings 883.560000 36.078481 -119.795634
16 Lake 608.338462 39.050541 -122.777656
17 Lassen 179.664706 40.768558 -120.730998
18 Los Angeles 2881.756000 34.053683 -118.242767
19 Madera 486.887500 37.171626 -119.773799
20 Marin 1366.937143 38.040914 -122.619964
21 Mariposa 48.263636 37.570148 -119.903659
22 Mendocino 198.010345 39.317649 -123.412640
23 Merced 1003.309091 37.302957 -120.484327
24 Modoc 100.856250 41.545049 -120.743600
25 Mono 133.145455 37.953393 -118.939876
26 Monterey 946.090323 36.600256 -121.894639
27 Napa 592.020000 38.297137 -122.285529
28 Nevada 338.892857 39.354033 -120.808984
29 Orange 1992.962500 33.750038 -117.870493
30 Placer 492.564000 39.101206 -120.765061
31 Plumas 87.817778 39.943099 -120.805952
32 Riverside 976.692105 33.953355 -117.396162
33 Sacramento 1369.729032 38.581572 -121.494400
34 San Benito 577.637500 36.624809 -121.117738
35 San Bernardino 612.176636 34.108345 -117.289765
36 San Diego 1281.848649 32.717421 -117.162771
37 San Francisco 7279.000000 37.779281 -122.419236
38 San Joaquin 1282.122222 37.937290 -121.277372
39 San Luis Obispo 627.285185 35.282753 -120.659616
40 San Mateo 1594.372973 37.496904 -122.333057
41 Santa Barbara 1133.525806 34.422132 -119.702667
42 Santa Clara 2090.724000 37.354113 -121.955174
43 Santa Cruz 1118.844444 36.974942 -122.028526
44 Shasta 180.137931 40.796512 -121.997919
45 Sierra 115.681818 39.584907 -120.530573
46 Siskiyou 202.170000 41.500722 -122.544354
47 Solano 871.818182 38.221894 -121.916355
48 Sonoma 926.674286 38.511080 -122.847339
49 Stanislaus 1181.864000 37.550087 -121.050143
50 Sutter 552.355556 38.950967 -121.697088
51 Tehama 206.862500 40.125133 -122.201553
52 Trinity 63.056250 40.605326 -123.171268
53 Tulare 681.425806 36.251647 -118.852583
54 Tuolumne 349.471429 38.056944 -119.991935
55 Ventura 1465.400000 34.343649 -119.295171
56 Yolo 958.890909 38.718454 -121.905900
{'meta': {'code': 200, 'requestId': '5cab80f04c1f6715df4e698d'},
'response': {'venues': [{'id': '4b9bf2abf964a520573936e3',
'name': 'Bishop Ranch Veterinary Center & Urgent Care',
'location': {'address': '2000 Bishop Dr',
'lat': 37.77129467449237,
'lng': -121.97112176203284,
'labeledLatLngs': [{'label': 'display',
'lat': 37.77129467449237,
'lng': -121.97112176203284}],
'distance': 19143,
'postalCode': '94583',
'cc': 'US',
'city': 'San Ramon',
'state': 'CA',
'country': 'United States',
'formattedAddress': ['2000 Bishop Dr',
'San Ramon, CA 94583',
'United States']},
'categories': [{'id': '4d954af4a243a5684765b473',
'name': 'Veterinarian',
'pluralName': 'Veterinarians',
'shortName': 'Veterinarians',
'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/building/medical_veterinarian_',
'suffix': '.png'},
'primary': True}],
'venuePage': {'id': '463205329'},
'referralId': 'v-1554743537',
'hasPerk': False},
{'id': '4b9acbfef964a5209dd635e3',
'name': 'San Francisco SPCA Veterinary Hospital',
'location': {'address': '201 Alabama St',
'crossStreet': 'at 16th St.',
'lat': 37.766633450405465,
'lng': -122.41214303998395,
'labeledLatLngs': [{'label': 'display',
'lat': 37.766633450405465,
'lng': -122.41214303998395}],
'distance': 48477,
'postalCode': '94103',
'cc': 'US',
'city': 'San Francisco',
'state': 'CA',
'country': 'United States',
'formattedAddress': ['201 Alabama St (at 16th St.)',
'San Francisco, CA 94103',
'United States']},
'categories': [{'id': '4d954af4a243a5684765b473',
'name': 'Veterinarian',
'pluralName': 'Veterinarians',
'shortName': 'Veterinarians',
'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/building/medical_veterinarian_',
'suffix': '.png'},
'primary': True}],
'referralId': 'v-1554743537',
'hasPerk': False},
{'id': '4b00d8ecf964a5204d4122e3',
'name': 'Pleasanton Veterinary Hospital',
'location': {'address': '3059B Hopyard Rd Ste B',
'lat': 37.67658,
'lng': -121.89778,
'labeledLatLngs': [{'label': 'display',
'lat': 37.67658,
'lng': -121.89778}],
'distance': 7520,
'postalCode': '94588',
'cc': 'US',
'city': 'Pleasanton',
'state': 'CA',
'country': 'United States',
'formattedAddress': ['3059B Hopyard Rd Ste B',
'Pleasanton, CA 94588',
'United States']},
'categories': [{'id': '4d954af4a243a5684765b473',
'name': 'Veterinarian',
'pluralName': 'Veterinarians',
'shortName': 'Veterinarians',
'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/building/medical_veterinarian_',
'suffix': '.png'},
'primary': True}],
'referralId': 'v-1554743537',
'hasPerk': False},
This is JSON file I am working on. And, I am tyring to exctract name, latitude, longitude, and city name.
results = requests.get(url).json()
results
names=county_merge['county']
la=county_merge['lat']
ln=county_merge['lng']
venues_list = []
venues_list.append([(
names,
la,
ln,
v['response']['venues'][0]['name'],
v['response']['venues'][0]['location']['lat'],
v['response']['venues'][0]['location']['lng'],
v['response']['venues'][0]['location']['city']) for v in results])
I am expecting this will give me the several line of list.
[County name, 38.xxxx, -120.xxxx, XXX veterinary clinic, 38.xxxx, -120.xxxx, san diego]
[County name, 38.xxxx, -120.xxxx, XXX veterinary clinic, 38.xxxx, -120.xxxx, san diego]
[County name, 38.xxxx, -120.xxxx, XXX veterinary clinic, 38.xxxx, -120.xxxx, san diego]
[County name, 38.xxxx, -120.xxxx, XXX veterinary clinic, 38.xxxx, -120.xxxx, san diego]
[County name, 38.xxxx, -120.xxxx, XXX veterinary clinic, 38.xxxx, -120.xxxx, san diego]
.
.
.
.
But, It only gives me an error and frustration.
TypeError Traceback (most recent call last)
<ipython-input-44-321b1c667727> in <module>
11 v['response']['venues'][0]['location']['lat'],
12 v['response']['venues'][0]['location']['lng'],
---> 13 v['response']['venues'][0]['location']['city']) for v in results])
<ipython-input-44-321b1c667727> in <listcomp>(.0)
11 v['response']['venues'][0]['location']['lat'],
12 v['response']['venues'][0]['location']['lng'],
---> 13 v['response']['venues'][0]['location']['city']) for v in results])
TypeError: string indices must be integers
Do you have any idea to fix this code?
[v for v in results]
gives you
['meta', 'response']
So you got the keys of results, which are strings. I think you want
venues_list.append([(
names,
la,
ln,
v['name'],
v['location']['lat'],
v['location']['lng'],
v['location']['city']) for v in results['response']['venues'])
I'm completely new to python. And I need a little help to be able to filter my JSON.
json = {
"selection":[
{
"person_id":105894,
"position_id":1,
"label":"Work",
"description":"A description",
"startDate":"2017-07-16T19:20:30+01:00",
"stopDate":"2017-07-16T20:20:30+01:00"
},
{
"person_id":945123,
"position_id":null,
"label":"Illness",
"description":"A description",
"startDate":"2017-07-17T19:20:30+01:00",
"stopDate":"2017-07-17T20:20:30+01:00"
}
]
}
Concretely what I'm trying to do is to transform my JSON (here above) into a Dataframe to be able to use the query methods on it, like:
selected_person_id = 105894
query_person_id = json[(json['person_id'] == selected_person_id)]
or
json.query('person_id <= 105894')
The columns must be:
cols = ['person_id', 'position_id', 'label', 'description', 'startDate', 'stopDate']
How can I do it ?
Use:
df = pd.DataFrame(json['selection'])
print (df)
description label person_id position_id startDate \
0 A description Work 105894 1.0 2017-07-16T19:20:30+01:00
1 A description Illness 945123 NaN 2017-07-17T19:20:30+01:00
stopDate
0 2017-07-16T20:20:30+01:00
1 2017-07-17T20:20:30+01:00
EDIT:
import json
with open('file.json') as data_file:
json = json.load(data_file)
for more complicated examples where a flattening of the structure is neeeded use json_normalize:
>>> data = [{'state': 'Florida',
... 'shortname': 'FL',
... 'info': {
... 'governor': 'Rick Scott'
... },
... 'counties': [{'name': 'Dade', 'population': 12345},
... {'name': 'Broward', 'population': 40000},
... {'name': 'Palm Beach', 'population': 60000}]},
... {'state': 'Ohio',
... 'shortname': 'OH',
... 'info': {
... 'governor': 'John Kasich'
... },
... 'counties': [{'name': 'Summit', 'population': 1234},
... {'name': 'Cuyahoga', 'population': 1337}]}]
>>> from pandas.io.json import json_normalize
>>> result = json_normalize(data, 'counties', ['state', 'shortname',
... ['info', 'governor']])
>>> result
name population info.governor state shortname
0 Dade 12345 Rick Scott Florida FL
1 Broward 40000 Rick Scott Florida FL
2 Palm Beach 60000 Rick Scott Florida FL
3 Summit 1234 John Kasich Ohio OH
4 Cuyahoga 1337 John Kasich Ohio OH
I'm trying to vectorize the get() method from one column containing dictionaries to another column in the same dataframe. For example, I would like the cities in the address column dictionaries to populate the address.city column.
df = pd.DataFrame({'address': [{'city': 'Lake Ashley', 'state': 'MN', 'street': '56833 Baker Branch', 'zip': '15884'},
{'city': 'Reginaldfurt', 'state': 'MO',
'street': '045 Bennett Motorway Suite 404', 'zip': '68916'},
{'city': 'East Stephaniefurt', 'state': 'VI', 'street': '908 Matthew Ports Suite 313', 'zip': '15956-9706'}],
'address.city': [None, None, None],
'address.street': [None, None, None]})
I was trying
df['address.city'].apply(df.address.get('city'))
but that doesn't work. I figured I was close since df.address[0].get('city') does extract the city value for that row. As you can imagine I want to do the same for address.street.
I think what you want is below the following. However, You can parse the address column like this
df.address.apply(pd.Series).add_prefix('address.')
# or
# pd.DataFrame(df.address.tolist()).add_prefix('address.')
address.city address.state address.street address.zip
0 Lake Ashley MN 56833 Baker Branch 15884
1 Reginaldfurt MO 045 Bennett Motorway Suite 404 68916
2 East Stephaniefurt VI 908 Matthew Ports Suite 313 15956-9706
This answers your question:
df['address.city'] = df.address.apply(lambda d: d['city'])
df
address address.city address.street
0 {'city': 'Lake Ashley', 'state': 'MN', 'street... Lake Ashley None
1 {'city': 'Reginaldfurt', 'state': 'MO', 'stree... Reginaldfurt None
2 {'city': 'East Stephaniefurt', 'state': 'VI', ... East Stephaniefurt None
So one of my major pain points is name comprehension and piecing together household names & titles. I have a 80% solution with a pretty massive regex I put together this morning that I probably shouldn't be proud of (but am anyway in a kind of sick way) that matches the following examples correctly:
John Jeffries
John Jeffries, M.D.
John Jeffries, MD
John Jeffries and Jim Smith
John and Jim Jeffries
John Jeffries & Jennifer Wilkes-Smith, DDS, MD
John Jeffries, CPA & Jennifer Wilkes-Smith, DDS, MD
John Jeffries, C.P.A & Jennifer Wilkes-Smith, DDS, MD
John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD
John Jeffries M.D. and Jennifer Holmes CPA
John Jeffries M.D. & Jennifer Holmes CPA
The regex matcher looks like this:
(?P<first_name>\S*\s*)?(?!and\s|&\s)(?P<last_name>[\w-]*\s*)(?P<titles1>,?\s*(?!and\s|&\s)[\w\.]*,*\s*(?!and\s|&\s)[\w\.]*)?(?P<connector>\sand\s|\s*&*\s*)?(?!and\s|&\s)(?P<first_name2>\S*\s*)(?P<last_name2>[\w-]*\s*)?(?P<titles2>,?\s*[\w\.]*,*\s*[\w\.]*)?
(wtf right?)
For convenience: http://www.pyregex.com/
So, for the example:
'John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD'
the regex results in a group dict that looks like:
connector: &
first_name: John
first_name2: Jennifer
last_name: Jeffries
last_name2: Wilkes-Smith
titles1: , C.P.A., MD
titles2: , DDS, MD
I need help with the final step that has been tripping me up, comprehending possible middle names.
Examples include:
'John Jimmy Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD'
'John Jeffries, C.P.A., MD & Jennifer Jenny Wilkes-Smith, DDS, MD'
Is this possible and is there a better way to do this without machine learning? Maybe I can use nameparser (discovered after I went down the regex rabbit hole) instead with some way to determine whether or not there are multiple names? The above matches 99.9% of my cases so I feel like it's worth finishing.
TLDR: I can't figure out if I can use some sort of lookahead or lookbehind to make sure that the possible middle name only matches if
there is a last name after it.
Note: I don't need to parse titles like Mr. Mrs. Ms., etc., but I suppose that can be added in the same manner as middle names.
Solution Notes: First, follow Richard's advice and don't do this. Second, investigate NLTK or use/contribute to nameparser for a more robust solution if necessary.
Regular expressions like this are the work of the Dark One.
Who, looking at your code later, will be able to understand what is going on? Will you even?
How will you test all of the possible edge cases?
Why have you chosen to use a regular expression at all? If the tool you are using is so difficult to work with, it suggests that maybe another tool would be better.
Try this:
import re
examples = [
"John Jeffries",
"John Jeffries, M.D.",
"John Jeffries, MD",
"John Jeffries and Jim Smith",
"John and Jim Jeffries",
"John Jeffries & Jennifer Wilkes-Smith, DDS, MD",
"John Jeffries, CPA & Jennifer Wilkes-Smith, DDS, MD",
"John Jeffries, C.P.A & Jennifer Wilkes-Smith, DDS, MD",
"John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD",
"John Jeffries M.D. and Jennifer Holmes CPA",
"John Jeffries M.D. & Jennifer Holmes CPA",
'John Jimmy Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD',
'John Jeffries, C.P.A., MD & Jennifer Jenny Wilkes-Smith, DDS, MD'
]
def IsTitle(inp):
return re.match('^([A-Z]\.?)+$',inp.strip())
def ParseName(name):
#Titles are separated from each other and from names with ","
#We don't need these, so we remove them
name = name.replace(',',' ')
#Split name and titles on spaces, combining adjacent spaces
name = name.split()
#Build an output object
ret_name = {"first":None, "middle":None, "last":None, "titles":[]}
#First string is always a first name
ret_name['first'] = name[0]
if len(name)>2: #John Johnson Smith/PhD
if IsTitle(name[2]): #John Smith PhD
ret_name['last'] = name[1]
ret_name['titles'] = name[2:]
else: #John Johnson Smith, PhD, MD
ret_name['middle'] = name[1]
ret_name['last'] = name[2]
ret_name['titles'] = name[3:]
elif len(name) == 2: #John Johnson
ret_name['last'] = name[1]
return ret_name
def CombineNames(inp):
if not inp[0]['last']:
inp[0]['last'] = inp[1]['last']
def ParseString(inp):
inp = inp.replace("&","and") #Names are combined with "&" or "and"
inp = re.split("\s+and\s+",inp) #Split names apart
inp = map(ParseName,inp)
CombineNames(inp)
return inp
for e in examples:
print e
print ParseString(e)
Output:
John Jeffries
[{'middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John'}]
John Jeffries, M.D.
[{'middle': None, 'titles': ['M.D.'], 'last': 'Jeffries', 'first': 'John'}]
John Jeffries, MD
[{'middle': None, 'titles': ['MD'], 'last': 'Jeffries', 'first': 'John'}]
John Jeffries and Jim Smith
[{'middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': [], 'last': 'Smith', 'first': 'Jim'}]
John and Jim Jeffries
[{'middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'Jim'}]
John Jeffries & Jennifer Wilkes-Smith, DDS, MD
[{'middle': None, 'titles': [], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer'}]
John Jeffries, CPA & Jennifer Wilkes-Smith, DDS, MD
[{'middle': None, 'titles': ['CPA'], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer'}]
John Jeffries, C.P.A & Jennifer Wilkes-Smith, DDS, MD
[{'middle': None, 'titles': ['C.P.A'], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer'}]
John Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD
[{'middle': None, 'titles': ['C.P.A.', 'MD'], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer'}]
John Jeffries M.D. and Jennifer Holmes CPA
[{'middle': None, 'titles': ['M.D.'], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['CPA'], 'last': 'Holmes', 'first': 'Jennifer'}]
John Jeffries M.D. & Jennifer Holmes CPA
[{'middle': None, 'titles': ['M.D.'], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['CPA'], 'last': 'Holmes', 'first': 'Jennifer'}]
John Jimmy Jeffries, C.P.A., MD & Jennifer Wilkes-Smith, DDS, MD
[{'middle': 'Jimmy', 'titles': ['C.P.A.', 'MD'], 'last': 'Jeffries', 'first': 'John'}, {'middle': None, 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer'}]
John Jeffries, C.P.A., MD & Jennifer Jenny Wilkes-Smith, DDS, MD
[{'middle': None, 'titles': ['C.P.A.', 'MD'], 'last': 'Jeffries', 'first': 'John'}, {'middle': 'Jenny', 'titles': ['DDS', 'MD'], 'last': 'Wilkes-Smith', 'first': 'Jennifer'}]
This took less than fifteen minutes and, at each stage, the logic is clear and the program can be debugged in pieces. While one-liners are cute, clarity and testability should take precedence.