2013-12-19 18 views
13

我正在使用CSV文件,其中幾個列具有簡單的json對象(幾個鍵值對),而其他列正常。這裏有一個例子:解析使用Pandas從CSV加載的JSON字符串

name,dob,stats 
john smith,1/1/1980,"{""eye_color"": ""brown"", ""height"": 160, ""weight"": 76}" 
dave jones,2/2/1981,"{""eye_color"": ""blue"", ""height"": 170, ""weight"": 85}" 
bob roberts,3/3/1982,"{""eye_color"": ""green"", ""height"": 180, ""weight"": 94}" 

使用df = pandas.read_csv('file.csv')後,有什麼解析最有效的方式和stats列拆分爲其他列?

大約一個小時後,我能想出的唯一的事情就是:

import json 
stdf = df['stats'].apply(json.loads) 
stlst = list(stdf) 
stjson = json.dumps(stlst) 
df.join(pandas.read_json(stjson)) 

這似乎是我做錯了,這是相當多的工作的考慮,我需要做的這經常在三列。

*編輯:期望的輸出是下面的數據幀對象。添加下面的代碼在我的(糟糕)的方式到那裏線:

df = df.join(pandas.read_json(stjson)) 
del(df['stats']) 
In [14]: df 

Out[14]: 
      name  dob eye_color height weight 
0 john smith 1/1/1980  brown  160  76 
1 dave jones 2/2/1981  blue  170  85 
2 bob roberts 3/3/1982  green  180  94 
+0

請問您可以在問題中顯示預期結果嗎? –

回答

15

有一個稍微更簡單的方法,但最終你必須有打電話json.loads是pandas中的轉換器的概念.read_csv

converters : dict. optional 

Dict of functions for converting values in certain columns. Keys can either be integers or column labels 

所以首先定義您的自定義分析器。在這種情況下,下面應該工作:

def CustomParser(data): 
    import json 
    j1 = json.loads(data) 
    return j1 

你的情況,你會碰到這樣的:

df = pandas.read_csv(f1, converters={'stats':CustomParser},header=0) 

我們告訴read_csv以標準方式來讀取數據,但對統計信息列使用我們的自定義分析器。這將使統計列成爲字典

從這裏,我們可以使用一些小技巧將這些列與相應的列名直接附加到一個步驟中。這將僅適用於常規數據的工作(JSON對象需要有3個值或至少需要在我們的CustomParser進行處理缺失值)

df[df['stats'][0].keys()] = df['stats'].apply(pandas.Series) 

在左側,我們得到來自新列名統計信息列元素的鍵。統計信息列中的每個元素都是一個字典。所以我們正在做批量分配。在右側,我們使用apply來分解'stats'列,以便從每個鍵/值對中創建一個數據框。

+1

謝謝,這真是太好了,我希望我將來需要處理更多的突變數據,這將會幫幫我。 –

+1

這個答案中的最後一行不能保證字典元素與正確的列名匹配。 '.apply(pandas.Series)'將每一行轉換爲一個Series並自動對索引進行排序,在這種情況下,索引是字典鍵的列表。所以爲了保持一致性,您必須確保LHS上的按鍵列表已排序。 – abeboparebop

+0

我會'輸入json',然後使用:'pandas.read_csv(f1,converters = {'stats':json.loads})''。你不需要定義一個新的函數,你絕對不需要在它裏面導入。 – gberger

7

我認爲應用json.load是一個好主意,但是從那裏,你可以簡單地直接將其轉換爲數據幀列,而不是寫/加載它再次:

stdf = df['stats'].apply(json.loads) 
pd.DataFrame(stdf.tolist()) # or stdf.apply(pd.Series) 

或可選擇地在一個步驟:

df.join(df['stats'].apply(json.loads).apply(pd.Series)) 
+0

ty,這對我當前的任務來說是完全足夠的,但是我將另一個標記爲答案,因爲它更廣泛適用 –

2

Paul的回答非常好,但總的來說不正確,因爲不能保證最後一行的左側和右側的列順序相同。 (事實上​​,它似乎並不適用於問題中的測試數據,而是錯誤地切換高度和重量列。)

我們可以通過確保對LHS上的dict鍵列表進行排序來解決此問題。這是有效的,因爲RHS上的apply會自動按索引排序,在這種情況下,索引是列名稱列表。

def CustomParser(data): 
    import json 
    j1 = json.loads(data) 
    return j1 

df = pandas.read_csv(f1, converters={'stats':CustomParser},header=0) 
df[sorted(df['stats'][0].keys())] = df['stats'].apply(pandas.Series)