根据索引和列名称逐个单元填写整个数据框?

时间:2018-02-21 21:04:44

标签: python pandas

我有一个数据框,行索引和列标题应该确定每个单元格的内容。我正在使用以下df的更大版本:

df = pd.DataFrame(index = ['afghijklde', 'afghijklmde', 'ade', 'afghilmde', 'amde'], 
                  columns = ['ae', 'azde', 'afgle', 'arlde', 'afghijklbcmde'])

具体来说,我想应用自定义函数edit_distance()或等效函数(请参阅here获取函数代码),它计算两个字符串之间的差异分数。这两个输入是行名和列名。以下工作但速度极慢:

for seq in df.index:
    for seq2 in df.columns:
        df.loc[seq, seq2] = edit_distance(seq, seq2) 

这会产生我想要的结果:

            ae  azde    afgle   arlde   afghijklbcmde
afghijklde  8    7        5       6          3
afghijklmde 9    8        6       7          2
ade         1    1        3       2          10
afghilmde   7    6        4       5          4
amde        2    1        3       2          9

有什么更好的方法可以使用applymap()?我使用applymap()applydf.iterrows()尝试的所有内容都返回了AttributeError: "'float' object has no attribute 'index'"类型的错误。感谢。

2 个答案:

答案 0 :(得分:3)

Turns out there's an even better way to do this. onepan's dictionary comprehension answer above is good but returns the df index and columns in random order. Using a nested .apply() accomplishes the same thing at about the same speed and doesn't change the row/column order. The key is to not get hung up on naming the df's rows and columns first and filling in the values second. Instead, do it the other way around, initially treating the future index and columns as standalone pandas Series.

series_rows = pd.Series(['afghijklde', 'afghijklmde', 'ade', 'afghilmde', 'amde'])
series_cols = pd.Series(['ae', 'azde', 'afgle', 'arlde', 'afghijklbcmde'])

df = pd.DataFrame(series_rows.apply(lambda x: series_cols.apply(lambda y: edit_distance(x, y))))
df.index = series_rows
df.columns = series_cols

答案 1 :(得分:1)

你可以使用理解,在我的电脑上加速它~4.5倍

first = ['afghijklde', 'afghijklmde', 'ade', 'afghilmde', 'amde']
second = ['ae', 'azde', 'afgle', 'arlde', 'afghijklbcmde']
pd.DataFrame.from_dict({f:{s:edit_distance(f, s) for s in second} for f in first}, orient='index')

# output
#              ae  azde  afgle arlde  afghijklbcmde
# ade          1   2     2     2      2
# afghijklde   1   3     4     4      9
# afghijklmde  1   3     4     4      10
# afghilmde    1   3     4     4      8
# amde         1   3     3     3      3

# this matches to edit_distance('ae', 'afghijklde') == 8, e.g.

注意我将此代码用于edit_distance(链接中的第一个响应):

def edit_distance(s1, s2):
    if len(s1) > len(s2):
        s1, s2 = s2, s1

    distances = range(len(s1) + 1)
    for i2, c2 in enumerate(s2):
        distances_ = [i2+1]
        for i1, c1 in enumerate(s1):
            if c1 == c2:
                distances_.append(distances[i1])
            else:
                distances_.append(1 + min((distances[i1], distances[i1 + 1], distances_[-1])))
        distances = distances_
    return distances[-1]