如何通过大型CSV文件更有效地进行迭代?

时间:2019-05-15 14:51:59

标签: python pandas csv

我有许多大型CSV文件(每个文件大约有200万行),其中包含几行时间戳,如下所示:

16.01.2019 12:52:22
16.01.2019 12:52:23
16.01.2019 12:52:24

鉴于每一秒都有一个条目(大约一年的时间),所以为什么会有这么多行是可以理解的。我想要更加灵活,这就是为什么要将时间戳分为三行:日期,日期+小时,日期+小时+分钟,日期+小时+秒,以便我可以随意将时间戳分组。这就是我的做法:

dates = []
hours = []
minutes = []
seconds = []
i = 0


#initial values
dates.append(str(get_date(i).date()))
hours.append(str(get_date(i).hour))
minutes.append(str(get_date(i).minute))
seconds.append(str(get_date(i).second))

for i in range(len(df)):
  if i < len(df) - 1 :
    if str(get_date(i).date) < str(get_date(i+1).date): #dates: YYYY-MM-DD
      dates.append(str(get_date(i+1).date()))
    else:
      dates.append(str(get_date(i).date()))

    if str(get_date(i).hour) < str(get_date(i+1).hour): #dates+hours: YYYY-MM-DD HH
      hours.append(str(get_date(i+1).date()) + " " + str(get_date(i+1).hour))
    else:
      hours.append(str(get_date(i).date()) + " " + str(get_date(i).hour))

    if str(get_date(i).minute) < str(get_date(i+1).minute): #dates+hours+minutes: YYYY-MM-DD HH:mm
      minutes.append(str(get_date(i+1).date()) + " " + str(get_date(i+1).hour) + ":" + str(get_date(i+1).minute))
    else: 
      minutes.append(str(get_date(i).date()) + " " + str(get_date(i).hour) + ":" + str(get_date(i).minute))

    if str(get_date(i).second) < str(get_date(i+1).second): #dates+hours+minutes+seconds: YYYY-MM-DD HH:mm+ss
      seconds.append(str(get_date(i+1).date()) + " " + str(get_date(i+1).hour) + ":" + str(get_date(i+1).minute) + ":" + str(get_date(i+1).second))
    else: 
      seconds.append(str(get_date(i).date()) + " " + str(get_date(i).hour) + ":" + str(get_date(i).minute) + ":" + str(get_date(i).second))


df["dates"] = dates
df["hours"] = hours
df["minutes"] = minutes
df["seconds"] = seconds

其中get_date()只是一个返回具有给定索引的时间戳的函数:

def get_date(i):
  return (dt.datetime.strptime(df["timestamp"][i], '%d.%m.%Y %H:%M:%S'))

我基本上遍历所有条目,将每个日期/小时/分钟/秒放入列表,然后将它们分别插入我的数据框,然后放入 其中get_date()只是一个返回带有给定索引的时间戳的函数。

我猜这会让我进入O(n²)吗?这显然是不理想的。

现在,对一个文件(〜60MB,200万行)执行此操作需要半小时。我个人想不出另一种方法来做自己想做的事,所以我只是想看看是否有什么办法可以减少复杂性。

编辑: 根据我的需要调整@Chris的答案:

times = bogie_df["timestamp"]

#got an error when applying map directly into pd.DataFrame, which is why I first converted it into a list
items = ['year', 'month', 'day', 'hour', 'minute', 'second']
df = pd.DataFrame(list(map(operator.attrgetter(*items), pd.to_datetime(times))), columns=items)

#for my desired YYYY-MM-DD format (though attrgetter only return "1" for "January instead of "01"
df["date"] = df['year'].map(str) + "-" + df["month"].map(str) + df["day"].map(str) 

3 个答案:

答案 0 :(得分:3)

operator.attrgetterpd.to_datetime一起使用:

import pandas as pd
import operator

s = pd.Series(["16.01.2019 12:52:22",
"16.01.2019 12:52:23",
"16.01.2019 12:52:24"])

items = ['day', 'hour', 'minute', 'second']
df = pd.DataFrame(list(map(operator.attrgetter(*items), pd.to_datetime(s))), columns=items)

输出:

   day  hour  minute  second
0   16    12      52      22
1   16    12      52      23
2   16    12      52      24

基准:

large_s = pd.Series(pd.date_range('16.01.2019 12:52:22', periods=2000000, freq='1s').astype(str).tolist())
# Make 2M rows of timestamp in str

%%timeit

items = ['day', 'hour', 'minute', 'second']
df = pd.DataFrame(list(map(operator.attrgetter(*items), pd.to_datetime(large_s))), columns=items)
# 6.77 s ± 54.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

大约需要7秒钟。

已更新:

YYYY-mm-dd代替您手动创建pandas格式的字符串。

df = pd.DataFrame(pd.to_datetime(s), columns = ['date'])
items = ['year', 'month', 'day', 'hour', 'minute', 'second']
df[items] = pd.DataFrame(list(map(operator.attrgetter(*items), df['date'])), columns=items)

然后:

df['dates'] = df['date'].astype(str).str[:10]

输出(月份填充为零):

0    2019-01-16
1    2019-01-16
2    2019-01-16

答案 1 :(得分:0)

由于评论的长度受到限制,至少要注意以下几点:

  1. 删除if i < len(df) - 1 :,您不需要它。将您的range替换为range(len(df)-1)
  2. 保存get_date函数的结果:

循环之前: next_time = get_date(0)

在循环内:

current_time = next_time
next_time = get_date(i+1)

这应该为您节省一些函数调用,但是可能pandas有一些更好的方法来执行此类操作。

答案 2 :(得分:0)

您不需要这样做,而应添加Timestamp类型的单列:

df['ts'] = pd.to_datetime(df.timestamp, format='%d.%m.%Y %H:%M:%S')

然后,您可以直接在该列上使用所有时间好东西:

  • df.ts.dt.date:将日期指定为datetime.date
  • df.ts.dt.strftime(format):以带格式的字符串形式表示日期。例如,df.ts.dt.strftime("YYYY-MM-DD HH:mm")是您的"minutes"
  • df.ts.dt.floor(freq='h'):是在小时级别上被截断的时间戳,例如用于分组
  • ...(请阅读pandas Datetime Data以供参考)