首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用熊猫比较肿瘤大小随时间的变化

用熊猫比较肿瘤大小随时间的变化
EN

Code Review用户
提问于 2017-11-23 13:48:18
回答 2查看 105关注 0票数 6

我在熊猫里有这样的数据

代码语言:javascript
复制
Date         cell         tumor_size (assume it is three dimensional)
25/10/2015    113           [51, 52, 55]
22/10/2015    222           [50, 68, 22]
22/10/2015    883           [45, 23, 67]
20/10/2015    334           [35, 23, 76]

我想做的是比较不同天检测到的肿瘤的大小。让我们以单元222为例;我想将其大小与不同的单元格进行比较,但在早期检测到,例如,我不将其大小与单元格883进行比较,因为它们是在同一天检测到的。或者我不会将它与113号单元进行比较,因为它是后来检测到的。由于我的数据集太大,所以我对行进行了迭代。下面是我的示例代码:

代码语言:javascript
复制
# These will be our lists of pairs and size differences.
pairs = []
diffs = []

# Loop over all unique dates
for date in df.Date.unique():
    # Only take dates earlier then current date.
    compare_df =df.loc[df.Date < pd.Timestamp(date).to_pydatetime()].copy()
    # Loop over each cell for this date and find the minimum
    for row in df.loc[df.Date ==  pd.Timestamp(date).to_pydatetime()].itertuples():
        # If no cells earlier are available use nans.
        if compare_df.empty:
            pairs.append(float('nan'))
            diffs.append(float('nan'))
        # Take lowest absolute value and fill in otherwise
        else:
            compare_df['distance'] = compare_df['tumor_size'].map(lambda x: np.linalg.norm(x - row.tumor_size))
            row_of_interest = compare_df.loc[compare_df.distance == compare_df.distance.min()]
            cell = row.cell
            Date = row.Date
            most_similar_to = row_of_interest.cell.values[0]
            similarity = row_of_interest.distance.values[0]
            with open('final_csv', "a", newline="") as f:
                writer = csv.writer(f, dialect="excel-tab")
                writer.writerow([cell, Date, most_similar_to, similarity])

如何提高代码的速度?我有超过一百万个细胞。

EN

回答 2

Code Review用户

发布于 2019-09-10 01:10:28

你可以做一些快速的改进。首先,始终从for循环中删除尽可能多的东西。在这种情况下,可以删除日期格式和打开的文件行。

日期。在第一个for循环之前,将数据中的日期格式化如下

代码语言:javascript
复制
df['Date'] = pd.to_datetime(df['Date']).dt.date

请注意,我只是将日期时间转换为日期,因为我不认为您需要知道时间。

这样你就可以写了

代码语言:javascript
复制
compare_df = df.loc[df.Date < date].copy()

代码语言:javascript
复制
for row in df.loc[df.Date == date].itertuples():

更快的方法是使用Pandas系列函数,而不是在遍历行时计算/转换类似pd.Timestamp(date).to_pydatetime()之类的内容。我非常喜欢这篇文章,它解释了用Python最快的方法。

保存到文件。在第二个for循环的内部

代码语言:javascript
复制
with open('final_csv.csv',"a", newline="") as f:
    writer = csv.writer(f, dialect="excel-tab")

它每次通过for循环打开和关闭文件。因此,对于一百万条记录,文件被打开和关闭了一百万次。在开始时打开一次,将这两行移动到脚本的开头应该会节省大量的时间。当我对您在示例中给出的四行进行计时时,只需在for循环之外移动两行,时间就被缩短了一半。

此外,“final_csv”没有扩展名,因此该文件被保存为“file”类型。如果您想要保存为csv,那么编写

代码语言:javascript
复制
with open('final_csv.csv', "a", newline="") as f:
    writer = csv.writer(f)

我从上面的第二行中删除了dialect=" Excel -tab“,以便在Excel中打开时,用逗号分隔列并显示在单独的列中。

如果您多次运行您的脚本,那么您已经创建的final_csv文件将被添加到其中,因此您可能会得到重复的条目。为了避免这种情况,请写

代码语言:javascript
复制
with open('final_csv.csv', "w", newline="") as f

将“a”替换为“w”,以便每次运行脚本时都会创建一个新文件。

您创建的对和差异列表从未使用过,因此请删除它们。正如您的代码现在所做的那样,当没有之前的日期时,最后一个most_similar_to和相似性将被第二次写入文件,而不是‘nan’。若要修复此问题,请删除

代码语言:javascript
复制
cell = row.cell
Date = row.Date

并将这两行放在第二条for语句之后。然后替换

代码语言:javascript
复制
pairs.append(float('nan'))
diffs.appen(float('nan'))

在if语句之后

代码语言:javascript
复制
most_similar_to = float('nan')
similarity = float('nan')

算法。您使用map和lambda函数是很聪明的。

另一种综合考虑的方法是使用NumPy和用矩阵运算计算距离。下面是我如何使用NumPy重写您的代码(包括上面提到的所有更改):

代码语言:javascript
复制
import pandas as pd
import numpy as np
import csv

df['Date'] = pd.to_datetime(df['Date']).dt.date 

with open('final_csv.csv',"w", newline="") as f:
    writer = csv.writer(f)
    for row in df.itertuples():
        cell = row.cell
        Date = row.Date
        compare_df = df.loc[df.Date < row.Date].copy()
        if compare_df.empty: 
            most_similar_to = float('nan')
            similarity = float('nan')
        else:
            sizes = np.array(list(compare_df['tumor_size']))
            diff = sizes - np.array(row.tumor_size).transpose()
            square = np.multiply(diff,diff)
            sums = np.sum(square, axis=1)
            distances = np.sqrt(sums).round(decimals=2)
            similarity = min(distances)
            ind = np.where(distances == similarity)[0][0]
            most_similar_to = list(compare_df['cell'])[ind]
        writer.writerow([cell, Date, most_similar_to, similarity])

对于少量的肿瘤大小,我看到使用NumPy的速度增加了2倍。然而,Python似乎只能轻松地处理10,000×10,000平方矩阵。任何更大的东西都会极大地减缓带有8GB内存和核心i7处理器的笔记本电脑的速度。下面是随着矩阵大小的增加,处理时间的图表。

我不清楚是否应该将每个单元格大小与以前每个日期以来的所有单元大小进行无限期的比较。如果您能够限制每行必须与之比较的单元格的数量(例如,可能只返回一定的天数),那么您将节省时间。如果确实需要将所有单元格大小与以前的所有单元格大小进行比较,则可以使用hdf5将矩阵块块并存储在PyTables文件中。

祝好运!

票数 6
EN

Code Review用户

发布于 2019-09-10 10:46:01

您在每个循环中重复大量的工作。一个简单的方法是从列表中提取tumor_sizes。您执行的每一行sizes = np.array(list(compare_df['tumor_size']))。如果在计算开始时执行tumor_sizes = df["tumor_size"].apply(pd.Series),则有一个包含所有肿瘤大小的系列,索引与df相同。

您可以将结果保存在DataFrame中,然后将其写入csv。如果您想做进一步的分析,您可以使用DataFrame。

代码语言:javascript
复制
result = pd.DataFrame(
    {
        "cell": df["cell"],
        "Date": df["Date"],
        "most_similar_to": None,
        "similarity": None,
    },
    index=df.index,
)

要在不同的日子中迭代,可以使用DataFrame.groupby

代码语言:javascript
复制
for date, data in df.groupby(pd.Grouper(key="Date", freq="d")):
    if data.empty:
        continue
    #     print(data)
    previous_samples = df["Date"] < date
    compare_df = df.loc[previous_samples]
    compare_sizes = tumor_sizes.loc[previous_samples]
    if compare_df.empty:
        continue

遍历所有日期。它跳过了那些没有样本的地方,或者没有以前的样本。

然后,您可以遍历每一行。

代码语言:javascript
复制
    for row in data.itertuples():
        distances = pd.Series(
            data=np.linalg.norm(
                compare_sizes - tumor_sizes.loc[[row.Index]].values, axis=1
            ),
            index=compare_sizes.index,
        )
        most_similar_index = distances.idxmin()
        result.loc[row.Index, ["most_similar_to", "similarity"]] = [
            compare_df.loc[most_similar_index, "cell"],
            distances[most_similar_index],
        ]
result

然后,您可以将结果写入csv。

代码语言:javascript
复制
result.to_csv(<filename>, **<format_options>)

更多的矢量化

您可以在数据集上使用scipy.spatial.distance_矩阵,而不是for-循环。就像这样,你不需要计算整个空间矩阵,而只是每天计算,例如,减少内存需求

代码语言:javascript
复制
from scipy.spatial import distance_matrix
result2 = pd.DataFrame(
    {
        "cell": df["cell"],
        "Date": df["Date"],
        "most_similar_to": None,
        "similarity": None,
    },
    index=df.index,
)
for date, data in df.groupby(pd.Grouper(key="Date", freq="d")):
    if data.empty:
        continue
    #     print(data)
    previous_samples = df["Date"] < date
    compare_df = df.loc[previous_samples]
    compare_sizes = tumor_sizes.loc[previous_samples]
    if compare_df.empty:
        continue
    distances = pd.DataFrame(
        distance_matrix(tumor_sizes.loc[data.index], compare_sizes),
        index=data.index,
        columns=compare_sizes.index,
    )
    most_similar_indices = distances.idxmin(axis=1)
    result2.loc[
        most_similar_indices.index, ["most_similar_to", "similarity"]
    ] = pd.DataFrame(
        {
            "most_similar_to": compare_df.loc[
                most_similar_indices, "cell"
            ].values,
            "similarity": distances.min(axis=1).values,
        },
        index=data.index,
    )

另一种选择是更多地留在numpy-land,而在熊猫中则更少:

代码语言:javascript
复制
result3 = pd.DataFrame(
    {
        "cell": df["cell"],
        "Date": df["Date"],
        "most_similar_to": None,
        "similarity": None,
    },
    index=df.index,
)
for date, data in df.groupby(pd.Grouper(key="Date", freq="d")):
    if data.empty:
        continue
    #     print(data)
    previous_samples = df["Date"] < date
    compare_df = df.loc[previous_samples]
    compare_sizes = tumor_sizes.loc[previous_samples]
    if compare_df.empty:
        continue
    distances = distance_matrix(tumor_sizes.loc[data.index], compare_sizes)

    most_similar_indices = distances.argmin(axis=1)
    most_similar_to = compare_df["cell"].values[most_similar_indices]
    similarities = np.choose(most_similar_indices, distances.T)

    result3.loc[data.index, "most_similar_to"] = most_similar_to
    result3.loc[data.index, "similarity"] = similarities
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/181154

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档