FileDataModel在官方说是除了装载csv,tsv文件外,还可以装载压缩文件例如zip或gzip的,mahout in action书中也是那样说的。于是我抱着学习的态度去试验了一把,结果是出人意料的报错了:
Exception in thread "main" java.util.NoSuchElementException at com.google.common.collect.AbstractIterator.peek(AbstractIterator.java:169) at org.apache.mahout.cf.taste.impl.model.file.FileDataModel.<init>(FileDataModel.java:193) at org.apache.mahout.cf.taste.impl.model.file.FileDataModel.<init>(FileDataModel.java:169) at org.apache.mahout.cf.taste.impl.model.file.FileDataModel.<init>(FileDataModel.java:149) at mahout.TestRecommenderEvaluator.main(TestRecommenderEvaluator.java:26)
static InputStream getFileInputStream(File file) throws IOException { InputStream is = new FileInputStream(file); String name = file.getName(); if ("gz".equalsIgnoreCase(Files.getFileExtension(name.toLowerCase()))) { return new GZIPInputStream(is); } else if ("zip".equalsIgnoreCase(Files.getFileExtension(name .toLowerCase()))) { //这是我改过的 ZipFile zf = null; ZipInputStream zis = null; try { zf = new ZipFile(file); zis = new ZipInputStream(is); ZipEntry entry = zis.getNextEntry(); if (entry == null) { throw new IOException("空的zip压缩文件,无法获得偏好值文件"); } return zf.getInputStream(entry); } finally { if (zis != null) { zis.close(); } } //这是源码中的写法 //return new ZipInputStream(is); } else { return is; } }源码中只是返回了ZipInputStream,而不是返回zip文件中某个具体的entry的stream。所以到外层调用String firstLine = iterator.peek();会报错。
我把源码中FileLineIterator的代码全部考了出来,在项目中按照全路径建了一个一样的类,然后修改代码,可以覆盖掉JAR包中的这个类。
现在我们可以测试一下装载zip文件了,写个main方法,添加一下代码:
DataModel dataModel = new FileDataModel(new File( MyFirstRecommender.class.getResource("intro.zip").getPath())); FastIDSet items = dataModel.getItemIDsFromUser(1); System.out.println(items);可以正确的装载并打印出userid=1的所有items。
最后我也思考了一下,为什么这个问题官方都没有修复,可能是因为实际当中根本就不会去装载zip文件,zip文件只是减少了一些磁盘的开销,虽然对于偏好值这种文本文件压缩比例非常高,但是并不能减少装载偏好值文件的内存的开销,因为后面还是要把zip文件在内存中解压然后读取里面的entry,返到由于解压zip消耗了更宝贵的cpu资源。
因为目前还没有实际经验,所以只是一个个人的看法,有误也请留言指正